Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.10 366 lines 7.9 kB view raw
1/* 2 * Register map access API - MMIO support 3 * 4 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms and conditions of the GNU General Public License, 8 * version 2, as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 * more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19#include <linux/clk.h> 20#include <linux/err.h> 21#include <linux/io.h> 22#include <linux/module.h> 23#include <linux/regmap.h> 24#include <linux/slab.h> 25 26#include "internal.h" 27 28struct regmap_mmio_context { 29 void __iomem *regs; 30 unsigned val_bytes; 31 struct clk *clk; 32 33 void (*reg_write)(struct regmap_mmio_context *ctx, 34 unsigned int reg, unsigned int val); 35 unsigned int (*reg_read)(struct regmap_mmio_context *ctx, 36 unsigned int reg); 37}; 38 39static int regmap_mmio_regbits_check(size_t reg_bits) 40{ 41 switch (reg_bits) { 42 case 8: 43 case 16: 44 case 32: 45#ifdef CONFIG_64BIT 46 case 64: 47#endif 48 return 0; 49 default: 50 return -EINVAL; 51 } 52} 53 54static int regmap_mmio_get_min_stride(size_t val_bits) 55{ 56 int min_stride; 57 58 switch (val_bits) { 59 case 8: 60 /* The core treats 0 as 1 */ 61 min_stride = 0; 62 return 0; 63 case 16: 64 min_stride = 2; 65 break; 66 case 32: 67 min_stride = 4; 68 break; 69#ifdef CONFIG_64BIT 70 case 64: 71 min_stride = 8; 72 break; 73#endif 74 default: 75 return -EINVAL; 76 } 77 78 return min_stride; 79} 80 81static void regmap_mmio_write8(struct regmap_mmio_context *ctx, 82 unsigned int reg, 83 unsigned int val) 84{ 85 writeb(val, ctx->regs + reg); 86} 87 88static void regmap_mmio_write16le(struct regmap_mmio_context *ctx, 89 unsigned int reg, 90 unsigned int val) 91{ 92 writew(val, ctx->regs + reg); 93} 94 95static void regmap_mmio_write16be(struct regmap_mmio_context *ctx, 96 unsigned int reg, 97 unsigned int val) 98{ 99 iowrite16be(val, ctx->regs + reg); 100} 101 102static void regmap_mmio_write32le(struct regmap_mmio_context *ctx, 103 unsigned int reg, 104 unsigned int val) 105{ 106 writel(val, ctx->regs + reg); 107} 108 109static void regmap_mmio_write32be(struct regmap_mmio_context *ctx, 110 unsigned int reg, 111 unsigned int val) 112{ 113 iowrite32be(val, ctx->regs + reg); 114} 115 116#ifdef CONFIG_64BIT 117static void regmap_mmio_write64le(struct regmap_mmio_context *ctx, 118 unsigned int reg, 119 unsigned int val) 120{ 121 writeq(val, ctx->regs + reg); 122} 123#endif 124 125static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val) 126{ 127 struct regmap_mmio_context *ctx = context; 128 int ret; 129 130 if (!IS_ERR(ctx->clk)) { 131 ret = clk_enable(ctx->clk); 132 if (ret < 0) 133 return ret; 134 } 135 136 ctx->reg_write(ctx, reg, val); 137 138 if (!IS_ERR(ctx->clk)) 139 clk_disable(ctx->clk); 140 141 return 0; 142} 143 144static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx, 145 unsigned int reg) 146{ 147 return readb(ctx->regs + reg); 148} 149 150static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx, 151 unsigned int reg) 152{ 153 return readw(ctx->regs + reg); 154} 155 156static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx, 157 unsigned int reg) 158{ 159 return ioread16be(ctx->regs + reg); 160} 161 162static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx, 163 unsigned int reg) 164{ 165 return readl(ctx->regs + reg); 166} 167 168static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx, 169 unsigned int reg) 170{ 171 return ioread32be(ctx->regs + reg); 172} 173 174#ifdef CONFIG_64BIT 175static unsigned int regmap_mmio_read64le(struct regmap_mmio_context *ctx, 176 unsigned int reg) 177{ 178 return readq(ctx->regs + reg); 179} 180#endif 181 182static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val) 183{ 184 struct regmap_mmio_context *ctx = context; 185 int ret; 186 187 if (!IS_ERR(ctx->clk)) { 188 ret = clk_enable(ctx->clk); 189 if (ret < 0) 190 return ret; 191 } 192 193 *val = ctx->reg_read(ctx, reg); 194 195 if (!IS_ERR(ctx->clk)) 196 clk_disable(ctx->clk); 197 198 return 0; 199} 200 201static void regmap_mmio_free_context(void *context) 202{ 203 struct regmap_mmio_context *ctx = context; 204 205 if (!IS_ERR(ctx->clk)) { 206 clk_unprepare(ctx->clk); 207 clk_put(ctx->clk); 208 } 209 kfree(context); 210} 211 212static const struct regmap_bus regmap_mmio = { 213 .fast_io = true, 214 .reg_write = regmap_mmio_write, 215 .reg_read = regmap_mmio_read, 216 .free_context = regmap_mmio_free_context, 217 .val_format_endian_default = REGMAP_ENDIAN_LITTLE, 218}; 219 220static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, 221 const char *clk_id, 222 void __iomem *regs, 223 const struct regmap_config *config) 224{ 225 struct regmap_mmio_context *ctx; 226 int min_stride; 227 int ret; 228 229 ret = regmap_mmio_regbits_check(config->reg_bits); 230 if (ret) 231 return ERR_PTR(ret); 232 233 if (config->pad_bits) 234 return ERR_PTR(-EINVAL); 235 236 min_stride = regmap_mmio_get_min_stride(config->val_bits); 237 if (min_stride < 0) 238 return ERR_PTR(min_stride); 239 240 if (config->reg_stride < min_stride) 241 return ERR_PTR(-EINVAL); 242 243 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 244 if (!ctx) 245 return ERR_PTR(-ENOMEM); 246 247 ctx->regs = regs; 248 ctx->val_bytes = config->val_bits / 8; 249 ctx->clk = ERR_PTR(-ENODEV); 250 251 switch (regmap_get_val_endian(dev, &regmap_mmio, config)) { 252 case REGMAP_ENDIAN_DEFAULT: 253 case REGMAP_ENDIAN_LITTLE: 254#ifdef __LITTLE_ENDIAN 255 case REGMAP_ENDIAN_NATIVE: 256#endif 257 switch (config->val_bits) { 258 case 8: 259 ctx->reg_read = regmap_mmio_read8; 260 ctx->reg_write = regmap_mmio_write8; 261 break; 262 case 16: 263 ctx->reg_read = regmap_mmio_read16le; 264 ctx->reg_write = regmap_mmio_write16le; 265 break; 266 case 32: 267 ctx->reg_read = regmap_mmio_read32le; 268 ctx->reg_write = regmap_mmio_write32le; 269 break; 270#ifdef CONFIG_64BIT 271 case 64: 272 ctx->reg_read = regmap_mmio_read64le; 273 ctx->reg_write = regmap_mmio_write64le; 274 break; 275#endif 276 default: 277 ret = -EINVAL; 278 goto err_free; 279 } 280 break; 281 case REGMAP_ENDIAN_BIG: 282#ifdef __BIG_ENDIAN 283 case REGMAP_ENDIAN_NATIVE: 284#endif 285 switch (config->val_bits) { 286 case 8: 287 ctx->reg_read = regmap_mmio_read8; 288 ctx->reg_write = regmap_mmio_write8; 289 break; 290 case 16: 291 ctx->reg_read = regmap_mmio_read16be; 292 ctx->reg_write = regmap_mmio_write16be; 293 break; 294 case 32: 295 ctx->reg_read = regmap_mmio_read32be; 296 ctx->reg_write = regmap_mmio_write32be; 297 break; 298 default: 299 ret = -EINVAL; 300 goto err_free; 301 } 302 break; 303 default: 304 ret = -EINVAL; 305 goto err_free; 306 } 307 308 if (clk_id == NULL) 309 return ctx; 310 311 ctx->clk = clk_get(dev, clk_id); 312 if (IS_ERR(ctx->clk)) { 313 ret = PTR_ERR(ctx->clk); 314 goto err_free; 315 } 316 317 ret = clk_prepare(ctx->clk); 318 if (ret < 0) { 319 clk_put(ctx->clk); 320 goto err_free; 321 } 322 323 return ctx; 324 325err_free: 326 kfree(ctx); 327 328 return ERR_PTR(ret); 329} 330 331struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id, 332 void __iomem *regs, 333 const struct regmap_config *config, 334 struct lock_class_key *lock_key, 335 const char *lock_name) 336{ 337 struct regmap_mmio_context *ctx; 338 339 ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); 340 if (IS_ERR(ctx)) 341 return ERR_CAST(ctx); 342 343 return __regmap_init(dev, &regmap_mmio, ctx, config, 344 lock_key, lock_name); 345} 346EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk); 347 348struct regmap *__devm_regmap_init_mmio_clk(struct device *dev, 349 const char *clk_id, 350 void __iomem *regs, 351 const struct regmap_config *config, 352 struct lock_class_key *lock_key, 353 const char *lock_name) 354{ 355 struct regmap_mmio_context *ctx; 356 357 ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); 358 if (IS_ERR(ctx)) 359 return ERR_CAST(ctx); 360 361 return __devm_regmap_init(dev, &regmap_mmio, ctx, config, 362 lock_key, lock_name); 363} 364EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk); 365 366MODULE_LICENSE("GPL v2");