参照元

説明

引数

返り値

参考

実装

struct regmap *__regmap_init(struct device *dev,
                             const struct regmap_bus *bus,
                             void *bus_context,
                             const struct regmap_config *config,
                             struct lock_class_key *lock_key,
                             const char *lock_name)
{
        struct regmap *map;
        int ret = -EINVAL;
        enum regmap_endian reg_endian, val_endian;
        int i, j;

        if (!config)
                goto err;

        map = kzalloc(sizeof(*map), GFP_KERNEL);
        if (map == NULL) {
                ret = -ENOMEM;
                goto err;
        }

        if (config->lock && config->unlock) {
                map->lock = config->lock;
                map->unlock = config->unlock;
                map->lock_arg = config->lock_arg;
        } else {
                if ((bus && bus->fast_io) ||
                    config->fast_io) {
                        spin_lock_init(&map->spinlock);
                        map->lock = regmap_lock_spinlock;
                        map->unlock = regmap_unlock_spinlock;
                        lockdep_set_class_and_name(&map->spinlock,
                                                   lock_key, lock_name);
                } else {
                        mutex_init(&map->mutex);
                        map->lock = regmap_lock_mutex;
                        map->unlock = regmap_unlock_mutex;
                        lockdep_set_class_and_name(&map->mutex,
                                                   lock_key, lock_name);
                }
                map->lock_arg = map;
        }

        /*
         * When we write in fast-paths with regmap_bulk_write() don't allocate
         * scratch buffers with sleeping allocations.
         */
        if ((bus && bus->fast_io) || config->fast_io)
                map->alloc_flags = GFP_ATOMIC;
        else
                map->alloc_flags = GFP_KERNEL;

        map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
        map->format.pad_bytes = config->pad_bits / 8;
        map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
        map->format.buf_size = DIV_ROUND_UP(config->reg_bits +
                        config->val_bits + config->pad_bits, 8);
        map->reg_shift = config->pad_bits % 8;
        if (config->reg_stride)
                map->reg_stride = config->reg_stride;
        else
                map->reg_stride = 1;
        map->use_single_read = config->use_single_rw || !bus || !bus->read;
        map->use_single_write = config->use_single_rw || !bus || !bus->write;
        map->can_multi_write = config->can_multi_write && bus && bus->write;
        if (bus) {
                map->max_raw_read = bus->max_raw_read;
                map->max_raw_write = bus->max_raw_write;
        }
        map->dev = dev;
        map->bus = bus;
        map->bus_context = bus_context;
        map->max_register = config->max_register;
        map->wr_table = config->wr_table;
        map->rd_table = config->rd_table;
        map->volatile_table = config->volatile_table;
        map->precious_table = config->precious_table;
        map->writeable_reg = config->writeable_reg;
        map->readable_reg = config->readable_reg;
        map->volatile_reg = config->volatile_reg;
        map->precious_reg = config->precious_reg;
        map->cache_type = config->cache_type;
        map->name = config->name;

        spin_lock_init(&map->async_lock);
        INIT_LIST_HEAD(&map->async_list);
        INIT_LIST_HEAD(&map->async_free);
        init_waitqueue_head(&map->async_waitq);

        if (config->read_flag_mask || config->write_flag_mask) {
                map->read_flag_mask = config->read_flag_mask;
                map->write_flag_mask = config->write_flag_mask;
        } else if (bus) {
                map->read_flag_mask = bus->read_flag_mask;
        }

        if (!bus) {
                map->reg_read  = config->reg_read;
                map->reg_write = config->reg_write;

                map->defer_caching = false;
                goto skip_format_initialization;
        } else if (!bus->read || !bus->write) {
                map->reg_read = _regmap_bus_reg_read;
                map->reg_write = _regmap_bus_reg_write;

                map->defer_caching = false;
                goto skip_format_initialization;
        } else {
                map->reg_read  = _regmap_bus_read;
                map->reg_update_bits = bus->reg_update_bits;
        }

        reg_endian = regmap_get_reg_endian(bus, config);
        val_endian = regmap_get_val_endian(dev, bus, config);

        switch (config->reg_bits + map->reg_shift) {
        case 2:
                switch (config->val_bits) {
                case 6:
                        map->format.format_write = regmap_format_2_6_write;
                        break;
                default:
                        goto err_map;
                }
                break;

        case 4:
                switch (config->val_bits) {
                case 12:
                        map->format.format_write = regmap_format_4_12_write;
                        break;
                default:
                        goto err_map;
                }
                break;

        case 7:
                switch (config->val_bits) {
                case 9:
                        map->format.format_write = regmap_format_7_9_write;
                        break;
                default:
                        goto err_map;
                }
                break;

        case 10:
                switch (config->val_bits) {
                case 14:
                        map->format.format_write = regmap_format_10_14_write;
                        break;
                default:
                        goto err_map;
                }
                break;

        case 8:
                map->format.format_reg = regmap_format_8;
                break;

        case 16:
                switch (reg_endian) {
                case REGMAP_ENDIAN_BIG:
                        map->format.format_reg = regmap_format_16_be;
                        break;
                case REGMAP_ENDIAN_NATIVE:
                        map->format.format_reg = regmap_format_16_native;
                        break;
                default:
                        goto err_map;
                }
                break;

        case 24:
                if (reg_endian != REGMAP_ENDIAN_BIG)
                        goto err_map;
                map->format.format_reg = regmap_format_24;
                break;

        case 32:
                switch (reg_endian) {
                case REGMAP_ENDIAN_BIG:
                        map->format.format_reg = regmap_format_32_be;
                        break;
                case REGMAP_ENDIAN_NATIVE:
                        map->format.format_reg = regmap_format_32_native;
                        break;
                default:
                        goto err_map;
                }
                break;

        default:
                goto err_map;
        }

        if (val_endian == REGMAP_ENDIAN_NATIVE)
                map->format.parse_inplace = regmap_parse_inplace_noop;

        switch (config->val_bits) {
        case 8:
                map->format.format_val = regmap_format_8;
                map->format.parse_val = regmap_parse_8;
                map->format.parse_inplace = regmap_parse_inplace_noop;
                break;
        case 16:
                switch (val_endian) {
                case REGMAP_ENDIAN_BIG:
                        map->format.format_val = regmap_format_16_be;
                        map->format.parse_val = regmap_parse_16_be;
                        map->format.parse_inplace = regmap_parse_16_be_inplace;
                        break;
                case REGMAP_ENDIAN_LITTLE:
                        map->format.format_val = regmap_format_16_le;
                        map->format.parse_val = regmap_parse_16_le;
                        map->format.parse_inplace = regmap_parse_16_le_inplace;
                        break;
                case REGMAP_ENDIAN_NATIVE:
                        map->format.format_val = regmap_format_16_native;
                        map->format.parse_val = regmap_parse_16_native;
                        break;
                default:
                        goto err_map;
                }
                break;
        case 24:
                if (val_endian != REGMAP_ENDIAN_BIG)
                        goto err_map;
                map->format.format_val = regmap_format_24;
                map->format.parse_val = regmap_parse_24;
                break;
        case 32:
                switch (val_endian) {
                case REGMAP_ENDIAN_BIG:
                        map->format.format_val = regmap_format_32_be;
                        map->format.parse_val = regmap_parse_32_be;
                        map->format.parse_inplace = regmap_parse_32_be_inplace;
                        break;
                case REGMAP_ENDIAN_LITTLE:
                        map->format.format_val = regmap_format_32_le;
                        map->format.parse_val = regmap_parse_32_le;
                        map->format.parse_inplace = regmap_parse_32_le_inplace;
                        break;
                case REGMAP_ENDIAN_NATIVE:
                        map->format.format_val = regmap_format_32_native;
                        map->format.parse_val = regmap_parse_32_native;
                        break;
                default:
                        goto err_map;
                }
                break;
        }

        if (map->format.format_write) {
                if ((reg_endian != REGMAP_ENDIAN_BIG) ||
                    (val_endian != REGMAP_ENDIAN_BIG))
                        goto err_map;
                map->use_single_write = true;
        }

        if (!map->format.format_write &&
            !(map->format.format_reg && map->format.format_val))
                goto err_map;

        map->work_buf = kzalloc(map->format.buf_size, GFP_KERNEL);
        if (map->work_buf == NULL) {
                ret = -ENOMEM;
                goto err_map;
        }

        if (map->format.format_write) {
                map->defer_caching = false;
                map->reg_write = _regmap_bus_formatted_write;
        } else if (map->format.format_val) {
                map->defer_caching = true;
                map->reg_write = _regmap_bus_raw_write;
        }

skip_format_initialization:

        map->range_tree = RB_ROOT;
        for (i = 0; i < config->num_ranges; i++) {
                const struct regmap_range_cfg *range_cfg = &config->ranges[i];
                struct regmap_range_node *new;

                /* Sanity check */
                if (range_cfg->range_max < range_cfg->range_min) {
                        dev_err(map->dev, "Invalid range %d: %d < %d\n", i,
                                range_cfg->range_max, range_cfg->range_min);
                        goto err_range;
                }

                if (range_cfg->range_max > map->max_register) {
                        dev_err(map->dev, "Invalid range %d: %d > %d\n", i,
                                range_cfg->range_max, map->max_register);
                        goto err_range;
                }

                if (range_cfg->selector_reg > map->max_register) {
                        dev_err(map->dev,
                                "Invalid range %d: selector out of map\n", i);
                        goto err_range;
                }

                if (range_cfg->window_len == 0) {
                        dev_err(map->dev, "Invalid range %d: window_len 0\n",
                                i);
                        goto err_range;
                }

                /* Make sure, that this register range has no selector
                   or data window within its boundary */
                for (j = 0; j < config->num_ranges; j++) {
                        unsigned sel_reg = config->ranges[j].selector_reg;
                        unsigned win_min = config->ranges[j].window_start;
                        unsigned win_max = win_min +
                                           config->ranges[j].window_len - 1;

                        /* Allow data window inside its own virtual range */
                        if (j == i)
                                continue;

                        if (range_cfg->range_min <= sel_reg &&
                            sel_reg <= range_cfg->range_max) {
                                dev_err(map->dev,
                                        "Range %d: selector for %d in window\n",
                                        i, j);
                                goto err_range;
                        }

                        if (!(win_max < range_cfg->range_min ||
                              win_min > range_cfg->range_max)) {
                                dev_err(map->dev,
                                        "Range %d: window for %d in window\n",
                                        i, j);
                                goto err_range;
                        }
                }

                new = kzalloc(sizeof(*new), GFP_KERNEL);
                if (new == NULL) {
                        ret = -ENOMEM;
                        goto err_range;
                }

                new->map = map;
                new->name = range_cfg->name;
                new->range_min = range_cfg->range_min;
                new->range_max = range_cfg->range_max;
                new->selector_reg = range_cfg->selector_reg;
                new->selector_mask = range_cfg->selector_mask;
                new->selector_shift = range_cfg->selector_shift;
                new->window_start = range_cfg->window_start;
                new->window_len = range_cfg->window_len;

                if (!_regmap_range_add(map, new)) {
                        dev_err(map->dev, "Failed to add range %d\n", i);
                        kfree(new);
                        goto err_range;
                }

                if (map->selector_work_buf == NULL) {
                        map->selector_work_buf =
                                kzalloc(map->format.buf_size, GFP_KERNEL);
                        if (map->selector_work_buf == NULL) {
                                ret = -ENOMEM;
                                goto err_range;
                        }
                }
        }

        ret = regcache_init(map, config);
        if (ret != 0)
                goto err_range;

        if (dev) {
                ret = regmap_attach_dev(dev, map, config);
                if (ret != 0)
                        goto err_regcache;
        }

        return map;

err_regcache:
        regcache_exit(map);
err_range:
        regmap_range_exit(map);
        kfree(map->work_buf);
err_map:
        kfree(map);
err:
        return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(__regmap_init);

コメント


トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS