diff options
| author | Philipp Zabel <p.zabel@pengutronix.de> | 2013-02-14 11:39:08 -0500 |
|---|---|---|
| committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2013-02-14 11:40:28 -0500 |
| commit | 878ec67b3ac528a2b6d44055f049cdbb9cfda30c (patch) | |
| tree | e6907b938e9c77c84aed65f7a6f8b2d72f4eb0fd | |
| parent | 836dc9e3fbbab0c30aa6e664417225f5c1fb1c39 (diff) | |
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: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
| -rw-r--r-- | drivers/base/regmap/regmap-mmio.c | 79 | ||||
| -rw-r--r-- | include/linux/regmap.h | 47 |
2 files changed, 107 insertions, 19 deletions
diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index f05fc74dd84a..98745dd77e8c 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 17 | */ | 17 | */ |
| 18 | 18 | ||
| 19 | #include <linux/clk.h> | ||
| 19 | #include <linux/err.h> | 20 | #include <linux/err.h> |
| 20 | #include <linux/init.h> | 21 | #include <linux/init.h> |
| 21 | #include <linux/io.h> | 22 | #include <linux/io.h> |
| @@ -26,6 +27,7 @@ | |||
| 26 | struct regmap_mmio_context { | 27 | struct regmap_mmio_context { |
| 27 | void __iomem *regs; | 28 | void __iomem *regs; |
| 28 | unsigned val_bytes; | 29 | unsigned val_bytes; |
| 30 | struct clk *clk; | ||
| 29 | }; | 31 | }; |
| 30 | 32 | ||
| 31 | static int regmap_mmio_gather_write(void *context, | 33 | static int regmap_mmio_gather_write(void *context, |
| @@ -34,9 +36,16 @@ static int regmap_mmio_gather_write(void *context, | |||
| 34 | { | 36 | { |
| 35 | struct regmap_mmio_context *ctx = context; | 37 | struct regmap_mmio_context *ctx = context; |
| 36 | u32 offset; | 38 | u32 offset; |
| 39 | int ret; | ||
| 37 | 40 | ||
| 38 | BUG_ON(reg_size != 4); | 41 | BUG_ON(reg_size != 4); |
| 39 | 42 | ||
| 43 | if (ctx->clk) { | ||
| 44 | ret = clk_enable(ctx->clk); | ||
| 45 | if (ret < 0) | ||
| 46 | return ret; | ||
| 47 | } | ||
| 48 | |||
| 40 | offset = *(u32 *)reg; | 49 | offset = *(u32 *)reg; |
| 41 | 50 | ||
| 42 | while (val_size) { | 51 | while (val_size) { |
| @@ -64,6 +73,9 @@ static int regmap_mmio_gather_write(void *context, | |||
| 64 | offset += ctx->val_bytes; | 73 | offset += ctx->val_bytes; |
| 65 | } | 74 | } |
| 66 | 75 | ||
| 76 | if (ctx->clk) | ||
| 77 | clk_disable(ctx->clk); | ||
| 78 | |||
| 67 | return 0; | 79 | return 0; |
| 68 | } | 80 | } |
| 69 | 81 | ||
| @@ -80,9 +92,16 @@ static int regmap_mmio_read(void *context, | |||
| 80 | { | 92 | { |
| 81 | struct regmap_mmio_context *ctx = context; | 93 | struct regmap_mmio_context *ctx = context; |
| 82 | u32 offset; | 94 | u32 offset; |
| 95 | int ret; | ||
| 83 | 96 | ||
| 84 | BUG_ON(reg_size != 4); | 97 | BUG_ON(reg_size != 4); |
| 85 | 98 | ||
| 99 | if (ctx->clk) { | ||
| 100 | ret = clk_enable(ctx->clk); | ||
| 101 | if (ret < 0) | ||
| 102 | return ret; | ||
| 103 | } | ||
| 104 | |||
| 86 | offset = *(u32 *)reg; | 105 | offset = *(u32 *)reg; |
| 87 | 106 | ||
| 88 | while (val_size) { | 107 | while (val_size) { |
| @@ -110,11 +129,20 @@ static int regmap_mmio_read(void *context, | |||
| 110 | offset += ctx->val_bytes; | 129 | offset += ctx->val_bytes; |
| 111 | } | 130 | } |
| 112 | 131 | ||
| 132 | if (ctx->clk) | ||
| 133 | clk_disable(ctx->clk); | ||
| 134 | |||
| 113 | return 0; | 135 | return 0; |
| 114 | } | 136 | } |
| 115 | 137 | ||
| 116 | static void regmap_mmio_free_context(void *context) | 138 | static void regmap_mmio_free_context(void *context) |
| 117 | { | 139 | { |
| 140 | struct regmap_mmio_context *ctx = context; | ||
| 141 | |||
| 142 | if (ctx->clk) { | ||
| 143 | clk_unprepare(ctx->clk); | ||
| 144 | clk_put(ctx->clk); | ||
| 145 | } | ||
| 118 | kfree(context); | 146 | kfree(context); |
| 119 | } | 147 | } |
| 120 | 148 | ||
| @@ -128,11 +156,14 @@ static struct regmap_bus regmap_mmio = { | |||
| 128 | .val_format_endian_default = REGMAP_ENDIAN_NATIVE, | 156 | .val_format_endian_default = REGMAP_ENDIAN_NATIVE, |
| 129 | }; | 157 | }; |
| 130 | 158 | ||
| 131 | static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, | 159 | static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, |
| 160 | const char *clk_id, | ||
| 161 | void __iomem *regs, | ||
| 132 | const struct regmap_config *config) | 162 | const struct regmap_config *config) |
| 133 | { | 163 | { |
| 134 | struct regmap_mmio_context *ctx; | 164 | struct regmap_mmio_context *ctx; |
| 135 | int min_stride; | 165 | int min_stride; |
| 166 | int ret; | ||
| 136 | 167 | ||
| 137 | if (config->reg_bits != 32) | 168 | if (config->reg_bits != 32) |
| 138 | return ERR_PTR(-EINVAL); | 169 | return ERR_PTR(-EINVAL); |
| @@ -179,37 +210,59 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, | |||
| 179 | ctx->regs = regs; | 210 | ctx->regs = regs; |
| 180 | ctx->val_bytes = config->val_bits / 8; | 211 | ctx->val_bytes = config->val_bits / 8; |
| 181 | 212 | ||
| 213 | if (clk_id == NULL) | ||
| 214 | return ctx; | ||
| 215 | |||
| 216 | ctx->clk = clk_get(dev, clk_id); | ||
| 217 | if (IS_ERR(ctx->clk)) { | ||
| 218 | ret = PTR_ERR(ctx->clk); | ||
| 219 | goto err_free; | ||
| 220 | } | ||
| 221 | |||
| 222 | ret = clk_prepare(ctx->clk); | ||
| 223 | if (ret < 0) { | ||
| 224 | clk_put(ctx->clk); | ||
| 225 | goto err_free; | ||
| 226 | } | ||
| 227 | |||
| 182 | return ctx; | 228 | return ctx; |
| 229 | |||
| 230 | err_free: | ||
| 231 | kfree(ctx); | ||
| 232 | |||
| 233 | return ERR_PTR(ret); | ||
| 183 | } | 234 | } |
| 184 | 235 | ||
| 185 | /** | 236 | /** |
| 186 | * regmap_init_mmio(): Initialise register map | 237 | * regmap_init_mmio_clk(): Initialise register map with register clock |
| 187 | * | 238 | * |
| 188 | * @dev: Device that will be interacted with | 239 | * @dev: Device that will be interacted with |
| 240 | * @clk_id: register clock consumer ID | ||
| 189 | * @regs: Pointer to memory-mapped IO region | 241 | * @regs: Pointer to memory-mapped IO region |
| 190 | * @config: Configuration for register map | 242 | * @config: Configuration for register map |
| 191 | * | 243 | * |
| 192 | * The return value will be an ERR_PTR() on error or a valid pointer to | 244 | * The return value will be an ERR_PTR() on error or a valid pointer to |
| 193 | * a struct regmap. | 245 | * a struct regmap. |
| 194 | */ | 246 | */ |
| 195 | struct regmap *regmap_init_mmio(struct device *dev, | 247 | struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id, |
| 196 | void __iomem *regs, | 248 | void __iomem *regs, |
| 197 | const struct regmap_config *config) | 249 | const struct regmap_config *config) |
| 198 | { | 250 | { |
| 199 | struct regmap_mmio_context *ctx; | 251 | struct regmap_mmio_context *ctx; |
| 200 | 252 | ||
| 201 | ctx = regmap_mmio_gen_context(regs, config); | 253 | ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); |
| 202 | if (IS_ERR(ctx)) | 254 | if (IS_ERR(ctx)) |
| 203 | return ERR_CAST(ctx); | 255 | return ERR_CAST(ctx); |
| 204 | 256 | ||
| 205 | return regmap_init(dev, ®map_mmio, ctx, config); | 257 | return regmap_init(dev, ®map_mmio, ctx, config); |
| 206 | } | 258 | } |
| 207 | EXPORT_SYMBOL_GPL(regmap_init_mmio); | 259 | EXPORT_SYMBOL_GPL(regmap_init_mmio_clk); |
| 208 | 260 | ||
| 209 | /** | 261 | /** |
| 210 | * devm_regmap_init_mmio(): Initialise managed register map | ||
