mux: add support for delay after muxing
Hardware may require some time for the muxed analog signals to settle after the muxing is changed. Allow users of the mux subsystem to specify this delay with the new mux_control_select_delay() function (and the _try equivalent). Signed-off-by: Vincent Whitchurch <vincent.whitchurch@axis.com> Reviewed-by: Lars-Peter Clausen <lars@metafoo.de> Tested-by: Lars-Peter Clausen <lars@metafoo.de> Acked-by: Peter Rosin <peda@axentia.se> Link: https://lore.kernel.org/r/20211007134641.13417-2-vincent.whitchurch@axis.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
parent
796e5d0b1e
commit
17b5b576ff
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#define pr_fmt(fmt) "mux-core: " fmt
|
#define pr_fmt(fmt) "mux-core: " fmt
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
|
@ -116,6 +117,7 @@ struct mux_chip *mux_chip_alloc(struct device *dev,
|
||||||
sema_init(&mux->lock, 1);
|
sema_init(&mux->lock, 1);
|
||||||
mux->cached_state = MUX_CACHE_UNKNOWN;
|
mux->cached_state = MUX_CACHE_UNKNOWN;
|
||||||
mux->idle_state = MUX_IDLE_AS_IS;
|
mux->idle_state = MUX_IDLE_AS_IS;
|
||||||
|
mux->last_change = ktime_get();
|
||||||
}
|
}
|
||||||
|
|
||||||
device_initialize(&mux_chip->dev);
|
device_initialize(&mux_chip->dev);
|
||||||
|
@ -129,6 +131,8 @@ static int mux_control_set(struct mux_control *mux, int state)
|
||||||
int ret = mux->chip->ops->set(mux, state);
|
int ret = mux->chip->ops->set(mux, state);
|
||||||
|
|
||||||
mux->cached_state = ret < 0 ? MUX_CACHE_UNKNOWN : state;
|
mux->cached_state = ret < 0 ? MUX_CACHE_UNKNOWN : state;
|
||||||
|
if (ret >= 0)
|
||||||
|
mux->last_change = ktime_get();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -314,10 +318,25 @@ static int __mux_control_select(struct mux_control *mux, int state)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mux_control_delay(struct mux_control *mux, unsigned int delay_us)
|
||||||
|
{
|
||||||
|
ktime_t delayend;
|
||||||
|
s64 remaining;
|
||||||
|
|
||||||
|
if (!delay_us)
|
||||||
|
return;
|
||||||
|
|
||||||
|
delayend = ktime_add_us(mux->last_change, delay_us);
|
||||||
|
remaining = ktime_us_delta(delayend, ktime_get());
|
||||||
|
if (remaining > 0)
|
||||||
|
fsleep(remaining);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mux_control_select() - Select the given multiplexer state.
|
* mux_control_select_delay() - Select the given multiplexer state.
|
||||||
* @mux: The mux-control to request a change of state from.
|
* @mux: The mux-control to request a change of state from.
|
||||||
* @state: The new requested state.
|
* @state: The new requested state.
|
||||||
|
* @delay_us: The time to delay (in microseconds) if the mux state is changed.
|
||||||
*
|
*
|
||||||
* On successfully selecting the mux-control state, it will be locked until
|
* On successfully selecting the mux-control state, it will be locked until
|
||||||
* there is a call to mux_control_deselect(). If the mux-control is already
|
* there is a call to mux_control_deselect(). If the mux-control is already
|
||||||
|
@ -331,7 +350,8 @@ static int __mux_control_select(struct mux_control *mux, int state)
|
||||||
* Return: 0 when the mux-control state has the requested state or a negative
|
* Return: 0 when the mux-control state has the requested state or a negative
|
||||||
* errno on error.
|
* errno on error.
|
||||||
*/
|
*/
|
||||||
int mux_control_select(struct mux_control *mux, unsigned int state)
|
int mux_control_select_delay(struct mux_control *mux, unsigned int state,
|
||||||
|
unsigned int delay_us)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -340,18 +360,21 @@ int mux_control_select(struct mux_control *mux, unsigned int state)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = __mux_control_select(mux, state);
|
ret = __mux_control_select(mux, state);
|
||||||
|
if (ret >= 0)
|
||||||
|
mux_control_delay(mux, delay_us);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
up(&mux->lock);
|
up(&mux->lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(mux_control_select);
|
EXPORT_SYMBOL_GPL(mux_control_select_delay);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mux_control_try_select() - Try to select the given multiplexer state.
|
* mux_control_try_select_delay() - Try to select the given multiplexer state.
|
||||||
* @mux: The mux-control to request a change of state from.
|
* @mux: The mux-control to request a change of state from.
|
||||||
* @state: The new requested state.
|
* @state: The new requested state.
|
||||||
|
* @delay_us: The time to delay (in microseconds) if the mux state is changed.
|
||||||
*
|
*
|
||||||
* On successfully selecting the mux-control state, it will be locked until
|
* On successfully selecting the mux-control state, it will be locked until
|
||||||
* mux_control_deselect() called.
|
* mux_control_deselect() called.
|
||||||
|
@ -363,7 +386,8 @@ EXPORT_SYMBOL_GPL(mux_control_select);
|
||||||
* Return: 0 when the mux-control state has the requested state or a negative
|
* Return: 0 when the mux-control state has the requested state or a negative
|
||||||
* errno on error. Specifically -EBUSY if the mux-control is contended.
|
* errno on error. Specifically -EBUSY if the mux-control is contended.
|
||||||
*/
|
*/
|
||||||
int mux_control_try_select(struct mux_control *mux, unsigned int state)
|
int mux_control_try_select_delay(struct mux_control *mux, unsigned int state,
|
||||||
|
unsigned int delay_us)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -371,13 +395,15 @@ int mux_control_try_select(struct mux_control *mux, unsigned int state)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
ret = __mux_control_select(mux, state);
|
ret = __mux_control_select(mux, state);
|
||||||
|
if (ret >= 0)
|
||||||
|
mux_control_delay(mux, delay_us);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
up(&mux->lock);
|
up(&mux->lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(mux_control_try_select);
|
EXPORT_SYMBOL_GPL(mux_control_try_select_delay);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mux_control_deselect() - Deselect the previously selected multiplexer state.
|
* mux_control_deselect() - Deselect the previously selected multiplexer state.
|
||||||
|
|
|
@ -16,10 +16,25 @@ struct device;
|
||||||
struct mux_control;
|
struct mux_control;
|
||||||
|
|
||||||
unsigned int mux_control_states(struct mux_control *mux);
|
unsigned int mux_control_states(struct mux_control *mux);
|
||||||
int __must_check mux_control_select(struct mux_control *mux,
|
int __must_check mux_control_select_delay(struct mux_control *mux,
|
||||||
unsigned int state);
|
unsigned int state,
|
||||||
int __must_check mux_control_try_select(struct mux_control *mux,
|
unsigned int delay_us);
|
||||||
unsigned int state);
|
int __must_check mux_control_try_select_delay(struct mux_control *mux,
|
||||||
|
unsigned int state,
|
||||||
|
unsigned int delay_us);
|
||||||
|
|
||||||
|
static inline int __must_check mux_control_select(struct mux_control *mux,
|
||||||
|
unsigned int state)
|
||||||
|
{
|
||||||
|
return mux_control_select_delay(mux, state, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int __must_check mux_control_try_select(struct mux_control *mux,
|
||||||
|
unsigned int state)
|
||||||
|
{
|
||||||
|
return mux_control_try_select_delay(mux, state, 0);
|
||||||
|
}
|
||||||
|
|
||||||
int mux_control_deselect(struct mux_control *mux);
|
int mux_control_deselect(struct mux_control *mux);
|
||||||
|
|
||||||
struct mux_control *mux_control_get(struct device *dev, const char *mux_name);
|
struct mux_control *mux_control_get(struct device *dev, const char *mux_name);
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <dt-bindings/mux/mux.h>
|
#include <dt-bindings/mux/mux.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
#include <linux/ktime.h>
|
||||||
#include <linux/semaphore.h>
|
#include <linux/semaphore.h>
|
||||||
|
|
||||||
struct mux_chip;
|
struct mux_chip;
|
||||||
|
@ -33,6 +34,7 @@ struct mux_control_ops {
|
||||||
* @states: The number of mux controller states.
|
* @states: The number of mux controller states.
|
||||||
* @idle_state: The mux controller state to use when inactive, or one
|
* @idle_state: The mux controller state to use when inactive, or one
|
||||||
* of MUX_IDLE_AS_IS and MUX_IDLE_DISCONNECT.
|
* of MUX_IDLE_AS_IS and MUX_IDLE_DISCONNECT.
|
||||||
|
* @last_change: Timestamp of last change
|
||||||
*
|
*
|
||||||
* Mux drivers may only change @states and @idle_state, and may only do so
|
* Mux drivers may only change @states and @idle_state, and may only do so
|
||||||
* between allocation and registration of the mux controller. Specifically,
|
* between allocation and registration of the mux controller. Specifically,
|
||||||
|
@ -47,6 +49,8 @@ struct mux_control {
|
||||||
|
|
||||||
unsigned int states;
|
unsigned int states;
|
||||||
int idle_state;
|
int idle_state;
|
||||||
|
|
||||||
|
ktime_t last_change;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue