regmap: Add hardware spinlock support
On some platforms, when reading or writing some special registers through regmap, we should acquire one hardware spinlock to synchronize between the multiple subsystems. Thus this patch adds the hardware spinlock support for regmap. Signed-off-by: Baolin Wang <baolin.wang@linaro.org> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
@@ -157,6 +157,8 @@ struct regmap {
|
||||
|
||||
struct rb_root range_tree;
|
||||
void *selector_work_buf; /* Scratch buffer used for selector */
|
||||
|
||||
struct hwspinlock *hwlock;
|
||||
};
|
||||
|
||||
struct regcache_ops {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/hwspinlock.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace.h"
|
||||
@@ -413,6 +414,49 @@ static unsigned int regmap_parse_64_native(const void *buf)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void regmap_lock_hwlock(void *__map)
|
||||
{
|
||||
struct regmap *map = __map;
|
||||
|
||||
hwspin_lock_timeout(map->hwlock, UINT_MAX);
|
||||
}
|
||||
|
||||
static void regmap_lock_hwlock_irq(void *__map)
|
||||
{
|
||||
struct regmap *map = __map;
|
||||
|
||||
hwspin_lock_timeout_irq(map->hwlock, UINT_MAX);
|
||||
}
|
||||
|
||||
static void regmap_lock_hwlock_irqsave(void *__map)
|
||||
{
|
||||
struct regmap *map = __map;
|
||||
|
||||
hwspin_lock_timeout_irqsave(map->hwlock, UINT_MAX,
|
||||
&map->spinlock_flags);
|
||||
}
|
||||
|
||||
static void regmap_unlock_hwlock(void *__map)
|
||||
{
|
||||
struct regmap *map = __map;
|
||||
|
||||
hwspin_unlock(map->hwlock);
|
||||
}
|
||||
|
||||
static void regmap_unlock_hwlock_irq(void *__map)
|
||||
{
|
||||
struct regmap *map = __map;
|
||||
|
||||
hwspin_unlock_irq(map->hwlock);
|
||||
}
|
||||
|
||||
static void regmap_unlock_hwlock_irqrestore(void *__map)
|
||||
{
|
||||
struct regmap *map = __map;
|
||||
|
||||
hwspin_unlock_irqrestore(map->hwlock, &map->spinlock_flags);
|
||||
}
|
||||
|
||||
static void regmap_lock_mutex(void *__map)
|
||||
{
|
||||
struct regmap *map = __map;
|
||||
@@ -627,6 +671,29 @@ struct regmap *__regmap_init(struct device *dev,
|
||||
map->lock = config->lock;
|
||||
map->unlock = config->unlock;
|
||||
map->lock_arg = config->lock_arg;
|
||||
} else if (config->hwlock_id) {
|
||||
map->hwlock = hwspin_lock_request_specific(config->hwlock_id);
|
||||
if (!map->hwlock) {
|
||||
ret = -ENXIO;
|
||||
goto err_map;
|
||||
}
|
||||
|
||||
switch (config->hwlock_mode) {
|
||||
case HWLOCK_IRQSTATE:
|
||||
map->lock = regmap_lock_hwlock_irqsave;
|
||||
map->unlock = regmap_unlock_hwlock_irqrestore;
|
||||
break;
|
||||
case HWLOCK_IRQ:
|
||||
map->lock = regmap_lock_hwlock_irq;
|
||||
map->unlock = regmap_unlock_hwlock_irq;
|
||||
break;
|
||||
default:
|
||||
map->lock = regmap_lock_hwlock;
|
||||
map->unlock = regmap_unlock_hwlock;
|
||||
break;
|
||||
}
|
||||
|
||||
map->lock_arg = map;
|
||||
} else {
|
||||
if ((bus && bus->fast_io) ||
|
||||
config->fast_io) {
|
||||
@@ -729,7 +796,7 @@ struct regmap *__regmap_init(struct device *dev,
|
||||
map->format.format_write = regmap_format_2_6_write;
|
||||
break;
|
||||
default:
|
||||
goto err_map;
|
||||
goto err_hwlock;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -739,7 +806,7 @@ struct regmap *__regmap_init(struct device *dev,
|
||||
map->format.format_write = regmap_format_4_12_write;
|
||||
break;
|
||||
default:
|
||||
goto err_map;
|
||||
goto err_hwlock;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -749,7 +816,7 @@ struct regmap *__regmap_init(struct device *dev,
|
||||
map->format.format_write = regmap_format_7_9_write;
|
||||
break;
|
||||
default:
|
||||
goto err_map;
|
||||
goto err_hwlock;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -759,7 +826,7 @@ struct regmap *__regmap_init(struct device *dev,
|
||||
map->format.format_write = regmap_format_10_14_write;
|
||||
break;
|
||||
default:
|
||||
goto err_map;
|
||||
goto err_hwlock;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -779,13 +846,13 @@ struct regmap *__regmap_init(struct device *dev,
|
||||
map->format.format_reg = regmap_format_16_native;
|
||||
break;
|
||||
default:
|
||||
goto err_map;
|
||||
goto err_hwlock;
|
||||
}
|
||||
break;
|
||||
|
||||
case 24:
|
||||
if (reg_endian != REGMAP_ENDIAN_BIG)
|
||||
goto err_map;
|
||||
goto err_hwlock;
|
||||
map->format.format_reg = regmap_format_24;
|
||||
break;
|
||||
|
||||
@@ -801,7 +868,7 @@ struct regmap *__regmap_init(struct device *dev,
|
||||
map->format.format_reg = regmap_format_32_native;
|
||||
break;
|
||||
default:
|
||||
goto err_map;
|
||||
goto err_hwlock;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -818,13 +885,13 @@ struct regmap *__regmap_init(struct device *dev,
|
||||
map->format.format_reg = regmap_format_64_native;
|
||||
break;
|
||||
default:
|
||||
goto err_map;
|
||||
goto err_hwlock;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto err_map;
|
||||
goto err_hwlock;
|
||||
}
|
||||
|
||||
if (val_endian == REGMAP_ENDIAN_NATIVE)
|
||||
@@ -853,12 +920,12 @@ struct regmap *__regmap_init(struct device *dev,
|
||||
map->format.parse_val = regmap_parse_16_native;
|
||||
break;
|
||||
default:
|
||||
goto err_map;
|
||||
goto err_hwlock;
|
||||
}
|
||||
break;
|
||||
case 24:
|
||||
if (val_endian != REGMAP_ENDIAN_BIG)
|
||||
goto err_map;
|
||||
goto err_hwlock;
|
||||
map->format.format_val = regmap_format_24;
|
||||
map->format.parse_val = regmap_parse_24;
|
||||
break;
|
||||
@@ -879,7 +946,7 @@ struct regmap *__regmap_init(struct device *dev,
|
||||
map->format.parse_val = regmap_parse_32_native;
|
||||
break;
|
||||
default:
|
||||
goto err_map;
|
||||
goto err_hwlock;
|
||||
}
|
||||
break;
|
||||
#ifdef CONFIG_64BIT
|
||||
@@ -900,7 +967,7 @@ struct regmap *__regmap_init(struct device *dev,
|
||||
map->format.parse_val = regmap_parse_64_native;
|
||||
break;
|
||||
default:
|
||||
goto err_map;
|
||||
goto err_hwlock;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
@@ -909,18 +976,18 @@ struct regmap *__regmap_init(struct device *dev,
|
||||
if (map->format.format_write) {
|
||||
if ((reg_endian != REGMAP_ENDIAN_BIG) ||
|
||||
(val_endian != REGMAP_ENDIAN_BIG))
|
||||
goto err_map;
|
||||
goto err_hwlock;
|
||||
map->use_single_write = true;
|
||||
}
|
||||
|
||||
if (!map->format.format_write &&
|
||||
!(map->format.format_reg && map->format.format_val))
|
||||
goto err_map;
|
||||
goto err_hwlock;
|
||||
|
||||
map->work_buf = kzalloc(map->format.buf_size, GFP_KERNEL);
|
||||
if (map->work_buf == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_map;
|
||||
goto err_hwlock;
|
||||
}
|
||||
|
||||
if (map->format.format_write) {
|
||||
@@ -1041,6 +1108,8 @@ err_regcache:
|
||||
err_range:
|
||||
regmap_range_exit(map);
|
||||
kfree(map->work_buf);
|
||||
err_hwlock:
|
||||
hwspin_lock_free(map->hwlock);
|
||||
err_map:
|
||||
kfree(map);
|
||||
err:
|
||||
|
||||
Reference in New Issue
Block a user