2018-06-01 11:22:58 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* SiRFstar GNSS receiver driver
*
* Copyright ( C ) 2018 Johan Hovold < johan @ kernel . org >
*/
# include <linux/errno.h>
# include <linux/gnss.h>
# include <linux/gpio/consumer.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/pm.h>
# include <linux/pm_runtime.h>
# include <linux/regulator/consumer.h>
2018-11-14 11:37:54 +03:00
# include <linux/sched.h>
2018-06-01 11:22:58 +03:00
# include <linux/serdev.h>
# include <linux/slab.h>
# include <linux/wait.h>
# define SIRF_BOOT_DELAY 500
# define SIRF_ON_OFF_PULSE_TIME 100
# define SIRF_ACTIVATE_TIMEOUT 200
# define SIRF_HIBERNATE_TIMEOUT 200
2019-01-24 09:34:36 +03:00
/*
* If no data arrives for this time , we assume that the chip is off .
* REVISIT : The report cycle is configurable and can be several minutes long ,
* so this will only work reliably if the report cycle is set to a reasonable
* low value . Also power saving settings ( like send data only on movement )
* might things work even worse .
* Workaround might be to parse shutdown or bootup messages .
*/
# define SIRF_REPORT_CYCLE 2000
2018-06-01 11:22:58 +03:00
struct sirf_data {
struct gnss_device * gdev ;
struct serdev_device * serdev ;
speed_t speed ;
struct regulator * vcc ;
2019-01-24 09:34:38 +03:00
struct regulator * lna ;
2018-06-01 11:22:58 +03:00
struct gpio_desc * on_off ;
struct gpio_desc * wakeup ;
int irq ;
bool active ;
2019-01-24 09:34:35 +03:00
struct mutex gdev_mutex ;
bool open ;
2019-01-24 09:34:36 +03:00
struct mutex serdev_mutex ;
int serdev_count ;
2018-06-01 11:22:58 +03:00
wait_queue_head_t power_wait ;
} ;
2019-01-24 09:34:36 +03:00
static int sirf_serdev_open ( struct sirf_data * data )
{
int ret = 0 ;
mutex_lock ( & data - > serdev_mutex ) ;
if ( + + data - > serdev_count = = 1 ) {
ret = serdev_device_open ( data - > serdev ) ;
if ( ret ) {
data - > serdev_count - - ;
goto out_unlock ;
}
serdev_device_set_baudrate ( data - > serdev , data - > speed ) ;
serdev_device_set_flow_control ( data - > serdev , false ) ;
}
out_unlock :
mutex_unlock ( & data - > serdev_mutex ) ;
return ret ;
}
static void sirf_serdev_close ( struct sirf_data * data )
{
mutex_lock ( & data - > serdev_mutex ) ;
if ( - - data - > serdev_count = = 0 )
serdev_device_close ( data - > serdev ) ;
mutex_unlock ( & data - > serdev_mutex ) ;
}
2018-06-01 11:22:58 +03:00
static int sirf_open ( struct gnss_device * gdev )
{
struct sirf_data * data = gnss_get_drvdata ( gdev ) ;
struct serdev_device * serdev = data - > serdev ;
int ret ;
2019-01-24 09:34:35 +03:00
mutex_lock ( & data - > gdev_mutex ) ;
data - > open = true ;
mutex_unlock ( & data - > gdev_mutex ) ;
2019-01-24 09:34:36 +03:00
ret = sirf_serdev_open ( data ) ;
2019-01-24 09:34:35 +03:00
if ( ret ) {
mutex_lock ( & data - > gdev_mutex ) ;
data - > open = false ;
mutex_unlock ( & data - > gdev_mutex ) ;
2018-06-01 11:22:58 +03:00
return ret ;
2019-01-24 09:34:35 +03:00
}
2018-06-01 11:22:58 +03:00
ret = pm_runtime_get_sync ( & serdev - > dev ) ;
if ( ret < 0 ) {
dev_err ( & gdev - > dev , " failed to runtime resume: %d \n " , ret ) ;
pm_runtime_put_noidle ( & serdev - > dev ) ;
goto err_close ;
}
return 0 ;
err_close :
2019-01-24 09:34:36 +03:00
sirf_serdev_close ( data ) ;
2018-06-01 11:22:58 +03:00
2019-01-24 09:34:35 +03:00
mutex_lock ( & data - > gdev_mutex ) ;
data - > open = false ;
mutex_unlock ( & data - > gdev_mutex ) ;
2018-06-01 11:22:58 +03:00
return ret ;
}
static void sirf_close ( struct gnss_device * gdev )
{
struct sirf_data * data = gnss_get_drvdata ( gdev ) ;
struct serdev_device * serdev = data - > serdev ;
2019-01-24 09:34:36 +03:00
sirf_serdev_close ( data ) ;
2018-06-01 11:22:58 +03:00
pm_runtime_put ( & serdev - > dev ) ;
2019-01-24 09:34:35 +03:00
mutex_lock ( & data - > gdev_mutex ) ;
data - > open = false ;
mutex_unlock ( & data - > gdev_mutex ) ;
2018-06-01 11:22:58 +03:00
}
static int sirf_write_raw ( struct gnss_device * gdev , const unsigned char * buf ,
size_t count )
{
struct sirf_data * data = gnss_get_drvdata ( gdev ) ;
struct serdev_device * serdev = data - > serdev ;
int ret ;
/* write is only buffered synchronously */
2018-11-14 11:37:54 +03:00
ret = serdev_device_write ( serdev , buf , count , MAX_SCHEDULE_TIMEOUT ) ;
2018-11-14 18:09:02 +03:00
if ( ret < 0 | | ret < count )
2018-06-01 11:22:58 +03:00
return ret ;
/* FIXME: determine if interrupted? */
serdev_device_wait_until_sent ( serdev , 0 ) ;
return count ;
}
static const struct gnss_operations sirf_gnss_ops = {
. open = sirf_open ,
. close = sirf_close ,
. write_raw = sirf_write_raw ,
} ;
2023-12-06 10:37:08 +03:00
static ssize_t sirf_receive_buf ( struct serdev_device * serdev ,
const u8 * buf , size_t count )
2018-06-01 11:22:58 +03:00
{
struct sirf_data * data = serdev_device_get_drvdata ( serdev ) ;
struct gnss_device * gdev = data - > gdev ;
2019-01-24 09:34:35 +03:00
int ret = 0 ;
2018-06-01 11:22:58 +03:00
2019-01-24 09:34:36 +03:00
if ( ! data - > wakeup & & ! data - > active ) {
data - > active = true ;
wake_up_interruptible ( & data - > power_wait ) ;
}
2019-01-24 09:34:35 +03:00
mutex_lock ( & data - > gdev_mutex ) ;
if ( data - > open )
ret = gnss_insert_raw ( gdev , buf , count ) ;
mutex_unlock ( & data - > gdev_mutex ) ;
return ret ;
2018-06-01 11:22:58 +03:00
}
static const struct serdev_device_ops sirf_serdev_ops = {
. receive_buf = sirf_receive_buf ,
. write_wakeup = serdev_device_write_wakeup ,
} ;
static irqreturn_t sirf_wakeup_handler ( int irq , void * dev_id )
{
struct sirf_data * data = dev_id ;
struct device * dev = & data - > serdev - > dev ;
int ret ;
ret = gpiod_get_value_cansleep ( data - > wakeup ) ;
dev_dbg ( dev , " %s - wakeup = %d \n " , __func__ , ret ) ;
if ( ret < 0 )
goto out ;
2019-01-22 20:22:55 +03:00
data - > active = ret ;
2018-06-01 11:22:58 +03:00
wake_up_interruptible ( & data - > power_wait ) ;
out :
return IRQ_HANDLED ;
}
2019-01-24 09:34:36 +03:00
static int sirf_wait_for_power_state_nowakeup ( struct sirf_data * data ,
bool active ,
unsigned long timeout )
{
int ret ;
/* Wait for state change (including any shutdown messages). */
msleep ( timeout ) ;
/* Wait for data reception or timeout. */
data - > active = false ;
ret = wait_event_interruptible_timeout ( data - > power_wait ,
data - > active , msecs_to_jiffies ( SIRF_REPORT_CYCLE ) ) ;
if ( ret < 0 )
return ret ;
if ( ret > 0 & & ! active )
return - ETIMEDOUT ;
if ( ret = = 0 & & active )
return - ETIMEDOUT ;
return 0 ;
}
2018-06-01 11:22:58 +03:00
static int sirf_wait_for_power_state ( struct sirf_data * data , bool active ,
unsigned long timeout )
{
int ret ;
2019-01-24 09:34:36 +03:00
if ( ! data - > wakeup )
return sirf_wait_for_power_state_nowakeup ( data , active , timeout ) ;
2018-06-01 11:22:58 +03:00
ret = wait_event_interruptible_timeout ( data - > power_wait ,
data - > active = = active , msecs_to_jiffies ( timeout ) ) ;
if ( ret < 0 )
return ret ;
if ( ret = = 0 ) {
dev_warn ( & data - > serdev - > dev , " timeout waiting for active state = %d \n " ,
active ) ;
return - ETIMEDOUT ;
}
return 0 ;
}
static void sirf_pulse_on_off ( struct sirf_data * data )
{
gpiod_set_value_cansleep ( data - > on_off , 1 ) ;
msleep ( SIRF_ON_OFF_PULSE_TIME ) ;
gpiod_set_value_cansleep ( data - > on_off , 0 ) ;
}
static int sirf_set_active ( struct sirf_data * data , bool active )
{
unsigned long timeout ;
int retries = 3 ;
int ret ;
if ( active )
timeout = SIRF_ACTIVATE_TIMEOUT ;
else
timeout = SIRF_HIBERNATE_TIMEOUT ;
2019-01-24 09:34:36 +03:00
if ( ! data - > wakeup ) {
ret = sirf_serdev_open ( data ) ;
if ( ret )
return ret ;
}
2018-12-05 13:21:49 +03:00
do {
2018-06-01 11:22:58 +03:00
sirf_pulse_on_off ( data ) ;
ret = sirf_wait_for_power_state ( data , active , timeout ) ;
2019-01-24 09:34:36 +03:00
} while ( ret = = - ETIMEDOUT & & retries - - ) ;
2018-06-01 11:22:58 +03:00
2019-01-24 09:34:36 +03:00
if ( ! data - > wakeup )
sirf_serdev_close ( data ) ;
2018-06-01 11:22:58 +03:00
2019-01-24 09:34:36 +03:00
if ( ret )
return ret ;
2018-06-01 11:22:58 +03:00
return 0 ;
}
static int sirf_runtime_suspend ( struct device * dev )
{
struct sirf_data * data = dev_get_drvdata ( dev ) ;
2019-01-24 09:34:38 +03:00
int ret2 ;
int ret ;
if ( data - > on_off )
ret = sirf_set_active ( data , false ) ;
else
ret = regulator_disable ( data - > vcc ) ;
if ( ret )
return ret ;
ret = regulator_disable ( data - > lna ) ;
if ( ret )
goto err_reenable ;
return 0 ;
2018-06-01 11:22:58 +03:00
2019-01-24 09:34:38 +03:00
err_reenable :
if ( data - > on_off )
ret2 = sirf_set_active ( data , true ) ;
else
ret2 = regulator_enable ( data - > vcc ) ;
2018-06-01 11:22:58 +03:00
2019-01-24 09:34:38 +03:00
if ( ret2 )
dev_err ( dev ,
" failed to reenable power on failed suspend: %d \n " ,
ret2 ) ;
return ret ;
2018-06-01 11:22:58 +03:00
}
static int sirf_runtime_resume ( struct device * dev )
{
struct sirf_data * data = dev_get_drvdata ( dev ) ;
2019-01-24 09:34:38 +03:00
int ret ;
2018-06-01 11:22:58 +03:00
2019-01-24 09:34:38 +03:00
ret = regulator_enable ( data - > lna ) ;
if ( ret )
return ret ;
if ( data - > on_off )
ret = sirf_set_active ( data , true ) ;
else
ret = regulator_enable ( data - > vcc ) ;
if ( ret )
goto err_disable_lna ;
2018-06-01 11:22:58 +03:00
2019-01-24 09:34:38 +03:00
return 0 ;
err_disable_lna :
regulator_disable ( data - > lna ) ;
return ret ;
2018-06-01 11:22:58 +03:00
}
static int __maybe_unused sirf_suspend ( struct device * dev )
{
struct sirf_data * data = dev_get_drvdata ( dev ) ;
int ret = 0 ;
if ( ! pm_runtime_suspended ( dev ) )
ret = sirf_runtime_suspend ( dev ) ;
if ( data - > wakeup )
disable_irq ( data - > irq ) ;
return ret ;
}
static int __maybe_unused sirf_resume ( struct device * dev )
{
struct sirf_data * data = dev_get_drvdata ( dev ) ;
int ret = 0 ;
if ( data - > wakeup )
enable_irq ( data - > irq ) ;
if ( ! pm_runtime_suspended ( dev ) )
ret = sirf_runtime_resume ( dev ) ;
return ret ;
}
static const struct dev_pm_ops sirf_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( sirf_suspend , sirf_resume )
SET_RUNTIME_PM_OPS ( sirf_runtime_suspend , sirf_runtime_resume , NULL )
} ;
static int sirf_parse_dt ( struct serdev_device * serdev )
{
struct sirf_data * data = serdev_device_get_drvdata ( serdev ) ;
struct device_node * node = serdev - > dev . of_node ;
u32 speed = 9600 ;
of_property_read_u32 ( node , " current-speed " , & speed ) ;
data - > speed = speed ;
return 0 ;
}
static int sirf_probe ( struct serdev_device * serdev )
{
struct device * dev = & serdev - > dev ;
struct gnss_device * gdev ;
struct sirf_data * data ;
int ret ;
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
gdev = gnss_allocate_device ( dev ) ;
if ( ! gdev )
return - ENOMEM ;
2018-06-01 11:22:59 +03:00
gdev - > type = GNSS_TYPE_SIRF ;
2018-06-01 11:22:58 +03:00
gdev - > ops = & sirf_gnss_ops ;
gnss_set_drvdata ( gdev , data ) ;
data - > serdev = serdev ;
data - > gdev = gdev ;
2019-01-24 09:34:35 +03:00
mutex_init ( & data - > gdev_mutex ) ;
2019-01-24 09:34:36 +03:00
mutex_init ( & data - > serdev_mutex ) ;
2018-06-01 11:22:58 +03:00
init_waitqueue_head ( & data - > power_wait ) ;
serdev_device_set_drvdata ( serdev , data ) ;
serdev_device_set_client_ops ( serdev , & sirf_serdev_ops ) ;
ret = sirf_parse_dt ( serdev ) ;
if ( ret )
goto err_put_device ;
data - > vcc = devm_regulator_get ( dev , " vcc " ) ;
if ( IS_ERR ( data - > vcc ) ) {
ret = PTR_ERR ( data - > vcc ) ;
goto err_put_device ;
}
2019-01-24 09:34:38 +03:00
data - > lna = devm_regulator_get ( dev , " lna " ) ;
if ( IS_ERR ( data - > lna ) ) {
ret = PTR_ERR ( data - > lna ) ;
goto err_put_device ;
}
2018-06-01 11:22:58 +03:00
data - > on_off = devm_gpiod_get_optional ( dev , " sirf,onoff " ,
GPIOD_OUT_LOW ) ;
2020-05-07 12:42:52 +03:00
if ( IS_ERR ( data - > on_off ) ) {
ret = PTR_ERR ( data - > on_off ) ;
2018-06-01 11:22:58 +03:00
goto err_put_device ;
2020-05-07 12:42:52 +03:00
}
2018-06-01 11:22:58 +03:00
if ( data - > on_off ) {
data - > wakeup = devm_gpiod_get_optional ( dev , " sirf,wakeup " ,
GPIOD_IN ) ;
2020-05-07 12:42:52 +03:00
if ( IS_ERR ( data - > wakeup ) ) {
ret = PTR_ERR ( data - > wakeup ) ;
2018-06-01 11:22:58 +03:00
goto err_put_device ;
2020-05-07 12:42:52 +03:00
}
2018-06-01 11:22:58 +03:00
2019-01-22 20:22:53 +03:00
ret = regulator_enable ( data - > vcc ) ;
if ( ret )
goto err_put_device ;
/* Wait for chip to boot into hibernate mode. */
msleep ( SIRF_BOOT_DELAY ) ;
2018-06-01 11:22:58 +03:00
}
if ( data - > wakeup ) {
2019-01-22 20:22:54 +03:00
ret = gpiod_get_value_cansleep ( data - > wakeup ) ;
if ( ret < 0 )
goto err_disable_vcc ;
data - > active = ret ;
2018-06-01 11:22:58 +03:00
ret = gpiod_to_irq ( data - > wakeup ) ;
if ( ret < 0 )
2019-01-22 20:22:53 +03:00
goto err_disable_vcc ;
2018-06-01 11:22:58 +03:00
data - > irq = ret ;
2019-01-22 20:22:53 +03:00
ret = request_threaded_irq ( data - > irq , NULL , sirf_wakeup_handler ,
2018-06-01 11:22:58 +03:00
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT ,
" wakeup " , data ) ;
if ( ret )
2019-01-22 20:22:53 +03:00
goto err_disable_vcc ;
2018-06-01 11:22:58 +03:00
}
2019-01-22 20:22:54 +03:00
if ( data - > on_off ) {
2019-01-24 09:34:36 +03:00
if ( ! data - > wakeup ) {
data - > active = false ;
ret = sirf_serdev_open ( data ) ;
if ( ret )
goto err_disable_vcc ;
msleep ( SIRF_REPORT_CYCLE ) ;
sirf_serdev_close ( data ) ;
}
2019-01-22 20:22:54 +03:00
/* Force hibernate mode if already active. */
if ( data - > active ) {
ret = sirf_set_active ( data , false ) ;
if ( ret ) {
dev_err ( dev , " failed to set hibernate mode: %d \n " ,
ret ) ;
goto err_free_irq ;
}
}
}
2018-06-01 11:22:58 +03:00
if ( IS_ENABLED ( CONFIG_PM ) ) {
pm_runtime_set_suspended ( dev ) ; /* clear runtime_error flag */
pm_runtime_enable ( dev ) ;
} else {
ret = sirf_runtime_resume ( dev ) ;
if ( ret < 0 )
2019-01-22 20:22:53 +03:00
goto err_free_irq ;
2018-06-01 11:22:58 +03:00
}
ret = gnss_register_device ( gdev ) ;
if ( ret )
goto err_disable_rpm ;
return 0 ;
err_disable_rpm :
if ( IS_ENABLED ( CONFIG_PM ) )
pm_runtime_disable ( dev ) ;
else
sirf_runtime_suspend ( dev ) ;
2019-01-22 20:22:53 +03:00
err_free_irq :
if ( data - > wakeup )
free_irq ( data - > irq , data ) ;
2018-06-01 11:22:58 +03:00
err_disable_vcc :
if ( data - > on_off )
regulator_disable ( data - > vcc ) ;
err_put_device :
gnss_put_device ( data - > gdev ) ;
return ret ;
}
static void sirf_remove ( struct serdev_device * serdev )
{
struct sirf_data * data = serdev_device_get_drvdata ( serdev ) ;
gnss_deregister_device ( data - > gdev ) ;
if ( IS_ENABLED ( CONFIG_PM ) )
pm_runtime_disable ( & serdev - > dev ) ;
else
sirf_runtime_suspend ( & serdev - > dev ) ;
2019-01-22 20:22:53 +03:00
if ( data - > wakeup )
free_irq ( data - > irq , data ) ;
2018-06-01 11:22:58 +03:00
if ( data - > on_off )
regulator_disable ( data - > vcc ) ;
gnss_put_device ( data - > gdev ) ;
2021-01-08 14:22:33 +03:00
}
2018-06-01 11:22:58 +03:00
# ifdef CONFIG_OF
static const struct of_device_id sirf_of_match [ ] = {
{ . compatible = " fastrax,uc430 " } ,
{ . compatible = " linx,r4 " } ,
2019-01-24 09:34:36 +03:00
{ . compatible = " wi2wi,w2sg0004 " } ,
2018-06-01 11:22:58 +03:00
{ . compatible = " wi2wi,w2sg0008i " } ,
{ . compatible = " wi2wi,w2sg0084i " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , sirf_of_match ) ;
# endif
static struct serdev_device_driver sirf_driver = {
. driver = {
. name = " gnss-sirf " ,
. of_match_table = of_match_ptr ( sirf_of_match ) ,
. pm = & sirf_pm_ops ,
} ,
. probe = sirf_probe ,
. remove = sirf_remove ,
} ;
module_serdev_device_driver ( sirf_driver ) ;
MODULE_AUTHOR ( " Johan Hovold <johan@kernel.org> " ) ;
MODULE_DESCRIPTION ( " SiRFstar GNSS receiver driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;