linux/drivers/thunderbolt/nvm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * NVM helpers
   4 *
   5 * Copyright (C) 2020, Intel Corporation
   6 * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
   7 */
   8
   9#include <linux/idr.h>
  10#include <linux/slab.h>
  11#include <linux/vmalloc.h>
  12
  13#include "tb.h"
  14
  15static DEFINE_IDA(nvm_ida);
  16
  17/**
  18 * tb_nvm_alloc() - Allocate new NVM structure
  19 * @dev: Device owning the NVM
  20 *
  21 * Allocates new NVM structure with unique @id and returns it. In case
  22 * of error returns ERR_PTR().
  23 */
  24struct tb_nvm *tb_nvm_alloc(struct device *dev)
  25{
  26        struct tb_nvm *nvm;
  27        int ret;
  28
  29        nvm = kzalloc(sizeof(*nvm), GFP_KERNEL);
  30        if (!nvm)
  31                return ERR_PTR(-ENOMEM);
  32
  33        ret = ida_simple_get(&nvm_ida, 0, 0, GFP_KERNEL);
  34        if (ret < 0) {
  35                kfree(nvm);
  36                return ERR_PTR(ret);
  37        }
  38
  39        nvm->id = ret;
  40        nvm->dev = dev;
  41
  42        return nvm;
  43}
  44
  45/**
  46 * tb_nvm_add_active() - Adds active NVMem device to NVM
  47 * @nvm: NVM structure
  48 * @size: Size of the active NVM in bytes
  49 * @reg_read: Pointer to the function to read the NVM (passed directly to the
  50 *            NVMem device)
  51 *
  52 * Registers new active NVmem device for @nvm. The @reg_read is called
  53 * directly from NVMem so it must handle possible concurrent access if
  54 * needed. The first parameter passed to @reg_read is @nvm structure.
  55 * Returns %0 in success and negative errno otherwise.
  56 */
  57int tb_nvm_add_active(struct tb_nvm *nvm, size_t size, nvmem_reg_read_t reg_read)
  58{
  59        struct nvmem_config config;
  60        struct nvmem_device *nvmem;
  61
  62        memset(&config, 0, sizeof(config));
  63
  64        config.name = "nvm_active";
  65        config.reg_read = reg_read;
  66        config.read_only = true;
  67        config.id = nvm->id;
  68        config.stride = 4;
  69        config.word_size = 4;
  70        config.size = size;
  71        config.dev = nvm->dev;
  72        config.owner = THIS_MODULE;
  73        config.priv = nvm;
  74
  75        nvmem = nvmem_register(&config);
  76        if (IS_ERR(nvmem))
  77                return PTR_ERR(nvmem);
  78
  79        nvm->active = nvmem;
  80        return 0;
  81}
  82
  83/**
  84 * tb_nvm_write_buf() - Write data to @nvm buffer
  85 * @nvm: NVM structure
  86 * @offset: Offset where to write the data
  87 * @val: Data buffer to write
  88 * @bytes: Number of bytes to write
  89 *
  90 * Helper function to cache the new NVM image before it is actually
  91 * written to the flash. Copies @bytes from @val to @nvm->buf starting
  92 * from @offset.
  93 */
  94int tb_nvm_write_buf(struct tb_nvm *nvm, unsigned int offset, void *val,
  95                     size_t bytes)
  96{
  97        if (!nvm->buf) {
  98                nvm->buf = vmalloc(NVM_MAX_SIZE);
  99                if (!nvm->buf)
 100                        return -ENOMEM;
 101        }
 102
 103        nvm->flushed = false;
 104        nvm->buf_data_size = offset + bytes;
 105        memcpy(nvm->buf + offset, val, bytes);
 106        return 0;
 107}
 108
 109/**
 110 * tb_nvm_add_non_active() - Adds non-active NVMem device to NVM
 111 * @nvm: NVM structure
 112 * @size: Size of the non-active NVM in bytes
 113 * @reg_write: Pointer to the function to write the NVM (passed directly
 114 *             to the NVMem device)
 115 *
 116 * Registers new non-active NVmem device for @nvm. The @reg_write is called
 117 * directly from NVMem so it must handle possible concurrent access if
 118 * needed. The first parameter passed to @reg_write is @nvm structure.
 119 * Returns %0 in success and negative errno otherwise.
 120 */
 121int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size,
 122                          nvmem_reg_write_t reg_write)
 123{
 124        struct nvmem_config config;
 125        struct nvmem_device *nvmem;
 126
 127        memset(&config, 0, sizeof(config));
 128
 129        config.name = "nvm_non_active";
 130        config.reg_write = reg_write;
 131        config.root_only = true;
 132        config.id = nvm->id;
 133        config.stride = 4;
 134        config.word_size = 4;
 135        config.size = size;
 136        config.dev = nvm->dev;
 137        config.owner = THIS_MODULE;
 138        config.priv = nvm;
 139
 140        nvmem = nvmem_register(&config);
 141        if (IS_ERR(nvmem))
 142                return PTR_ERR(nvmem);
 143
 144        nvm->non_active = nvmem;
 145        return 0;
 146}
 147
 148/**
 149 * tb_nvm_free() - Release NVM and its resources
 150 * @nvm: NVM structure to release
 151 *
 152 * Releases NVM and the NVMem devices if they were registered.
 153 */
 154void tb_nvm_free(struct tb_nvm *nvm)
 155{
 156        if (nvm) {
 157                if (nvm->non_active)
 158                        nvmem_unregister(nvm->non_active);
 159                if (nvm->active)
 160                        nvmem_unregister(nvm->active);
 161                vfree(nvm->buf);
 162                ida_simple_remove(&nvm_ida, nvm->id);
 163        }
 164        kfree(nvm);
 165}
 166
 167void tb_nvm_exit(void)
 168{
 169        ida_destroy(&nvm_ida);
 170}
 171