uboot/drivers/core/regmap.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2015 Google, Inc
   4 * Written by Simon Glass <sjg@chromium.org>
   5 */
   6
   7#include <common.h>
   8#include <dm.h>
   9#include <errno.h>
  10#include <linux/libfdt.h>
  11#include <malloc.h>
  12#include <mapmem.h>
  13#include <regmap.h>
  14#include <asm/io.h>
  15#include <dm/of_addr.h>
  16#include <linux/ioport.h>
  17
  18DECLARE_GLOBAL_DATA_PTR;
  19
  20/**
  21 * regmap_alloc() - Allocate a regmap with a given number of ranges.
  22 *
  23 * @count: Number of ranges to be allocated for the regmap.
  24 * Return: A pointer to the newly allocated regmap, or NULL on error.
  25 */
  26static struct regmap *regmap_alloc(int count)
  27{
  28        struct regmap *map;
  29
  30        map = malloc(sizeof(*map) + sizeof(map->ranges[0]) * count);
  31        if (!map)
  32                return NULL;
  33        map->range_count = count;
  34
  35        return map;
  36}
  37
  38#if CONFIG_IS_ENABLED(OF_PLATDATA)
  39int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count,
  40                             struct regmap **mapp)
  41{
  42        struct regmap_range *range;
  43        struct regmap *map;
  44
  45        map = regmap_alloc(count);
  46        if (!map)
  47                return -ENOMEM;
  48
  49        for (range = map->ranges; count > 0; reg += 2, range++, count--) {
  50                range->start = *reg;
  51                range->size = reg[1];
  52        }
  53
  54        *mapp = map;
  55
  56        return 0;
  57}
  58#else
  59/**
  60 * init_range() - Initialize a single range of a regmap
  61 * @node:     Device node that will use the map in question
  62 * @range:    Pointer to a regmap_range structure that will be initialized
  63 * @addr_len: The length of the addr parts of the reg property
  64 * @size_len: The length of the size parts of the reg property
  65 * @index:    The index of the range to initialize
  66 *
  67 * This function will read the necessary 'reg' information from the device tree
  68 * (the 'addr' part, and the 'length' part), and initialize the range in
  69 * quesion.
  70 *
  71 * Return: 0 if OK, -ve on error
  72 */
  73static int init_range(ofnode node, struct regmap_range *range, int addr_len,
  74                      int size_len, int index)
  75{
  76        fdt_size_t sz;
  77        struct resource r;
  78
  79        if (of_live_active()) {
  80                int ret;
  81
  82                ret = of_address_to_resource(ofnode_to_np(node),
  83                                             index, &r);
  84                if (ret) {
  85                        debug("%s: Could not read resource of range %d (ret = %d)\n",
  86                              ofnode_get_name(node), index, ret);
  87                        return ret;
  88                }
  89
  90                range->start = r.start;
  91                range->size = r.end - r.start + 1;
  92        } else {
  93                int offset = ofnode_to_offset(node);
  94
  95                range->start = fdtdec_get_addr_size_fixed(gd->fdt_blob, offset,
  96                                                          "reg", index,
  97                                                          addr_len, size_len,
  98                                                          &sz, true);
  99                if (range->start == FDT_ADDR_T_NONE) {
 100                        debug("%s: Could not read start of range %d\n",
 101                              ofnode_get_name(node), index);
 102                        return -EINVAL;
 103                }
 104
 105                range->size = sz;
 106        }
 107
 108        return 0;
 109}
 110
 111int regmap_init_mem_index(ofnode node, struct regmap **mapp, int index)
 112{
 113        struct regmap *map;
 114        int addr_len, size_len;
 115        int ret;
 116
 117        addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node));
 118        if (addr_len < 0) {
 119                debug("%s: Error while reading the addr length (ret = %d)\n",
 120                      ofnode_get_name(node), addr_len);
 121                return addr_len;
 122        }
 123
 124        size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node));
 125        if (size_len < 0) {
 126                debug("%s: Error while reading the size length: (ret = %d)\n",
 127                      ofnode_get_name(node), size_len);
 128                return size_len;
 129        }
 130
 131        map = regmap_alloc(1);
 132        if (!map)
 133                return -ENOMEM;
 134
 135        ret = init_range(node, map->ranges, addr_len, size_len, index);
 136        if (ret)
 137                goto err;
 138
 139        if (ofnode_read_bool(node, "little-endian"))
 140                map->endianness = REGMAP_LITTLE_ENDIAN;
 141        else if (ofnode_read_bool(node, "big-endian"))
 142                map->endianness = REGMAP_BIG_ENDIAN;
 143        else if (ofnode_read_bool(node, "native-endian"))
 144                map->endianness = REGMAP_NATIVE_ENDIAN;
 145        else /* Default: native endianness */
 146                map->endianness = REGMAP_NATIVE_ENDIAN;
 147
 148        *mapp = map;
 149
 150        return 0;
 151err:
 152        regmap_uninit(map);
 153
 154        return ret;
 155}
 156
 157int regmap_init_mem(ofnode node, struct regmap **mapp)
 158{
 159        struct regmap_range *range;
 160        struct regmap *map;
 161        int count;
 162        int addr_len, size_len, both_len;
 163        int len;
 164        int index;
 165        int ret;
 166
 167        addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node));
 168        if (addr_len < 0) {
 169                debug("%s: Error while reading the addr length (ret = %d)\n",
 170                      ofnode_get_name(node), addr_len);
 171                return addr_len;
 172        }
 173
 174        size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node));
 175        if (size_len < 0) {
 176                debug("%s: Error while reading the size length: (ret = %d)\n",
 177                      ofnode_get_name(node), size_len);
 178                return size_len;
 179        }
 180
 181        both_len = addr_len + size_len;
 182        if (!both_len) {
 183                debug("%s: Both addr and size length are zero\n",
 184                      ofnode_get_name(node));
 185                return -EINVAL;
 186        }
 187
 188        len = ofnode_read_size(node, "reg");
 189        if (len < 0) {
 190                debug("%s: Error while reading reg size (ret = %d)\n",
 191                      ofnode_get_name(node), len);
 192                return len;
 193        }
 194        len /= sizeof(fdt32_t);
 195        count = len / both_len;
 196        if (!count) {
 197                debug("%s: Not enough data in reg property\n",
 198                      ofnode_get_name(node));
 199                return -EINVAL;
 200        }
 201
 202        map = regmap_alloc(count);
 203        if (!map)
 204                return -ENOMEM;
 205
 206        for (range = map->ranges, index = 0; count > 0;
 207             count--, range++, index++) {
 208                ret = init_range(node, range, addr_len, size_len, index);
 209                if (ret)
 210                        goto err;
 211        }
 212
 213        if (ofnode_read_bool(node, "little-endian"))
 214                map->endianness = REGMAP_LITTLE_ENDIAN;
 215        else if (ofnode_read_bool(node, "big-endian"))
 216                map->endianness = REGMAP_BIG_ENDIAN;
 217        else if (ofnode_read_bool(node, "native-endian"))
 218                map->endianness = REGMAP_NATIVE_ENDIAN;
 219        else /* Default: native endianness */
 220                map->endianness = REGMAP_NATIVE_ENDIAN;
 221
 222        *mapp = map;
 223
 224        return 0;
 225err:
 226        regmap_uninit(map);
 227
 228        return ret;
 229}
 230#endif
 231
 232void *regmap_get_range(struct regmap *map, unsigned int range_num)
 233{
 234        struct regmap_range *range;
 235
 236        if (range_num >= map->range_count)
 237                return NULL;
 238        range = &map->ranges[range_num];
 239
 240        return map_sysmem(range->start, range->size);
 241}
 242
 243int regmap_uninit(struct regmap *map)
 244{
 245        free(map);
 246
 247        return 0;
 248}
 249
 250static inline u8 __read_8(u8 *addr, enum regmap_endianness_t endianness)
 251{
 252        return readb(addr);
 253}
 254
 255static inline u16 __read_16(u16 *addr, enum regmap_endianness_t endianness)
 256{
 257        switch (endianness) {
 258        case REGMAP_LITTLE_ENDIAN:
 259                return in_le16(addr);
 260        case REGMAP_BIG_ENDIAN:
 261                return in_be16(addr);
 262        case REGMAP_NATIVE_ENDIAN:
 263                return readw(addr);
 264        }
 265
 266        return readw(addr);
 267}
 268
 269static inline u32 __read_32(u32 *addr, enum regmap_endianness_t endianness)
 270{
 271        switch (endianness) {
 272        case REGMAP_LITTLE_ENDIAN:
 273                return in_le32(addr);
 274        case REGMAP_BIG_ENDIAN:
 275                return in_be32(addr);
 276        case REGMAP_NATIVE_ENDIAN:
 277                return readl(addr);
 278        }
 279
 280        return readl(addr);
 281}
 282
 283#if defined(in_le64) && defined(in_be64) && defined(readq)
 284static inline u64 __read_64(u64 *addr, enum regmap_endianness_t endianness)
 285{
 286        switch (endianness) {
 287        case REGMAP_LITTLE_ENDIAN:
 288                return in_le64(addr);
 289        case REGMAP_BIG_ENDIAN:
 290                return in_be64(addr);
 291        case REGMAP_NATIVE_ENDIAN:
 292                return readq(addr);
 293        }
 294
 295        return readq(addr);
 296}
 297#endif
 298
 299int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset,
 300                          void *valp, size_t val_len)
 301{
 302        struct regmap_range *range;
 303        void *ptr;
 304
 305        if (range_num >= map->range_count) {
 306                debug("%s: range index %d larger than range count\n",
 307                      __func__, range_num);
 308                return -ERANGE;
 309        }
 310        range = &map->ranges[range_num];
 311
 312        ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE);
 313
 314        if (offset + val_len > range->size) {
 315                debug("%s: offset/size combination invalid\n", __func__);
 316                return -ERANGE;
 317        }
 318
 319        switch (val_len) {
 320        case REGMAP_SIZE_8:
 321                *((u8 *)valp) = __read_8(ptr, map->endianness);
 322                break;
 323        case REGMAP_SIZE_16:
 324                *((u16 *)valp) = __read_16(ptr, map->endianness);
 325                break;
 326        case REGMAP_SIZE_32:
 327                *((u32 *)valp) = __read_32(ptr, map->endianness);
 328                break;
 329#if defined(in_le64) && defined(in_be64) && defined(readq)
 330        case REGMAP_SIZE_64:
 331                *((u64 *)valp) = __read_64(ptr, map->endianness);
 332                break;
 333#endif
 334        default:
 335                debug("%s: regmap size %zu unknown\n", __func__, val_len);
 336                return -EINVAL;
 337        }
 338
 339        return 0;
 340}
 341
 342int regmap_raw_read(struct regmap *map, uint offset, void *valp, size_t val_len)
 343{
 344        return regmap_raw_read_range(map, 0, offset, valp, val_len);
 345}
 346
 347int regmap_read(struct regmap *map, uint offset, uint *valp)
 348{
 349        return regmap_raw_read(map, offset, valp, REGMAP_SIZE_32);
 350}
 351
 352static inline void __write_8(u8 *addr, const u8 *val,
 353                             enum regmap_endianness_t endianness)
 354{
 355        writeb(*val, addr);
 356}
 357
 358static inline void __write_16(u16 *addr, const u16 *val,
 359                              enum regmap_endianness_t endianness)
 360{
 361        switch (endianness) {
 362        case REGMAP_NATIVE_ENDIAN:
 363                writew(*val, addr);
 364                break;
 365        case REGMAP_LITTLE_ENDIAN:
 366                out_le16(addr, *val);
 367                break;
 368        case REGMAP_BIG_ENDIAN:
 369                out_be16(addr, *val);
 370                break;
 371        }
 372}
 373
 374static inline void __write_32(u32 *addr, const u32 *val,
 375                              enum regmap_endianness_t endianness)
 376{
 377        switch (endianness) {
 378        case REGMAP_NATIVE_ENDIAN:
 379                writel(*val, addr);
 380                break;
 381        case REGMAP_LITTLE_ENDIAN:
 382                out_le32(addr, *val);
 383                break;
 384        case REGMAP_BIG_ENDIAN:
 385                out_be32(addr, *val);
 386                break;
 387        }
 388}
 389
 390#if defined(out_le64) && defined(out_be64) && defined(writeq)
 391static inline void __write_64(u64 *addr, const u64 *val,
 392                              enum regmap_endianness_t endianness)
 393{
 394        switch (endianness) {
 395        case REGMAP_NATIVE_ENDIAN:
 396                writeq(*val, addr);
 397                break;
 398        case REGMAP_LITTLE_ENDIAN:
 399                out_le64(addr, *val);
 400                break;
 401        case REGMAP_BIG_ENDIAN:
 402                out_be64(addr, *val);
 403                break;
 404        }
 405}
 406#endif
 407
 408int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset,
 409                           const void *val, size_t val_len)
 410{
 411        struct regmap_range *range;
 412        void *ptr;
 413
 414        if (range_num >= map->range_count) {
 415                debug("%s: range index %d larger than range count\n",
 416                      __func__, range_num);
 417                return -ERANGE;
 418        }
 419        range = &map->ranges[range_num];
 420
 421        ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE);
 422
 423        if (offset + val_len > range->size) {
 424                debug("%s: offset/size combination invalid\n", __func__);
 425                return -ERANGE;
 426        }
 427
 428        switch (val_len) {
 429        case REGMAP_SIZE_8:
 430                __write_8(ptr, val, map->endianness);
 431                break;
 432        case REGMAP_SIZE_16:
 433                __write_16(ptr, val, map->endianness);
 434                break;
 435        case REGMAP_SIZE_32:
 436                __write_32(ptr, val, map->endianness);
 437                break;
 438#if defined(out_le64) && defined(out_be64) && defined(writeq)
 439        case REGMAP_SIZE_64:
 440                __write_64(ptr, val, map->endianness);
 441                break;
 442#endif
 443        default:
 444                debug("%s: regmap size %zu unknown\n", __func__, val_len);
 445                return -EINVAL;
 446        }
 447
 448        return 0;
 449}
 450
 451int regmap_raw_write(struct regmap *map, uint offset, const void *val,
 452                     size_t val_len)
 453{
 454        return regmap_raw_write_range(map, 0, offset, val, val_len);
 455}
 456
 457int regmap_write(struct regmap *map, uint offset, uint val)
 458{
 459        return regmap_raw_write(map, offset, &val, REGMAP_SIZE_32);
 460}
 461
 462int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val)
 463{
 464        uint reg;
 465        int ret;
 466
 467        ret = regmap_read(map, offset, &reg);
 468        if (ret)
 469                return ret;
 470
 471        reg &= ~mask;
 472
 473        return regmap_write(map, offset, reg | (val & mask));
 474}
 475