mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
regmap: mmio: Support accelerared noinc operations
Use the newly added callback for accelerated noinc MMIO to provide writesb, writesw, writesl, writesq, readsb, readsw, readsl and readsq. A special quirk is needed to deal with big endian regmaps: there are no accelerated operations defined for big endian, so fall back to calling the big endian operations itereatively for this case. The Hexagon architecture turns out to have an incomplete <asm/io.h>: writesb() is not implemented. Fix this by doing what other architectures do: include <asm-generic/io.h> into the <asm/io.h> file. Cc: Brian Cain <bcain@quicinc.com> Cc: linux-hexagon@vger.kernel.org Cc: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Link: https://lore.kernel.org/r/20220816204832.265837-2-linus.walleij@linaro.org Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
c20cc099b3
commit
81c0386c13
@ -308,6 +308,8 @@ static inline void outsl(unsigned long port, const void *buffer, int count)
|
||||
}
|
||||
}
|
||||
|
||||
#include <asm-generic/io.h>
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif
|
||||
|
@ -16,6 +16,7 @@
|
||||
struct regmap_mmio_context {
|
||||
void __iomem *regs;
|
||||
unsigned int val_bytes;
|
||||
bool big_endian;
|
||||
|
||||
bool attached_clk;
|
||||
struct clk *clk;
|
||||
@ -165,6 +166,83 @@ static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int regmap_mmio_noinc_write(void *context, unsigned int reg,
|
||||
const void *val, size_t val_count)
|
||||
{
|
||||
struct regmap_mmio_context *ctx = context;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
if (!IS_ERR(ctx->clk)) {
|
||||
ret = clk_enable(ctx->clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* There are no native, assembly-optimized write single register
|
||||
* operations for big endian, so fall back to emulation if this
|
||||
* is needed. (Single bytes are fine, they are not affected by
|
||||
* endianness.)
|
||||
*/
|
||||
if (ctx->big_endian && (ctx->val_bytes > 1)) {
|
||||
switch (ctx->val_bytes) {
|
||||
case 2:
|
||||
{
|
||||
const u16 *valp = (const u16 *)val;
|
||||
for (i = 0; i < val_count; i++)
|
||||
writew(swab16(valp[i]), ctx->regs + reg);
|
||||
goto out_clk;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
const u32 *valp = (const u32 *)val;
|
||||
for (i = 0; i < val_count; i++)
|
||||
writel(swab32(valp[i]), ctx->regs + reg);
|
||||
goto out_clk;
|
||||
}
|
||||
#ifdef CONFIG_64BIT
|
||||
case 8:
|
||||
{
|
||||
const u64 *valp = (const u64 *)val;
|
||||
for (i = 0; i < val_count; i++)
|
||||
writeq(swab64(valp[i]), ctx->regs + reg);
|
||||
goto out_clk;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out_clk;
|
||||
}
|
||||
}
|
||||
|
||||
switch (ctx->val_bytes) {
|
||||
case 1:
|
||||
writesb(ctx->regs + reg, (const u8 *)val, val_count);
|
||||
break;
|
||||
case 2:
|
||||
writesw(ctx->regs + reg, (const u16 *)val, val_count);
|
||||
break;
|
||||
case 4:
|
||||
writesl(ctx->regs + reg, (const u32 *)val, val_count);
|
||||
break;
|
||||
#ifdef CONFIG_64BIT
|
||||
case 8:
|
||||
writesq(ctx->regs + reg, (const u64 *)val, val_count);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
out_clk:
|
||||
if (!IS_ERR(ctx->clk))
|
||||
clk_disable(ctx->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx,
|
||||
unsigned int reg)
|
||||
{
|
||||
@ -262,6 +340,87 @@ static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int regmap_mmio_noinc_read(void *context, unsigned int reg,
|
||||
void *val, size_t val_count)
|
||||
{
|
||||
struct regmap_mmio_context *ctx = context;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
if (!IS_ERR(ctx->clk)) {
|
||||
ret = clk_enable(ctx->clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (ctx->val_bytes) {
|
||||
case 1:
|
||||
readsb(ctx->regs + reg, (u8 *)val, val_count);
|
||||
break;
|
||||
case 2:
|
||||
readsw(ctx->regs + reg, (u16 *)val, val_count);
|
||||
break;
|
||||
case 4:
|
||||
readsl(ctx->regs + reg, (u32 *)val, val_count);
|
||||
break;
|
||||
#ifdef CONFIG_64BIT
|
||||
case 8:
|
||||
readsq(ctx->regs + reg, (u64 *)val, val_count);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out_clk;
|
||||
}
|
||||
|
||||
/*
|
||||
* There are no native, assembly-optimized write single register
|
||||
* operations for big endian, so fall back to emulation if this
|
||||
* is needed. (Single bytes are fine, they are not affected by
|
||||
* endianness.)
|
||||
*/
|
||||
if (ctx->big_endian && (ctx->val_bytes > 1)) {
|
||||
switch (ctx->val_bytes) {
|
||||
case 2:
|
||||
{
|
||||
u16 *valp = (u16 *)val;
|
||||
for (i = 0; i < val_count; i++)
|
||||
valp[i] = swab16(valp[i]);
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
u32 *valp = (u32 *)val;
|
||||
for (i = 0; i < val_count; i++)
|
||||
valp[i] = swab32(valp[i]);
|
||||
break;
|
||||
}
|
||||
#ifdef CONFIG_64BIT
|
||||
case 8:
|
||||
{
|
||||
u64 *valp = (u64 *)val;
|
||||
for (i = 0; i < val_count; i++)
|
||||
valp[i] = swab64(valp[i]);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
out_clk:
|
||||
if (!IS_ERR(ctx->clk))
|
||||
clk_disable(ctx->clk);
|
||||
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void regmap_mmio_free_context(void *context)
|
||||
{
|
||||
struct regmap_mmio_context *ctx = context;
|
||||
@ -278,6 +437,8 @@ static const struct regmap_bus regmap_mmio = {
|
||||
.fast_io = true,
|
||||
.reg_write = regmap_mmio_write,
|
||||
.reg_read = regmap_mmio_read,
|
||||
.reg_noinc_write = regmap_mmio_noinc_write,
|
||||
.reg_noinc_read = regmap_mmio_noinc_read,
|
||||
.free_context = regmap_mmio_free_context,
|
||||
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
|
||||
};
|
||||
@ -368,6 +529,7 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
|
||||
#ifdef __BIG_ENDIAN
|
||||
case REGMAP_ENDIAN_NATIVE:
|
||||
#endif
|
||||
ctx->big_endian = true;
|
||||
switch (config->val_bits) {
|
||||
case 8:
|
||||
if (config->io_port) {
|
||||
|
Loading…
Reference in New Issue
Block a user