Commit 878ec67b authored by Philipp Zabel's avatar Philipp Zabel Committed by Mark Brown

regmap: mmio: add register clock support

Some mmio devices have a dedicated interface clock that needs
to be enabled to access their registers. This patch optionally
enables a clock before accessing registers in the regmap_bus
callbacks.

I added (devm_)regmap_init_mmio_clk variants of the init
functions that have an added clk_id string parameter. This
is passed to clk_get to request the clock from the clk
framework.
Signed-off-by: default avatarPhilipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent 836dc9e3
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
...@@ -26,6 +27,7 @@ ...@@ -26,6 +27,7 @@
struct regmap_mmio_context { struct regmap_mmio_context {
void __iomem *regs; void __iomem *regs;
unsigned val_bytes; unsigned val_bytes;
struct clk *clk;
}; };
static int regmap_mmio_gather_write(void *context, static int regmap_mmio_gather_write(void *context,
...@@ -34,9 +36,16 @@ static int regmap_mmio_gather_write(void *context, ...@@ -34,9 +36,16 @@ static int regmap_mmio_gather_write(void *context,
{ {
struct regmap_mmio_context *ctx = context; struct regmap_mmio_context *ctx = context;
u32 offset; u32 offset;
int ret;
BUG_ON(reg_size != 4); BUG_ON(reg_size != 4);
if (ctx->clk) {
ret = clk_enable(ctx->clk);
if (ret < 0)
return ret;
}
offset = *(u32 *)reg; offset = *(u32 *)reg;
while (val_size) { while (val_size) {
...@@ -64,6 +73,9 @@ static int regmap_mmio_gather_write(void *context, ...@@ -64,6 +73,9 @@ static int regmap_mmio_gather_write(void *context,
offset += ctx->val_bytes; offset += ctx->val_bytes;
} }
if (ctx->clk)
clk_disable(ctx->clk);
return 0; return 0;
} }
...@@ -80,9 +92,16 @@ static int regmap_mmio_read(void *context, ...@@ -80,9 +92,16 @@ static int regmap_mmio_read(void *context,
{ {
struct regmap_mmio_context *ctx = context; struct regmap_mmio_context *ctx = context;
u32 offset; u32 offset;
int ret;
BUG_ON(reg_size != 4); BUG_ON(reg_size != 4);
if (ctx->clk) {
ret = clk_enable(ctx->clk);
if (ret < 0)
return ret;
}
offset = *(u32 *)reg; offset = *(u32 *)reg;
while (val_size) { while (val_size) {
...@@ -110,11 +129,20 @@ static int regmap_mmio_read(void *context, ...@@ -110,11 +129,20 @@ static int regmap_mmio_read(void *context,
offset += ctx->val_bytes; offset += ctx->val_bytes;
} }
if (ctx->clk)
clk_disable(ctx->clk);
return 0; return 0;
} }
static void regmap_mmio_free_context(void *context) static void regmap_mmio_free_context(void *context)
{ {
struct regmap_mmio_context *ctx = context;
if (ctx->clk) {
clk_unprepare(ctx->clk);
clk_put(ctx->clk);
}
kfree(context); kfree(context);
} }
...@@ -128,11 +156,14 @@ static struct regmap_bus regmap_mmio = { ...@@ -128,11 +156,14 @@ static struct regmap_bus regmap_mmio = {
.val_format_endian_default = REGMAP_ENDIAN_NATIVE, .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
}; };
static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
const char *clk_id,
void __iomem *regs,
const struct regmap_config *config) const struct regmap_config *config)
{ {
struct regmap_mmio_context *ctx; struct regmap_mmio_context *ctx;
int min_stride; int min_stride;
int ret;
if (config->reg_bits != 32) if (config->reg_bits != 32)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
...@@ -179,37 +210,59 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, ...@@ -179,37 +210,59 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs,
ctx->regs = regs; ctx->regs = regs;
ctx->val_bytes = config->val_bits / 8; ctx->val_bytes = config->val_bits / 8;
if (clk_id == NULL)
return ctx; return ctx;
ctx->clk = clk_get(dev, clk_id);
if (IS_ERR(ctx->clk)) {
ret = PTR_ERR(ctx->clk);
goto err_free;
}
ret = clk_prepare(ctx->clk);
if (ret < 0) {
clk_put(ctx->clk);
goto err_free;
}
return ctx;
err_free:
kfree(ctx);
return ERR_PTR(ret);
} }
/** /**
* regmap_init_mmio(): Initialise register map * regmap_init_mmio_clk(): Initialise register map with register clock
* *
* @dev: Device that will be interacted with * @dev: Device that will be interacted with
* @clk_id: register clock consumer ID
* @regs: Pointer to memory-mapped IO region * @regs: Pointer to memory-mapped IO region
* @config: Configuration for register map * @config: Configuration for register map
* *
* The return value will be an ERR_PTR() on error or a valid pointer to * The return value will be an ERR_PTR() on error or a valid pointer to
* a struct regmap. * a struct regmap.
*/ */
struct regmap *regmap_init_mmio(struct device *dev, struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs, void __iomem *regs,
const struct regmap_config *config) const struct regmap_config *config)
{ {
struct regmap_mmio_context *ctx; struct regmap_mmio_context *ctx;
ctx = regmap_mmio_gen_context(regs, config); ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
if (IS_ERR(ctx)) if (IS_ERR(ctx))
return ERR_CAST(ctx); return ERR_CAST(ctx);
return regmap_init(dev, &regmap_mmio, ctx, config); return regmap_init(dev, &regmap_mmio, ctx, config);
} }
EXPORT_SYMBOL_GPL(regmap_init_mmio); EXPORT_SYMBOL_GPL(regmap_init_mmio_clk);
/** /**
* devm_regmap_init_mmio(): Initialise managed register map * devm_regmap_init_mmio_clk(): Initialise managed register map with clock
* *
* @dev: Device that will be interacted with * @dev: Device that will be interacted with
* @clk_id: register clock consumer ID
* @regs: Pointer to memory-mapped IO region * @regs: Pointer to memory-mapped IO region
* @config: Configuration for register map * @config: Configuration for register map
* *
...@@ -217,18 +270,18 @@ EXPORT_SYMBOL_GPL(regmap_init_mmio); ...@@ -217,18 +270,18 @@ EXPORT_SYMBOL_GPL(regmap_init_mmio);
* to a struct regmap. The regmap will be automatically freed by the * to a struct regmap. The regmap will be automatically freed by the
* device management code. * device management code.
*/ */
struct regmap *devm_regmap_init_mmio(struct device *dev, struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs, void __iomem *regs,
const struct regmap_config *config) const struct regmap_config *config)
{ {
struct regmap_mmio_context *ctx; struct regmap_mmio_context *ctx;
ctx = regmap_mmio_gen_context(regs, config); ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
if (IS_ERR(ctx)) if (IS_ERR(ctx))
return ERR_CAST(ctx); return ERR_CAST(ctx);
return devm_regmap_init(dev, &regmap_mmio, ctx, config); return devm_regmap_init(dev, &regmap_mmio, ctx, config);
} }
EXPORT_SYMBOL_GPL(devm_regmap_init_mmio); EXPORT_SYMBOL_GPL(devm_regmap_init_mmio_clk);
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
...@@ -285,7 +285,7 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c, ...@@ -285,7 +285,7 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config); const struct regmap_config *config);
struct regmap *regmap_init_spi(struct spi_device *dev, struct regmap *regmap_init_spi(struct spi_device *dev,
const struct regmap_config *config); const struct regmap_config *config);
struct regmap *regmap_init_mmio(struct device *dev, struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs, void __iomem *regs,
const struct regmap_config *config); const struct regmap_config *config);
...@@ -297,10 +297,45 @@ struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, ...@@ -297,10 +297,45 @@ struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config); const struct regmap_config *config);
struct regmap *devm_regmap_init_spi(struct spi_device *dev, struct regmap *devm_regmap_init_spi(struct spi_device *dev,
const struct regmap_config *config); const struct regmap_config *config);
struct regmap *devm_regmap_init_mmio(struct device *dev, struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs, void __iomem *regs,
const struct regmap_config *config); const struct regmap_config *config);
/**
* regmap_init_mmio(): Initialise register map
*
* @dev: Device that will be interacted with
* @regs: Pointer to memory-mapped IO region
* @config: Configuration for register map
*
* The return value will be an ERR_PTR() on error or a valid pointer to
* a struct regmap.
*/
static inline struct regmap *regmap_init_mmio(struct device *dev,
void __iomem *regs,
const struct regmap_config *config)
{
return regmap_init_mmio_clk(dev, NULL, regs, config);
}
/**
* devm_regmap_init_mmio(): Initialise managed register map
*
* @dev: Device that will be interacted with
* @regs: Pointer to memory-mapped IO region
* @config: Configuration for register map
*
* The return value will be an ERR_PTR() on error or a valid pointer
* to a struct regmap. The regmap will be automatically freed by the
* device management code.
*/
static inline struct regmap *devm_regmap_init_mmio(struct device *dev,
void __iomem *regs,
const struct regmap_config *config)
{
return devm_regmap_init_mmio_clk(dev, NULL, regs, config);
}
void regmap_exit(struct regmap *map); void regmap_exit(struct regmap *map);
int regmap_reinit_cache(struct regmap *map, int regmap_reinit_cache(struct regmap *map,
const struct regmap_config *config); const struct regmap_config *config);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment