2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2006-12-12 20:18:30 +03:00
/*
* Apple Motion Sensor driver ( PMU variant )
*
* Copyright ( C ) 2006 Michael Hanselmann ( linux - kernel @ hansmi . ch )
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/adb.h>
# include <linux/pmu.h>
# include "ams.h"
/* Attitude */
# define AMS_X 0x00
# define AMS_Y 0x01
# define AMS_Z 0x02
/* Not exactly known, maybe chip vendor */
# define AMS_VENDOR 0x03
/* Freefall registers */
# define AMS_FF_CLEAR 0x04
# define AMS_FF_ENABLE 0x05
# define AMS_FF_LOW_LIMIT 0x06
# define AMS_FF_DEBOUNCE 0x07
/* Shock registers */
# define AMS_SHOCK_CLEAR 0x08
# define AMS_SHOCK_ENABLE 0x09
# define AMS_SHOCK_HIGH_LIMIT 0x0a
# define AMS_SHOCK_DEBOUNCE 0x0b
/* Global interrupt and power control register */
# define AMS_CONTROL 0x0c
static u8 ams_pmu_cmd ;
static void ams_pmu_req_complete ( struct adb_request * req )
{
complete ( ( struct completion * ) req - > arg ) ;
}
/* Only call this function from task context */
static void ams_pmu_set_register ( u8 reg , u8 value )
{
static struct adb_request req ;
DECLARE_COMPLETION ( req_complete ) ;
req . arg = & req_complete ;
if ( pmu_request ( & req , ams_pmu_req_complete , 4 , ams_pmu_cmd , 0x00 , reg , value ) )
return ;
wait_for_completion ( & req_complete ) ;
}
/* Only call this function from task context */
static u8 ams_pmu_get_register ( u8 reg )
{
static struct adb_request req ;
DECLARE_COMPLETION ( req_complete ) ;
req . arg = & req_complete ;
if ( pmu_request ( & req , ams_pmu_req_complete , 3 , ams_pmu_cmd , 0x01 , reg ) )
return 0 ;
wait_for_completion ( & req_complete ) ;
if ( req . reply_len > 0 )
return req . reply [ 0 ] ;
else
return 0 ;
}
/* Enables or disables the specified interrupts */
static void ams_pmu_set_irq ( enum ams_irq reg , char enable )
{
if ( reg & AMS_IRQ_FREEFALL ) {
u8 val = ams_pmu_get_register ( AMS_FF_ENABLE ) ;
if ( enable )
val | = 0x80 ;
else
val & = ~ 0x80 ;
ams_pmu_set_register ( AMS_FF_ENABLE , val ) ;
}
if ( reg & AMS_IRQ_SHOCK ) {
u8 val = ams_pmu_get_register ( AMS_SHOCK_ENABLE ) ;
if ( enable )
val | = 0x80 ;
else
val & = ~ 0x80 ;
ams_pmu_set_register ( AMS_SHOCK_ENABLE , val ) ;
}
if ( reg & AMS_IRQ_GLOBAL ) {
u8 val = ams_pmu_get_register ( AMS_CONTROL ) ;
if ( enable )
val | = 0x80 ;
else
val & = ~ 0x80 ;
ams_pmu_set_register ( AMS_CONTROL , val ) ;
}
}
static void ams_pmu_clear_irq ( enum ams_irq reg )
{
if ( reg & AMS_IRQ_FREEFALL )
ams_pmu_set_register ( AMS_FF_CLEAR , 0x00 ) ;
if ( reg & AMS_IRQ_SHOCK )
ams_pmu_set_register ( AMS_SHOCK_CLEAR , 0x00 ) ;
}
static u8 ams_pmu_get_vendor ( void )
{
return ams_pmu_get_register ( AMS_VENDOR ) ;
}
static void ams_pmu_get_xyz ( s8 * x , s8 * y , s8 * z )
{
* x = ams_pmu_get_register ( AMS_X ) ;
* y = ams_pmu_get_register ( AMS_Y ) ;
* z = ams_pmu_get_register ( AMS_Z ) ;
}
static void ams_pmu_exit ( void )
{
2010-01-31 07:03:23 +03:00
ams_sensor_detach ( ) ;
2006-12-12 20:18:30 +03:00
/* Disable interrupts */
ams_pmu_set_irq ( AMS_IRQ_ALL , 0 ) ;
/* Clear interrupts */
ams_pmu_clear_irq ( AMS_IRQ_ALL ) ;
ams_info . has_device = 0 ;
printk ( KERN_INFO " ams: Unloading \n " ) ;
}
int __init ams_pmu_init ( struct device_node * np )
{
2007-04-03 04:52:17 +04:00
const u32 * prop ;
2006-12-12 20:18:30 +03:00
int result ;
/* Set implementation stuff */
ams_info . of_node = np ;
ams_info . exit = ams_pmu_exit ;
ams_info . get_vendor = ams_pmu_get_vendor ;
ams_info . get_xyz = ams_pmu_get_xyz ;
ams_info . clear_irq = ams_pmu_clear_irq ;
ams_info . bustype = BUS_HOST ;
/* Get PMU command, should be 0x4e, but we can never know */
2007-05-01 07:54:02 +04:00
prop = of_get_property ( ams_info . of_node , " reg " , NULL ) ;
2008-10-17 19:51:12 +04:00
if ( ! prop )
return - ENODEV ;
2006-12-12 20:18:30 +03:00
ams_pmu_cmd = ( ( * prop ) > > 8 ) & 0xff ;
/* Disable interrupts */
ams_pmu_set_irq ( AMS_IRQ_ALL , 0 ) ;
/* Clear interrupts */
ams_pmu_clear_irq ( AMS_IRQ_ALL ) ;
result = ams_sensor_attach ( ) ;
if ( result < 0 )
2008-10-17 19:51:12 +04:00
return result ;
2006-12-12 20:18:30 +03:00
/* Set default values */
ams_pmu_set_register ( AMS_FF_LOW_LIMIT , 0x15 ) ;
ams_pmu_set_register ( AMS_FF_ENABLE , 0x08 ) ;
ams_pmu_set_register ( AMS_FF_DEBOUNCE , 0x14 ) ;
ams_pmu_set_register ( AMS_SHOCK_HIGH_LIMIT , 0x60 ) ;
ams_pmu_set_register ( AMS_SHOCK_ENABLE , 0x0f ) ;
ams_pmu_set_register ( AMS_SHOCK_DEBOUNCE , 0x14 ) ;
ams_pmu_set_register ( AMS_CONTROL , 0x4f ) ;
/* Clear interrupts */
ams_pmu_clear_irq ( AMS_IRQ_ALL ) ;
ams_info . has_device = 1 ;
/* Enable interrupts */
ams_pmu_set_irq ( AMS_IRQ_ALL , 1 ) ;
printk ( KERN_INFO " ams: Found PMU based motion sensor \n " ) ;
2008-10-17 19:51:12 +04:00
return 0 ;
2006-12-12 20:18:30 +03:00
}