2019-05-29 17:17:56 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-04-16 10:29:00 +04:00
/*
* GPIO - based I2C Arbitration Using a Challenge & Response Mechanism
*
* Copyright ( C ) 2012 Google , Inc
*/
# include <linux/delay.h>
2019-06-01 19:59:31 +03:00
# include <linux/gpio/consumer.h>
2013-04-16 10:29:00 +04:00
# include <linux/kernel.h>
# include <linux/i2c.h>
# include <linux/i2c-mux.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
/**
* struct i2c_arbitrator_data - Driver data for I2C arbitrator
*
2019-06-01 19:59:31 +03:00
* @ our_gpio : GPIO descriptor we ' ll use to claim .
* @ their_gpio : GPIO descriptor that the other side will use to claim .
2013-04-16 10:29:00 +04:00
* @ slew_delay_us : microseconds to wait for a GPIO to go high .
* @ wait_retry_us : we ' ll attempt another claim after this many microseconds .
* @ wait_free_us : we ' ll give up after this many microseconds .
*/
struct i2c_arbitrator_data {
2019-06-01 19:59:31 +03:00
struct gpio_desc * our_gpio ;
struct gpio_desc * their_gpio ;
2013-04-16 10:29:00 +04:00
unsigned int slew_delay_us ;
unsigned int wait_retry_us ;
unsigned int wait_free_us ;
} ;
2021-05-20 22:00:51 +03:00
/*
2013-04-16 10:29:00 +04:00
* i2c_arbitrator_select - claim the I2C bus
*
* Use the GPIO - based signalling protocol ; return - EBUSY if we fail .
*/
2016-04-20 09:39:46 +03:00
static int i2c_arbitrator_select ( struct i2c_mux_core * muxc , u32 chan )
2013-04-16 10:29:00 +04:00
{
2016-04-20 09:39:46 +03:00
const struct i2c_arbitrator_data * arb = i2c_mux_priv ( muxc ) ;
2013-04-16 10:29:00 +04:00
unsigned long stop_retry , stop_time ;
/* Start a round of trying to claim the bus */
stop_time = jiffies + usecs_to_jiffies ( arb - > wait_free_us ) + 1 ;
do {
/* Indicate that we want to claim the bus */
2019-06-01 19:59:31 +03:00
gpiod_set_value ( arb - > our_gpio , 1 ) ;
2013-04-16 10:29:00 +04:00
udelay ( arb - > slew_delay_us ) ;
/* Wait for the other master to release it */
stop_retry = jiffies + usecs_to_jiffies ( arb - > wait_retry_us ) + 1 ;
while ( time_before ( jiffies , stop_retry ) ) {
2019-06-01 19:59:31 +03:00
int gpio_val = gpiod_get_value ( arb - > their_gpio ) ;
2013-04-16 10:29:00 +04:00
2019-06-01 19:59:31 +03:00
if ( ! gpio_val ) {
2013-04-16 10:29:00 +04:00
/* We got it, so return */
return 0 ;
}
usleep_range ( 50 , 200 ) ;
}
/* It didn't release, so give up, wait, and try again */
2019-06-01 19:59:31 +03:00
gpiod_set_value ( arb - > our_gpio , 0 ) ;
2013-04-16 10:29:00 +04:00
usleep_range ( arb - > wait_retry_us , arb - > wait_retry_us * 2 ) ;
} while ( time_before ( jiffies , stop_time ) ) ;
/* Give up, release our claim */
2019-06-01 19:59:31 +03:00
gpiod_set_value ( arb - > our_gpio , 0 ) ;
2013-04-16 10:29:00 +04:00
udelay ( arb - > slew_delay_us ) ;
2016-04-20 09:39:46 +03:00
dev_err ( muxc - > dev , " Could not claim bus, timeout \n " ) ;
2013-04-16 10:29:00 +04:00
return - EBUSY ;
}
2021-05-20 22:00:51 +03:00
/*
2013-04-16 10:29:00 +04:00
* i2c_arbitrator_deselect - release the I2C bus
*
* Release the I2C bus using the GPIO - based signalling protocol .
*/
2016-04-20 09:39:46 +03:00
static int i2c_arbitrator_deselect ( struct i2c_mux_core * muxc , u32 chan )
2013-04-16 10:29:00 +04:00
{
2016-04-20 09:39:46 +03:00
const struct i2c_arbitrator_data * arb = i2c_mux_priv ( muxc ) ;
2013-04-16 10:29:00 +04:00
/* Release the bus and wait for the other master to notice */
2019-06-01 19:59:31 +03:00
gpiod_set_value ( arb - > our_gpio , 0 ) ;
2013-04-16 10:29:00 +04:00
udelay ( arb - > slew_delay_us ) ;
return 0 ;
}
static int i2c_arbitrator_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
struct device_node * parent_np ;
2016-04-20 09:39:46 +03:00
struct i2c_mux_core * muxc ;
2013-04-16 10:29:00 +04:00
struct i2c_arbitrator_data * arb ;
2019-06-01 19:59:31 +03:00
struct gpio_desc * dummy ;
2013-04-16 10:29:00 +04:00
int ret ;
/* We only support probing from device tree; no platform_data */
if ( ! np ) {
dev_err ( dev , " Cannot find device tree node \n " ) ;
return - ENODEV ;
}
2013-07-30 11:59:33 +04:00
if ( dev_get_platdata ( dev ) ) {
2013-04-16 10:29:00 +04:00
dev_err ( dev , " Platform data is not supported \n " ) ;
return - EINVAL ;
}
2016-08-11 17:49:54 +03:00
muxc = i2c_mux_alloc ( NULL , dev , 1 , sizeof ( * arb ) , I2C_MUX_ARBITRATOR ,
2016-04-20 09:39:46 +03:00
i2c_arbitrator_select , i2c_arbitrator_deselect ) ;
if ( ! muxc )
2013-04-16 10:29:00 +04:00
return - ENOMEM ;
2016-04-20 09:39:46 +03:00
arb = i2c_mux_priv ( muxc ) ;
platform_set_drvdata ( pdev , muxc ) ;
2013-04-16 10:29:00 +04:00
2019-06-01 19:59:31 +03:00
/* Request GPIOs, our GPIO as unclaimed to begin with */
arb - > our_gpio = devm_gpiod_get ( dev , " our-claim " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( arb - > our_gpio ) ) {
dev_err ( dev , " could not get \" our-claim \" GPIO (%ld) \n " ,
PTR_ERR ( arb - > our_gpio ) ) ;
return PTR_ERR ( arb - > our_gpio ) ;
2013-04-16 10:29:00 +04:00
}
2019-06-01 19:59:31 +03:00
arb - > their_gpio = devm_gpiod_get ( dev , " their-claim " , GPIOD_IN ) ;
if ( IS_ERR ( arb - > their_gpio ) ) {
dev_err ( dev , " could not get \" their-claim \" GPIO (%ld) \n " ,
PTR_ERR ( arb - > their_gpio ) ) ;
return PTR_ERR ( arb - > their_gpio ) ;
2013-04-16 10:29:00 +04:00
}
/* At the moment we only support a single two master (us + 1 other) */
2019-06-01 19:59:31 +03:00
dummy = devm_gpiod_get_index ( dev , " their-claim " , 1 , GPIOD_IN ) ;
if ( ! IS_ERR ( dummy ) ) {
2013-04-16 10:29:00 +04:00
dev_err ( dev , " Only one other master is supported \n " ) ;
return - EINVAL ;
2019-06-01 19:59:31 +03:00
} else if ( PTR_ERR ( dummy ) = = - EPROBE_DEFER ) {
return - EPROBE_DEFER ;
2013-04-16 10:29:00 +04:00
}
/* Arbitration parameters */
if ( of_property_read_u32 ( np , " slew-delay-us " , & arb - > slew_delay_us ) )
arb - > slew_delay_us = 10 ;
if ( of_property_read_u32 ( np , " wait-retry-us " , & arb - > wait_retry_us ) )
arb - > wait_retry_us = 3000 ;
if ( of_property_read_u32 ( np , " wait-free-us " , & arb - > wait_free_us ) )
arb - > wait_free_us = 50000 ;
/* Find our parent */
parent_np = of_parse_phandle ( np , " i2c-parent " , 0 ) ;
if ( ! parent_np ) {
dev_err ( dev , " Cannot parse i2c-parent \n " ) ;
return - EINVAL ;
}
2016-04-20 09:39:46 +03:00
muxc - > parent = of_get_i2c_adapter_by_node ( parent_np ) ;
2015-08-26 23:59:33 +03:00
of_node_put ( parent_np ) ;
2016-04-20 09:39:46 +03:00
if ( ! muxc - > parent ) {
2013-04-16 10:29:00 +04:00
dev_err ( dev , " Cannot find parent bus \n " ) ;
2013-10-10 12:19:13 +04:00
return - EPROBE_DEFER ;
2013-04-16 10:29:00 +04:00
}
/* Actually add the mux adapter */
2016-04-20 09:39:46 +03:00
ret = i2c_mux_add_adapter ( muxc , 0 , 0 , 0 ) ;
2017-04-03 11:14:06 +03:00
if ( ret )
2016-04-20 09:39:46 +03:00
i2c_put_adapter ( muxc - > parent ) ;
2013-04-16 10:29:00 +04:00
return ret ;
}
2023-05-08 23:51:38 +03:00
static void i2c_arbitrator_remove ( struct platform_device * pdev )
2013-04-16 10:29:00 +04:00
{
2016-04-20 09:39:46 +03:00
struct i2c_mux_core * muxc = platform_get_drvdata ( pdev ) ;
2013-04-16 10:29:00 +04:00
2016-04-20 09:39:46 +03:00
i2c_mux_del_adapters ( muxc ) ;
i2c_put_adapter ( muxc - > parent ) ;
2013-04-16 10:29:00 +04:00
}
static const struct of_device_id i2c_arbitrator_of_match [ ] = {
{ . compatible = " i2c-arb-gpio-challenge " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , i2c_arbitrator_of_match ) ;
static struct platform_driver i2c_arbitrator_driver = {
. probe = i2c_arbitrator_probe ,
2023-05-08 23:51:38 +03:00
. remove_new = i2c_arbitrator_remove ,
2013-04-16 10:29:00 +04:00
. driver = {
. name = " i2c-arb-gpio-challenge " ,
2013-09-30 07:34:25 +04:00
. of_match_table = i2c_arbitrator_of_match ,
2013-04-16 10:29:00 +04:00
} ,
} ;
module_platform_driver ( i2c_arbitrator_driver ) ;
MODULE_DESCRIPTION ( " GPIO-based I2C Arbitration " ) ;
MODULE_AUTHOR ( " Doug Anderson <dianders@chromium.org> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:i2c-arb-gpio-challenge " ) ;