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 <log.h>
  11#include <asm/global_data.h>
  12#include <linux/libfdt.h>
  13#include <malloc.h>
  14#include <mapmem.h>
  15#include <regmap.h>
  16#include <asm/io.h>
  17#include <dm/of_addr.h>
  18#include <dm/devres.h>
  19#include <linux/ioport.h>
  20#include <linux/compat.h>
  21#include <linux/err.h>
  22#include <linux/bitops.h>
  23
  24/*
  25 * Internal representation of a regmap field. Instead of storing the MSB and
  26 * LSB, store the shift and mask. This makes the code a bit cleaner and faster
  27 * because the shift and mask don't have to be calculated every time.
  28 */
  29struct regmap_field {
  30        struct regmap *regmap;
  31        unsigned int mask;
  32        /* lsb */
  33        unsigned int shift;
  34        unsigned int reg;
  35};
  36
  37DECLARE_GLOBAL_DATA_PTR;
  38
  39/**
  40 * regmap_alloc() - Allocate a regmap with a given number of ranges.
  41 *
  42 * @count: Number of ranges to be allocated for the regmap.
  43 *
  44 * The default regmap width is set to REGMAP_SIZE_32. Callers can override it
  45 * if they need.
  46 *
  47 * Return: A pointer to the newly allocated regmap, or NULL on error.
  48 */
  49static struct regmap *regmap_alloc(int count)
  50{
  51        struct regmap *map;
  52        size_t size = sizeof(*map) + sizeof(map->ranges[0]) * count;
  53
  54        map = calloc(1, size);
  55        if (!map)
  56                return NULL;
  57        map->range_count = count;
  58        map->width = REGMAP_SIZE_32;
  59
  60        return map;
  61}
  62
  63#if CONFIG_IS_ENABLED(OF_PLATDATA)
  64int regmap_init_mem_plat(struct udevice *dev, fdt_val_t *reg, int count,
  65                         struct regmap **mapp)
  66{
  67        struct regmap_range *range;
  68        struct regmap *map;
  69
  70        map = regmap_alloc(count);
  71        if (!map)
  72                return -ENOMEM;
  73
  74        for (range = map->ranges; count > 0; reg += 2, range++, count--) {
  75                range->start = *reg;
  76                range->size = reg[1];
  77        }
  78
  79        *mapp = map;
  80
  81        return 0;
  82}
  83#else
  84/**
  85 * init_range() - Initialize a single range of a regmap
  86 * @node:     Device node that will use the map in question
  87 * @range:    Pointer to a regmap_range structure that will be initialized
  88 * @addr_len: The length of the addr parts of the reg property
  89 * @size_len: The length of the size parts of the reg property
  90 * @index:    The index of the range to initialize
  91 *
  92 * This function will read the necessary 'reg' information from the device tree
  93 * (the 'addr' part, and the 'length' part), and initialize the range in
  94 * quesion.
  95 *
  96 * Return: 0 if OK, -ve on error
  97 */
  98static int init_range(ofnode node, struct regmap_range *range, int addr_len,
  99                      int size_len, int index)
 100{
 101        fdt_size_t sz;
 102        struct resource r;
 103
 104        if (of_live_active()) {
 105                int ret;
 106
 107                ret = of_address_to_resource(ofnode_to_np(node),
 108                                             index, &r);
 109                if (ret) {
 110                        debug("%s: Could not read resource of range %d (ret = %d)\n",
 111                              ofnode_get_name(node), index, ret);
 112                        return ret;
 113                }
 114
 115                range->start = r.start;
 116                range->size = r.end - r.start + 1;
 117        } else {
 118                int offset = ofnode_to_offset(node);
 119
 120                range->start = fdtdec_get_addr_size_fixed(gd->fdt_blob, offset,
 121                                                          "reg", index,
 122                                                          addr_len, size_len,
 123                                                          &sz, true);
 124                if (range->start == FDT_ADDR_T_NONE) {
 125                        debug("%s: Could not read start of range %d\n",
 126                              ofnode_get_name(node), index);
 127                        return -EINVAL;
 128                }
 129
 130                range->size = sz;
 131        }
 132
 133        return 0;
 134}
 135
 136int regmap_init_mem_index(ofnode node, struct regmap **mapp, int index)
 137{
 138        struct regmap *map;
 139        int addr_len, size_len;
 140        int ret;
 141
 142        addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node));
 143        if (addr_len < 0) {
 144                debug("%s: Error while reading the addr length (ret = %d)\n",
 145                      ofnode_get_name(node), addr_len);
 146                return addr_len;
 147        }
 148
 149        size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node));
 150        if (size_len < 0) {
 151                debug("%s: Error while reading the size length: (ret = %d)\n",
 152                      ofnode_get_name(node), size_len);
 153                return size_len;
 154        }
 155
 156        map = regmap_alloc(1);
 157        if (!map)
 158                return -ENOMEM;
 159
 160        ret = init_range(node, map->ranges, addr_len, size_len, index);
 161        if (ret)
 162                goto err;
 163
 164        if (ofnode_read_bool(node, "little-endian"))
 165                map->endianness = REGMAP_LITTLE_ENDIAN;
 166        else if (ofnode_read_bool(node, "big-endian"))
 167                map->endianness = REGMAP_BIG_ENDIAN;
 168        else if (ofnode_read_bool(node, "native-endian"))
 169                map->endianness = REGMAP_NATIVE_ENDIAN;
 170        else /* Default: native endianness */
 171                map->endianness = REGMAP_NATIVE_ENDIAN;
 172
 173        *mapp = map;
 174
 175        return 0;
 176err:
 177        regmap_uninit(map);
 178
 179        return ret;
 180}
 181
 182int regmap_init_mem_range(ofnode node, ulong r_start, ulong r_size,
 183                          struct regmap **mapp)
 184{
 185        struct regmap *map;
 186        struct regmap_range *range;
 187
 188        map = regmap_alloc(1);
 189        if (!map)
 190                return -ENOMEM;
 191
 192        range = &map->ranges[0];
 193        range->start = r_start;
 194        range->size = r_size;
 195
 196        if (ofnode_read_bool(node, "little-endian"))
 197                map->endianness = REGMAP_LITTLE_ENDIAN;
 198        else if (ofnode_read_bool(node, "big-endian"))
 199                map->endianness = REGMAP_BIG_ENDIAN;
 200        else if (ofnode_read_bool(node, "native-endian"))
 201                map->endianness = REGMAP_NATIVE_ENDIAN;
 202        else /* Default: native endianness */
 203                map->endianness = REGMAP_NATIVE_ENDIAN;
 204
 205        *mapp = map;
 206        return 0;
 207}
 208
 209int regmap_init_mem(ofnode node, struct regmap **mapp)
 210{
 211        struct regmap_range *range;
 212        struct regmap *map;
 213        int count;
 214        int addr_len, size_len, both_len;
 215        int len;
 216        int index;
 217        int ret;
 218
 219        addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node));
 220        if (addr_len < 0) {
 221                debug("%s: Error while reading the addr length (ret = %d)\n",
 222                      ofnode_get_name(node), addr_len);
 223                return addr_len;
 224        }
 225
 226        size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node));
 227        if (size_len < 0) {
 228                debug("%s: Error while reading the size length: (ret = %d)\n",
 229                      ofnode_get_name(node), size_len);
 230                return size_len;
 231        }
 232
 233        both_len = addr_len + size_len;
 234        if (!both_len) {
 235                debug("%s: Both addr and size length are zero\n",
 236                      ofnode_get_name(node));
 237                return -EINVAL;
 238        }
 239
 240        len = ofnode_read_size(node, "reg");
 241        if (len < 0) {
 242                debug("%s: Error while reading reg size (ret = %d)\n",
 243                      ofnode_get_name(node), len);
 244                return len;
 245        }
 246        len /= sizeof(fdt32_t);
 247        count = len / both_len;
 248        if (!count) {
 249                debug("%s: Not enough data in reg property\n",
 250                      ofnode_get_name(node));
 251                return -EINVAL;
 252        }
 253
 254        map = regmap_alloc(count);
 255        if (!map)
 256                return -ENOMEM;
 257
 258        for (range = map->ranges, index = 0; count > 0;
 259             count--, range++, index++) {
 260                ret = init_range(node, range, addr_len, size_len, index);
 261                if (ret)
 262                        goto err;
 263        }
 264
 265        if (ofnode_read_bool(node, "little-endian"))
 266                map->endianness = REGMAP_LITTLE_ENDIAN;
 267        else if (ofnode_read_bool(node, "big-endian"))
 268                map->endianness = REGMAP_BIG_ENDIAN;
 269        else if (ofnode_read_bool(node, "native-endian"))
 270                map->endianness = REGMAP_NATIVE_ENDIAN;
 271        else /* Default: native endianness */
 272                map->endianness = REGMAP_NATIVE_ENDIAN;
 273
 274        *mapp = map;
 275
 276        return 0;
 277err:
 278        regmap_uninit(map);
 279
 280        return ret;
 281}
 282
 283static void devm_regmap_release(struct udevice *dev, void *res)
 284{
 285        regmap_uninit(*(struct regmap **)res);
 286}
 287
 288struct regmap *devm_regmap_init(struct udevice *dev,
 289                                const struct regmap_bus *bus,
 290                                void *bus_context,
 291                                const struct regmap_config *config)
 292{
 293        int rc;
 294        struct regmap **mapp, *map;
 295
 296        /* this looks like a leak, but devres takes care of it */
 297        mapp = devres_alloc(devm_regmap_release, sizeof(struct regmap *),
 298                            __GFP_ZERO);
 299        if (unlikely(!mapp))
 300                return ERR_PTR(-ENOMEM);
 301
 302        if (config && config->r_size != 0)
 303                rc = regmap_init_mem_range(dev_ofnode(dev), config->r_start,
 304                                           config->r_size, mapp);
 305        else
 306                rc = regmap_init_mem(dev_ofnode(dev), mapp);
 307        if (rc)
 308                return ERR_PTR(rc);
 309
 310        map = *mapp;
 311        if (config) {
 312                map->width = config->width;
 313                map->reg_offset_shift = config->reg_offset_shift;
 314        }
 315
 316        devres_add(dev, mapp);
 317        return *mapp;
 318}
 319#endif
 320
 321void *regmap_get_range(struct regmap *map, unsigned int range_num)
 322{
 323        struct regmap_range *range;
 324
 325        if (range_num >= map->range_count)
 326                return NULL;
 327        range = &map->ranges[range_num];
 328
 329        return map_sysmem(range->start, range->size);
 330}
 331
 332int regmap_uninit(struct regmap *map)
 333{
 334        free(map);
 335
 336        return 0;
 337}
 338
 339static inline u8 __read_8(u8 *addr, enum regmap_endianness_t endianness)
 340{
 341        return readb(addr);
 342}
 343
 344static inline u16 __read_16(u16 *addr, enum regmap_endianness_t endianness)
 345{
 346        switch (endianness) {
 347        case REGMAP_LITTLE_ENDIAN:
 348                return in_le16(addr);
 349        case REGMAP_BIG_ENDIAN:
 350                return in_be16(addr);
 351        case REGMAP_NATIVE_ENDIAN:
 352                return readw(addr);
 353        }
 354
 355        return readw(addr);
 356}
 357
 358static inline u32 __read_32(u32 *addr, enum regmap_endianness_t endianness)
 359{
 360        switch (endianness) {
 361        case REGMAP_LITTLE_ENDIAN:
 362                return in_le32(addr);
 363        case REGMAP_BIG_ENDIAN:
 364                return in_be32(addr);
 365        case REGMAP_NATIVE_ENDIAN:
 366                return readl(addr);
 367        }
 368
 369        return readl(addr);
 370}
 371
 372#if defined(in_le64) && defined(in_be64) && defined(readq)
 373static inline u64 __read_64(u64 *addr, enum regmap_endianness_t endianness)
 374{
 375        switch (endianness) {
 376        case REGMAP_LITTLE_ENDIAN:
 377                return in_le64(addr);
 378        case REGMAP_BIG_ENDIAN:
 379                return in_be64(addr);
 380        case REGMAP_NATIVE_ENDIAN:
 381                return readq(addr);
 382        }
 383
 384        return readq(addr);
 385}
 386#endif
 387
 388int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset,
 389                          void *valp, size_t val_len)
 390{
 391        struct regmap_range *range;
 392        void *ptr;
 393
 394        if (range_num >= map->range_count) {
 395                debug("%s: range index %d larger than range count\n",
 396                      __func__, range_num);
 397                return -ERANGE;
 398        }
 399        range = &map->ranges[range_num];
 400
 401        offset <<= map->reg_offset_shift;
 402        if (offset + val_len > range->size) {
 403                debug("%s: offset/size combination invalid\n", __func__);
 404                return -ERANGE;
 405        }
 406
 407        ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE);
 408
 409        switch (val_len) {
 410        case REGMAP_SIZE_8:
 411                *((u8 *)valp) = __read_8(ptr, map->endianness);
 412                break;
 413        case REGMAP_SIZE_16:
 414                *((u16 *)valp) = __read_16(ptr, map->endianness);
 415                break;
 416        case REGMAP_SIZE_32:
 417                *((u32 *)valp) = __read_32(ptr, map->endianness);
 418                break;
 419#if defined(in_le64) && defined(in_be64) && defined(readq)
 420        case REGMAP_SIZE_64:
 421                *((u64 *)valp) = __read_64(ptr, map->endianness);
 422                break;
 423#endif
 424        default:
 425                debug("%s: regmap size %zu unknown\n", __func__, val_len);
 426                return -EINVAL;
 427        }
 428
 429        return 0;
 430}
 431
 432int regmap_raw_read(struct regmap *map, uint offset, void *valp, size_t val_len)
 433{
 434        return regmap_raw_read_range(map, 0, offset, valp, val_len);
 435}
 436
 437int regmap_read(struct regmap *map, uint offset, uint *valp)
 438{
 439        union {
 440                u8 v8;
 441                u16 v16;
 442                u32 v32;
 443                u64 v64;
 444        } u;
 445        int res;
 446
 447        res = regmap_raw_read(map, offset, &u, map->width);
 448        if (res)
 449                return res;
 450
 451        switch (map->width) {
 452        case REGMAP_SIZE_8:
 453                *valp = u.v8;
 454                break;
 455        case REGMAP_SIZE_16:
 456                *valp = u.v16;
 457                break;
 458        case REGMAP_SIZE_32:
 459                *valp = u.v32;
 460                break;
 461        case REGMAP_SIZE_64:
 462                *valp = u.v64;
 463                break;
 464        default:
 465                unreachable();
 466        }
 467
 468        return 0;
 469}
 470
 471static inline void __write_8(u8 *addr, const u8 *val,
 472                             enum regmap_endianness_t endianness)
 473{
 474        writeb(*val, addr);
 475}
 476
 477static inline void __write_16(u16 *addr, const u16 *val,
 478                              enum regmap_endianness_t endianness)
 479{
 480        switch (endianness) {
 481        case REGMAP_NATIVE_ENDIAN:
 482                writew(*val, addr);
 483                break;
 484        case REGMAP_LITTLE_ENDIAN:
 485                out_le16(addr, *val);
 486                break;
 487        case REGMAP_BIG_ENDIAN:
 488                out_be16(addr, *val);
 489                break;
 490        }
 491}
 492
 493static inline void __write_32(u32 *addr, const u32 *val,
 494                              enum regmap_endianness_t endianness)
 495{
 496        switch (endianness) {
 497        case REGMAP_NATIVE_ENDIAN:
 498                writel(*val, addr);
 499                break;
 500        case REGMAP_LITTLE_ENDIAN:
 501                out_le32(addr, *val);
 502                break;
 503        case REGMAP_BIG_ENDIAN:
 504                out_be32(addr, *val);
 505                break;
 506        }
 507}
 508
 509#if defined(out_le64) && defined(out_be64) && defined(writeq)
 510static inline void __write_64(u64 *addr, const u64 *val,
 511                              enum regmap_endianness_t endianness)
 512{
 513        switch (endianness) {
 514        case REGMAP_NATIVE_ENDIAN:
 515                writeq(*val, addr);
 516                break;
 517        case REGMAP_LITTLE_ENDIAN:
 518                out_le64(addr, *val);
 519                break;
 520        case REGMAP_BIG_ENDIAN:
 521                out_be64(addr, *val);
 522                break;
 523        }
 524}
 525#endif
 526
 527int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset,
 528                           const void *val, size_t val_len)
 529{
 530        struct regmap_range *range;
 531        void *ptr;
 532
 533        if (range_num >= map->range_count) {
 534                debug("%s: range index %d larger than range count\n",
 535                      __func__, range_num);
 536                return -ERANGE;
 537        }
 538        range = &map->ranges[range_num];
 539
 540        offset <<= map->reg_offset_shift;
 541        if (offset + val_len > range->size) {
 542                debug("%s: offset/size combination invalid\n", __func__);
 543                return -ERANGE;
 544        }
 545
 546        ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE);
 547
 548        switch (val_len) {
 549        case REGMAP_SIZE_8:
 550                __write_8(ptr, val, map->endianness);
 551                break;
 552        case REGMAP_SIZE_16:
 553                __write_16(ptr, val, map->endianness);
 554                break;
 555        case REGMAP_SIZE_32:
 556                __write_32(ptr, val, map->endianness);
 557                break;
 558#if defined(out_le64) && defined(out_be64) && defined(writeq)
 559        case REGMAP_SIZE_64:
 560                __write_64(ptr, val, map->endianness);
 561                break;
 562#endif
 563        default:
 564                debug("%s: regmap size %zu unknown\n", __func__, val_len);
 565                return -EINVAL;
 566        }
 567
 568        return 0;
 569}
 570
 571int regmap_raw_write(struct regmap *map, uint offset, const void *val,
 572                     size_t val_len)
 573{
 574        return regmap_raw_write_range(map, 0, offset, val, val_len);
 575}
 576
 577int regmap_write(struct regmap *map, uint offset, uint val)
 578{
 579        union {
 580                u8 v8;
 581                u16 v16;
 582                u32 v32;
 583                u64 v64;
 584        } u;
 585
 586        switch (map->width) {
 587        case REGMAP_SIZE_8:
 588                u.v8 = val;
 589                break;
 590        case REGMAP_SIZE_16:
 591                u.v16 = val;
 592                break;
 593        case REGMAP_SIZE_32:
 594                u.v32 = val;
 595                break;
 596        case REGMAP_SIZE_64:
 597                u.v64 = val;
 598                break;
 599        default:
 600                debug("%s: regmap size %zu unknown\n", __func__,
 601                      (size_t)map->width);
 602                return -EINVAL;
 603        }
 604
 605        return regmap_raw_write(map, offset, &u, map->width);
 606}
 607
 608int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val)
 609{
 610        uint reg;
 611        int ret;
 612
 613        ret = regmap_read(map, offset, &reg);
 614        if (ret)
 615                return ret;
 616
 617        reg &= ~mask;
 618
 619        return regmap_write(map, offset, reg | (val & mask));
 620}
 621
 622int regmap_field_read(struct regmap_field *field, unsigned int *val)
 623{
 624        int ret;
 625        unsigned int reg_val;
 626
 627        ret = regmap_read(field->regmap, field->reg, &reg_val);
 628        if (ret != 0)
 629                return ret;
 630
 631        reg_val &= field->mask;
 632        reg_val >>= field->shift;
 633        *val = reg_val;
 634
 635        return ret;
 636}
 637
 638int regmap_field_write(struct regmap_field *field, unsigned int val)
 639{
 640        return regmap_update_bits(field->regmap, field->reg, field->mask,
 641                                  val << field->shift);
 642}
 643
 644static void regmap_field_init(struct regmap_field *rm_field,
 645                              struct regmap *regmap,
 646                              struct reg_field reg_field)
 647{
 648        rm_field->regmap = regmap;
 649        rm_field->reg = reg_field.reg;
 650        rm_field->shift = reg_field.lsb;
 651        rm_field->mask = GENMASK(reg_field.msb, reg_field.lsb);
 652}
 653
 654struct regmap_field *devm_regmap_field_alloc(struct udevice *dev,
 655                                             struct regmap *regmap,
 656                                             struct reg_field reg_field)
 657{
 658        struct regmap_field *rm_field = devm_kzalloc(dev, sizeof(*rm_field),
 659                                                     GFP_KERNEL);
 660        if (!rm_field)
 661                return ERR_PTR(-ENOMEM);
 662
 663        regmap_field_init(rm_field, regmap, reg_field);
 664
 665        return rm_field;
 666}
 667
 668void devm_regmap_field_free(struct udevice *dev, struct regmap_field *field)
 669{
 670        devm_kfree(dev, field);
 671}
 672
 673struct regmap_field *regmap_field_alloc(struct regmap *regmap,
 674                                        struct reg_field reg_field)
 675{
 676        struct regmap_field *rm_field = kzalloc(sizeof(*rm_field), GFP_KERNEL);
 677
 678        if (!rm_field)
 679                return ERR_PTR(-ENOMEM);
 680
 681        regmap_field_init(rm_field, regmap, reg_field);
 682
 683        return rm_field;
 684}
 685
 686void regmap_field_free(struct regmap_field *field)
 687{
 688        kfree(field);
 689}
 690