0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/idr.h>
0010 #include <linux/slab.h>
0011 #include <linux/vmalloc.h>
0012
0013 #include "tb.h"
0014
0015 static DEFINE_IDA(nvm_ida);
0016
0017
0018
0019
0020
0021
0022
0023
0024 struct tb_nvm *tb_nvm_alloc(struct device *dev)
0025 {
0026 struct tb_nvm *nvm;
0027 int ret;
0028
0029 nvm = kzalloc(sizeof(*nvm), GFP_KERNEL);
0030 if (!nvm)
0031 return ERR_PTR(-ENOMEM);
0032
0033 ret = ida_simple_get(&nvm_ida, 0, 0, GFP_KERNEL);
0034 if (ret < 0) {
0035 kfree(nvm);
0036 return ERR_PTR(ret);
0037 }
0038
0039 nvm->id = ret;
0040 nvm->dev = dev;
0041
0042 return nvm;
0043 }
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057 int tb_nvm_add_active(struct tb_nvm *nvm, size_t size, nvmem_reg_read_t reg_read)
0058 {
0059 struct nvmem_config config;
0060 struct nvmem_device *nvmem;
0061
0062 memset(&config, 0, sizeof(config));
0063
0064 config.name = "nvm_active";
0065 config.reg_read = reg_read;
0066 config.read_only = true;
0067 config.id = nvm->id;
0068 config.stride = 4;
0069 config.word_size = 4;
0070 config.size = size;
0071 config.dev = nvm->dev;
0072 config.owner = THIS_MODULE;
0073 config.priv = nvm;
0074
0075 nvmem = nvmem_register(&config);
0076 if (IS_ERR(nvmem))
0077 return PTR_ERR(nvmem);
0078
0079 nvm->active = nvmem;
0080 return 0;
0081 }
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094 int tb_nvm_write_buf(struct tb_nvm *nvm, unsigned int offset, void *val,
0095 size_t bytes)
0096 {
0097 if (!nvm->buf) {
0098 nvm->buf = vmalloc(NVM_MAX_SIZE);
0099 if (!nvm->buf)
0100 return -ENOMEM;
0101 }
0102
0103 nvm->flushed = false;
0104 nvm->buf_data_size = offset + bytes;
0105 memcpy(nvm->buf + offset, val, bytes);
0106 return 0;
0107 }
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121 int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size,
0122 nvmem_reg_write_t reg_write)
0123 {
0124 struct nvmem_config config;
0125 struct nvmem_device *nvmem;
0126
0127 memset(&config, 0, sizeof(config));
0128
0129 config.name = "nvm_non_active";
0130 config.reg_write = reg_write;
0131 config.root_only = true;
0132 config.id = nvm->id;
0133 config.stride = 4;
0134 config.word_size = 4;
0135 config.size = size;
0136 config.dev = nvm->dev;
0137 config.owner = THIS_MODULE;
0138 config.priv = nvm;
0139
0140 nvmem = nvmem_register(&config);
0141 if (IS_ERR(nvmem))
0142 return PTR_ERR(nvmem);
0143
0144 nvm->non_active = nvmem;
0145 return 0;
0146 }
0147
0148
0149
0150
0151
0152
0153
0154 void tb_nvm_free(struct tb_nvm *nvm)
0155 {
0156 if (nvm) {
0157 nvmem_unregister(nvm->non_active);
0158 nvmem_unregister(nvm->active);
0159 vfree(nvm->buf);
0160 ida_simple_remove(&nvm_ida, nvm->id);
0161 }
0162 kfree(nvm);
0163 }
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179 int tb_nvm_read_data(unsigned int address, void *buf, size_t size,
0180 unsigned int retries, read_block_fn read_block,
0181 void *read_block_data)
0182 {
0183 do {
0184 unsigned int dwaddress, dwords, offset;
0185 u8 data[NVM_DATA_DWORDS * 4];
0186 size_t nbytes;
0187 int ret;
0188
0189 offset = address & 3;
0190 nbytes = min_t(size_t, size + offset, NVM_DATA_DWORDS * 4);
0191
0192 dwaddress = address / 4;
0193 dwords = ALIGN(nbytes, 4) / 4;
0194
0195 ret = read_block(read_block_data, dwaddress, data, dwords);
0196 if (ret) {
0197 if (ret != -ENODEV && retries--)
0198 continue;
0199 return ret;
0200 }
0201
0202 nbytes -= offset;
0203 memcpy(buf, data + offset, nbytes);
0204
0205 size -= nbytes;
0206 address += nbytes;
0207 buf += nbytes;
0208 } while (size > 0);
0209
0210 return 0;
0211 }
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226 int tb_nvm_write_data(unsigned int address, const void *buf, size_t size,
0227 unsigned int retries, write_block_fn write_block,
0228 void *write_block_data)
0229 {
0230 do {
0231 unsigned int offset, dwaddress;
0232 u8 data[NVM_DATA_DWORDS * 4];
0233 size_t nbytes;
0234 int ret;
0235
0236 offset = address & 3;
0237 nbytes = min_t(u32, size + offset, NVM_DATA_DWORDS * 4);
0238
0239 memcpy(data + offset, buf, nbytes);
0240
0241 dwaddress = address / 4;
0242 ret = write_block(write_block_data, dwaddress, data, nbytes / 4);
0243 if (ret) {
0244 if (ret == -ETIMEDOUT) {
0245 if (retries--)
0246 continue;
0247 ret = -EIO;
0248 }
0249 return ret;
0250 }
0251
0252 size -= nbytes;
0253 address += nbytes;
0254 buf += nbytes;
0255 } while (size > 0);
0256
0257 return 0;
0258 }
0259
0260 void tb_nvm_exit(void)
0261 {
0262 ida_destroy(&nvm_ida);
0263 }