@@ -342,7 +342,8 @@ MUX
devm_mux_chip_free()
devm_mux_chip_register()
devm_mux_chip_unregister()
- devm_mux_control_get()
+ devm_mux_control_get_shared()
+ devm_mux_control_get_exclusive()
devm_mux_control_put()
PER-CPU MEM
@@ -87,7 +87,7 @@ static int i2c_mux_probe(struct platform_device *pdev)
if (!mux)
return -ENOMEM;
- mux->control = devm_mux_control_get(dev, NULL);
+ mux->control = devm_mux_control_get_shared(dev, NULL);
if (IS_ERR(mux->control)) {
if (PTR_ERR(mux->control) != -EPROBE_DEFER)
dev_err(dev, "failed to get control-mux\n");
@@ -413,7 +413,7 @@ static int mux_probe(struct platform_device *pdev)
}
}
- mux->control = devm_mux_control_get(dev, NULL);
+ mux->control = devm_mux_control_get_shared(dev, NULL);
if (IS_ERR(mux->control)) {
if (PTR_ERR(mux->control) != -EPROBE_DEFER)
dev_err(dev, "failed to get control-mux\n");
@@ -37,6 +37,7 @@ static struct class mux_class = {
};
static DEFINE_IDA(mux_ida);
+static DEFINE_MUTEX(mux_lock);
static int __init mux_init(void)
{
@@ -333,7 +334,8 @@ unsigned int mux_control_states(struct mux_control *mux)
EXPORT_SYMBOL_GPL(mux_control_states);
/*
- * The mux->lock must be held when calling this function.
+ * The mux->lock must be held when calling this function if the
+ * mux-control is shared.
*/
static int __mux_control_select(struct mux_control *mux, int state)
{
@@ -372,11 +374,12 @@ int mux_control_select(struct mux_control *mux, unsigned int state)
{
int ret;
- mutex_lock(&mux->lock);
+ if (mux->shared >= 0)
+ mutex_lock(&mux->lock);
ret = __mux_control_select(mux, state);
- if (ret < 0)
+ if (ret < 0 && mux->shared >= 0)
mutex_unlock(&mux->lock);
return ret;
@@ -399,12 +402,12 @@ int mux_control_try_select(struct mux_control *mux, unsigned int state)
{
int ret;
- if (!mutex_trylock(&mux->lock))
+ if (mux->shared >= 0 && !mutex_trylock(&mux->lock))
return -EBUSY;
ret = __mux_control_select(mux, state);
- if (ret < 0)
+ if (ret < 0 && mux->shared >= 0)
mutex_unlock(&mux->lock);
return ret;
@@ -427,7 +430,8 @@ int mux_control_deselect(struct mux_control *mux)
mux->idle_state != mux->cached_state)
ret = mux_control_set(mux, mux->idle_state);
- mutex_unlock(&mux->lock);
+ if (mux->shared >= 0)
+ mutex_unlock(&mux->lock);
return ret;
}
@@ -447,18 +451,13 @@ static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np)
return dev ? to_mux_chip(dev) : NULL;
}
-/**
- * mux_control_get() - Get the mux-control for a device.
- * @dev: The device that needs a mux-control.
- * @mux_name: The name identifying the mux-control.
- *
- * Return: A pointer to the mux-control, or an ERR_PTR with a negative errno.
- */
-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,
+ bool shared)
{
struct device_node *np = dev->of_node;
struct of_phandle_args args;
struct mux_chip *mux_chip;
+ struct mux_control *mux;
unsigned int controller;
int index = 0;
int ret;
@@ -504,10 +503,20 @@ struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
return ERR_PTR(-EINVAL);
}
+ mux = &mux_chip->mux[controller];
+
+ mutex_lock(&mux_lock);
+ if (WARN_ON(mux->shared < 0 || (!shared && mux->shared))) {
+ mutex_unlock(&mux_lock);
+ return ERR_PTR(-EBUSY);
+ }
+ mux->shared = shared ? mux->shared + 1 : -1;
+ mutex_unlock(&mux_lock);
+
get_device(&mux_chip->dev);
- return &mux_chip->mux[controller];
+ return mux;
}
-EXPORT_SYMBOL_GPL(mux_control_get);
+EXPORT_SYMBOL_GPL(__mux_control_get);
/**
* mux_control_put() - Put away the mux-control for good.
@@ -517,6 +526,14 @@ EXPORT_SYMBOL_GPL(mux_control_get);
*/
void mux_control_put(struct mux_control *mux)
{
+ mutex_lock(&mux_lock);
+ WARN_ON(!mux->shared);
+ if (mux->shared > 0)
+ --mux->shared;
+ else
+ mux->shared = 0;
+ mutex_unlock(&mux_lock);
+
put_device(&mux->chip->dev);
}
EXPORT_SYMBOL_GPL(mux_control_put);
@@ -528,16 +545,9 @@ static void devm_mux_control_release(struct device *dev, void *res)
mux_control_put(mux);
}
-/**
- * devm_mux_control_get() - Get the mux-control for a device, with resource
- * management.
- * @dev: The device that needs a mux-control.
- * @mux_name: The name identifying the mux-control.
- *
- * Return: Pointer to the mux-control, or an ERR_PTR with a negative errno.
- */
-struct mux_control *devm_mux_control_get(struct device *dev,
- const char *mux_name)
+struct mux_control *__devm_mux_control_get(struct device *dev,
+ const char *mux_name,
+ bool shared)
{
struct mux_control **ptr, *mux;
@@ -545,7 +555,7 @@ struct mux_control *devm_mux_control_get(struct device *dev,
if (!ptr)
return ERR_PTR(-ENOMEM);
- mux = mux_control_get(dev, mux_name);
+ mux = __mux_control_get(dev, mux_name, shared);
if (IS_ERR(mux)) {
devres_free(ptr);
return mux;
@@ -556,7 +566,7 @@ struct mux_control *devm_mux_control_get(struct device *dev,
return mux;
}
-EXPORT_SYMBOL_GPL(devm_mux_control_get);
+EXPORT_SYMBOL_GPL(__devm_mux_control_get);
static int devm_mux_control_match(struct device *dev, void *res, void *data)
{
@@ -23,11 +23,81 @@ int __must_check mux_control_try_select(struct mux_control *mux,
unsigned int state);
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, bool shared);
void mux_control_put(struct mux_control *mux);
-
-struct mux_control *devm_mux_control_get(struct device *dev,
- const char *mux_name);
+struct mux_control *__devm_mux_control_get(struct device *dev,
+ const char *mux_name, bool shared);
void devm_mux_control_put(struct device *dev, struct mux_control *mux);
+/**
+ * mux_control_get_shared() - Get the mux-control for a device.
+ * @dev: The device that needs a mux-control.
+ * @mux_name: The name identifying the mux-control.
+ *
+ * A shared mux-control can be operated by several independent consumers.
+ * The mux core will coordinate access by grabbing a lock when the mux-control
+ * is selected and releasing it when it is deselected.
+ *
+ * Return: A pointer to the mux-control, or an ERR_PTR with a negative errno.
+ */
+static inline struct mux_control *mux_control_get_shared(
+ struct device *dev,
+ const char *mux_name)
+{
+ return __mux_control_get(dev, mux_name, true);
+}
+
+/**
+ * mux_control_get_exclusive() - Get the mux-control for a device.
+ * @dev: The device that needs a mux-control.
+ * @mux_name: The name identifying the mux-control.
+ *
+ * An exclusive mux-control can only be operated by a single consumer. The
+ * mux core will return EBUSY for any further attempts to get a mux-control
+ * that already has an exclusive consumer. The mux core will also not lock
+ * the mux-control on mux_control_select, and the consumer is free to call
+ * repeatedly call select without calling mux_control_deselect. The consumer
+ * may however still call mux_control_deselect in order to activate the idle
+ * state.
+ *
+ * Return: A pointer to the mux-control, or an ERR_PTR with a negative errno.
+ */
+static inline struct mux_control *mux_control_get_exclusive(
+ struct device *dev,
+ const char *mux_name)
+{
+ return __mux_control_get(dev, mux_name, false);
+}
+
+/**
+ * devm_mux_control_get_shared() - Get a shared mux-control for a device, with
+ * resource management.
+ * @dev: The device that needs a mux-control.
+ * @mux_name: The name identifying the mux-control.
+ *
+ * Return: Pointer to the mux-control, or an ERR_PTR with a negative errno.
+ */
+static inline struct mux_control *devm_mux_control_get_shared(
+ struct device *dev,
+ const char *mux_name)
+{
+ return __devm_mux_control_get(dev, mux_name, true);
+}
+
+/**
+ * devm_mux_control_get_exclusive() - Get an exclusive mux-control for a
+ * device, with resource management.
+ * @dev: The device that needs a mux-control.
+ * @mux_name: The name identifying the mux-control.
+ *
+ * Return: Pointer to the mux-control, or an ERR_PTR with a negative errno.
+ */
+static inline struct mux_control *devm_mux_control_get_exclusive(
+ struct device *dev,
+ const char *mux_name)
+{
+ return __devm_mux_control_get(dev, mux_name, false);
+}
+
#endif /* _LINUX_MUX_CONSUMER_H */
@@ -33,6 +33,8 @@ struct mux_control_ops {
* struct mux_control - Represents a mux controller.
* @lock: Protects the mux controller state.
* @chip: The mux chip that is handling this mux controller.
+ * @shared: The shared state, -1 if exclusive, 0 if no consumer
+ * and if positive the number of sharing consumers.
* @cached_state: The current mux controller state, or -1 if none.
* @states: The number of mux controller states.
* @idle_state: The mux controller state to use when inactive, or one
@@ -40,13 +42,14 @@ struct mux_control_ops {
*
* Mux drivers may only change @states and @idle_state, and may only do so
* between allocation and registration of the mux controller. Specifically,
- * @cached_state is internal to the mux core and should never be written by
- * mux drivers.
+ * @cached_state and @shared are internal to the mux core and should never be
+ * written by mux drivers.
*/
struct mux_control {
struct mutex lock; /* protects the state of the mux */
struct mux_chip *chip;
+ int shared;
int cached_state;
unsigned int states;