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 struct regmap_mmio_context { 27 void __iomem *regs; 28 unsigned reg_bytes; 29 unsigned val_bytes; 30 unsigned pad_bytes; 31 struct clk *clk; 32 }; 33 34 static inline void regmap_mmio_regsize_check(size_t reg_size) 35 { 36 switch (reg_size) { 37 case 1: 38 case 2: 39 case 4: 40 #ifdef CONFIG_64BIT 41 case 8: 42 #endif 43 break; 44 default: 45 BUG(); 46 } 47 } 48 49 static int regmap_mmio_regbits_check(size_t reg_bits) 50 { 51 switch (reg_bits) { 52 case 8: 53 case 16: 54 case 32: 55 #ifdef CONFIG_64BIT 56 case 64: 57 #endif 58 return 0; 59 default: 60 return -EINVAL; 61 } 62 } 63 64 static int regmap_mmio_get_min_stride(size_t val_bits) 65 { 66 int min_stride; 67 68 switch (val_bits) { 69 case 8: 70 /* The core treats 0 as 1 */ 71 min_stride = 0; 72 return 0; 73 case 16: 74 min_stride = 2; 75 break; 76 case 32: 77 min_stride = 4; 78 break; 79 #ifdef CONFIG_64BIT 80 case 64: 81 min_stride = 8; 82 break; 83 #endif 84 default: 85 return -EINVAL; 86 } 87 88 return min_stride; 89 } 90 91 static inline void regmap_mmio_count_check(size_t count, u32 offset) 92 { 93 BUG_ON(count <= offset); 94 } 95 96 static inline unsigned int 97 regmap_mmio_get_offset(const void *reg, size_t reg_size) 98 { 99 switch (reg_size) { 100 case 1: 101 return *(u8 *)reg; 102 case 2: 103 return *(u16 *)reg; 104 case 4: 105 return *(u32 *)reg; 106 #ifdef CONFIG_64BIT 107 case 8: 108 return *(u64 *)reg; 109 #endif 110 default: 111 BUG(); 112 } 113 } 114 115 static int regmap_mmio_gather_write(void *context, 116 const void *reg, size_t reg_size, 117 const void *val, size_t val_size) 118 { 119 struct regmap_mmio_context *ctx = context; 120 unsigned int offset; 121 int ret; 122 123 regmap_mmio_regsize_check(reg_size); 124 125 if (!IS_ERR(ctx->clk)) { 126 ret = clk_enable(ctx->clk); 127 if (ret < 0) 128 return ret; 129 } 130 131 offset = regmap_mmio_get_offset(reg, reg_size); 132 133 while (val_size) { 134 switch (ctx->val_bytes) { 135 case 1: 136 __raw_writeb(*(u8 *)val, ctx->regs + offset); 137 break; 138 case 2: 139 __raw_writew(*(u16 *)val, ctx->regs + offset); 140 break; 141 case 4: 142 __raw_writel(*(u32 *)val, ctx->regs + offset); 143 break; 144 #ifdef CONFIG_64BIT 145 case 8: 146 __raw_writeq(*(u64 *)val, ctx->regs + offset); 147 break; 148 #endif 149 default: 150 /* Should be caught by regmap_mmio_check_config */ 151 BUG(); 152 } 153 val_size -= ctx->val_bytes; 154 val += ctx->val_bytes; 155 offset += ctx->val_bytes; 156 } 157 158 if (!IS_ERR(ctx->clk)) 159 clk_disable(ctx->clk); 160 161 return 0; 162 } 163 164 static int regmap_mmio_write(void *context, const void *data, size_t count) 165 { 166 struct regmap_mmio_context *ctx = context; 167 unsigned int offset = ctx->reg_bytes + ctx->pad_bytes; 168 169 regmap_mmio_count_check(count, offset); 170 171 return regmap_mmio_gather_write(context, data, ctx->reg_bytes, 172 data + offset, count - offset); 173 } 174 175 static int regmap_mmio_read(void *context, 176 const void *reg, size_t reg_size, 177 void *val, size_t val_size) 178 { 179 struct regmap_mmio_context *ctx = context; 180 unsigned int offset; 181 int ret; 182 183 regmap_mmio_regsize_check(reg_size); 184 185 if (!IS_ERR(ctx->clk)) { 186 ret = clk_enable(ctx->clk); 187 if (ret < 0) 188 return ret; 189 } 190 191 offset = regmap_mmio_get_offset(reg, reg_size); 192 193 while (val_size) { 194 switch (ctx->val_bytes) { 195 case 1: 196 *(u8 *)val = __raw_readb(ctx->regs + offset); 197 break; 198 case 2: 199 *(u16 *)val = __raw_readw(ctx->regs + offset); 200 break; 201 case 4: 202 *(u32 *)val = __raw_readl(ctx->regs + offset); 203 break; 204 #ifdef CONFIG_64BIT 205 case 8: 206 *(u64 *)val = __raw_readq(ctx->regs + offset); 207 break; 208 #endif 209 default: 210 /* Should be caught by regmap_mmio_check_config */ 211 BUG(); 212 } 213 val_size -= ctx->val_bytes; 214 val += ctx->val_bytes; 215 offset += ctx->val_bytes; 216 } 217 218 if (!IS_ERR(ctx->clk)) 219 clk_disable(ctx->clk); 220 221 return 0; 222 } 223 224 static void regmap_mmio_free_context(void *context) 225 { 226 struct regmap_mmio_context *ctx = context; 227 228 if (!IS_ERR(ctx->clk)) { 229 clk_unprepare(ctx->clk); 230 clk_put(ctx->clk); 231 } 232 kfree(context); 233 } 234 235 static struct regmap_bus regmap_mmio = { 236 .fast_io = true, 237 .write = regmap_mmio_write, 238 .gather_write = regmap_mmio_gather_write, 239 .read = regmap_mmio_read, 240 .free_context = regmap_mmio_free_context, 241 .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, 242 .val_format_endian_default = REGMAP_ENDIAN_NATIVE, 243 }; 244 245 static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, 246 const char *clk_id, 247 void __iomem *regs, 248 const struct regmap_config *config) 249 { 250 struct regmap_mmio_context *ctx; 251 int min_stride; 252 int ret; 253 254 ret = regmap_mmio_regbits_check(config->reg_bits); 255 if (ret) 256 return ERR_PTR(ret); 257 258 if (config->pad_bits) 259 return ERR_PTR(-EINVAL); 260 261 min_stride = regmap_mmio_get_min_stride(config->val_bits); 262 if (min_stride < 0) 263 return ERR_PTR(min_stride); 264 265 if (config->reg_stride < min_stride) 266 return ERR_PTR(-EINVAL); 267 268 switch (config->reg_format_endian) { 269 case REGMAP_ENDIAN_DEFAULT: 270 case REGMAP_ENDIAN_NATIVE: 271 break; 272 default: 273 return ERR_PTR(-EINVAL); 274 } 275 276 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 277 if (!ctx) 278 return ERR_PTR(-ENOMEM); 279 280 ctx->regs = regs; 281 ctx->val_bytes = config->val_bits / 8; 282 ctx->reg_bytes = config->reg_bits / 8; 283 ctx->pad_bytes = config->pad_bits / 8; 284 ctx->clk = ERR_PTR(-ENODEV); 285 286 if (clk_id == NULL) 287 return ctx; 288 289 ctx->clk = clk_get(dev, clk_id); 290 if (IS_ERR(ctx->clk)) { 291 ret = PTR_ERR(ctx->clk); 292 goto err_free; 293 } 294 295 ret = clk_prepare(ctx->clk); 296 if (ret < 0) { 297 clk_put(ctx->clk); 298 goto err_free; 299 } 300 301 return ctx; 302 303 err_free: 304 kfree(ctx); 305 306 return ERR_PTR(ret); 307 } 308 309 struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id, 310 void __iomem *regs, 311 const struct regmap_config *config, 312 struct lock_class_key *lock_key, 313 const char *lock_name) 314 { 315 struct regmap_mmio_context *ctx; 316 317 ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); 318 if (IS_ERR(ctx)) 319 return ERR_CAST(ctx); 320 321 return __regmap_init(dev, ®map_mmio, ctx, config, 322 lock_key, lock_name); 323 } 324 EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk); 325 326 struct regmap *__devm_regmap_init_mmio_clk(struct device *dev, 327 const char *clk_id, 328 void __iomem *regs, 329 const struct regmap_config *config, 330 struct lock_class_key *lock_key, 331 const char *lock_name) 332 { 333 struct regmap_mmio_context *ctx; 334 335 ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); 336 if (IS_ERR(ctx)) 337 return ERR_CAST(ctx); 338 339 return __devm_regmap_init(dev, ®map_mmio, ctx, config, 340 lock_key, lock_name); 341 } 342 EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk); 343 344 MODULE_LICENSE("GPL v2"); 345