linux/drivers/nvmem/rave-sp-eeprom.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2
   3/*
   4 * EEPROM driver for RAVE SP
   5 *
   6 * Copyright (C) 2018 Zodiac Inflight Innovations
   7 *
   8 */
   9#include <linux/kernel.h>
  10#include <linux/mfd/rave-sp.h>
  11#include <linux/module.h>
  12#include <linux/nvmem-provider.h>
  13#include <linux/of_device.h>
  14#include <linux/platform_device.h>
  15#include <linux/sizes.h>
  16
  17/**
  18 * enum rave_sp_eeprom_access_type - Supported types of EEPROM access
  19 *
  20 * @RAVE_SP_EEPROM_WRITE:       EEPROM write
  21 * @RAVE_SP_EEPROM_READ:        EEPROM read
  22 */
  23enum rave_sp_eeprom_access_type {
  24        RAVE_SP_EEPROM_WRITE = 0,
  25        RAVE_SP_EEPROM_READ  = 1,
  26};
  27
  28/**
  29 * enum rave_sp_eeprom_header_size - EEPROM command header sizes
  30 *
  31 * @RAVE_SP_EEPROM_HEADER_SMALL: EEPROM header size for "small" devices (< 8K)
  32 * @RAVE_SP_EEPROM_HEADER_BIG:   EEPROM header size for "big" devices (> 8K)
  33 */
  34enum rave_sp_eeprom_header_size {
  35        RAVE_SP_EEPROM_HEADER_SMALL = 4U,
  36        RAVE_SP_EEPROM_HEADER_BIG   = 5U,
  37};
  38#define RAVE_SP_EEPROM_HEADER_MAX       RAVE_SP_EEPROM_HEADER_BIG
  39
  40#define RAVE_SP_EEPROM_PAGE_SIZE        32U
  41
  42/**
  43 * struct rave_sp_eeprom_page - RAVE SP EEPROM page
  44 *
  45 * @type:       Access type (see enum rave_sp_eeprom_access_type)
  46 * @success:    Success flag (Success = 1, Failure = 0)
  47 * @data:       Read data
  48
  49 * Note this structure corresponds to RSP_*_EEPROM payload from RAVE
  50 * SP ICD
  51 */
  52struct rave_sp_eeprom_page {
  53        u8  type;
  54        u8  success;
  55        u8  data[RAVE_SP_EEPROM_PAGE_SIZE];
  56} __packed;
  57
  58/**
  59 * struct rave_sp_eeprom - RAVE SP EEPROM device
  60 *
  61 * @sp:                 Pointer to parent RAVE SP device
  62 * @mutex:              Lock protecting access to EEPROM
  63 * @address:            EEPROM device address
  64 * @header_size:        Size of EEPROM command header for this device
  65 * @dev:                Pointer to corresponding struct device used for logging
  66 */
  67struct rave_sp_eeprom {
  68        struct rave_sp *sp;
  69        struct mutex mutex;
  70        u8 address;
  71        unsigned int header_size;
  72        struct device *dev;
  73};
  74
  75/**
  76 * rave_sp_eeprom_io - Low-level part of EEPROM page access
  77 *
  78 * @eeprom:     EEPROM device to write to
  79 * @type:       EEPROM access type (read or write)
  80 * @idx:        number of the EEPROM page
  81 * @page:       Data to write or buffer to store result (via page->data)
  82 *
  83 * This function does all of the low-level work required to perform a
  84 * EEPROM access. This includes formatting correct command payload,
  85 * sending it and checking received results.
  86 *
  87 * Returns zero in case of success or negative error code in
  88 * case of failure.
  89 */
  90static int rave_sp_eeprom_io(struct rave_sp_eeprom *eeprom,
  91                             enum rave_sp_eeprom_access_type type,
  92                             u16 idx,
  93                             struct rave_sp_eeprom_page *page)
  94{
  95        const bool is_write = type == RAVE_SP_EEPROM_WRITE;
  96        const unsigned int data_size = is_write ? sizeof(page->data) : 0;
  97        const unsigned int cmd_size = eeprom->header_size + data_size;
  98        const unsigned int rsp_size =
  99                is_write ? sizeof(*page) - sizeof(page->data) : sizeof(*page);
 100        unsigned int offset = 0;
 101        u8 cmd[RAVE_SP_EEPROM_HEADER_MAX + sizeof(page->data)];
 102        int ret;
 103
 104        if (WARN_ON(cmd_size > sizeof(cmd)))
 105                return -EINVAL;
 106
 107        cmd[offset++] = eeprom->address;
 108        cmd[offset++] = 0;
 109        cmd[offset++] = type;
 110        cmd[offset++] = idx;
 111
 112        /*
 113         * If there's still room in this command's header it means we
 114         * are talkin to EEPROM that uses 16-bit page numbers and we
 115         * have to specify index's MSB in payload as well.
 116         */
 117        if (offset < eeprom->header_size)
 118                cmd[offset++] = idx >> 8;
 119        /*
 120         * Copy our data to write to command buffer first. In case of
 121         * a read data_size should be zero and memcpy would become a
 122         * no-op
 123         */
 124        memcpy(&cmd[offset], page->data, data_size);
 125
 126        ret = rave_sp_exec(eeprom->sp, cmd, cmd_size, page, rsp_size);
 127        if (ret)
 128                return ret;
 129
 130        if (page->type != type)
 131                return -EPROTO;
 132
 133        if (!page->success)
 134                return -EIO;
 135
 136        return 0;
 137}
 138
 139/**
 140 * rave_sp_eeprom_page_access - Access single EEPROM page
 141 *
 142 * @eeprom:     EEPROM device to access
 143 * @type:       Access type to perform (read or write)
 144 * @offset:     Offset within EEPROM to access
 145 * @data:       Data buffer
 146 * @data_len:   Size of the data buffer
 147 *
 148 * This function performs a generic access to a single page or a
 149 * portion thereof. Requested access MUST NOT cross the EEPROM page
 150 * boundary.
 151 *
 152 * Returns zero in case of success or negative error code in
 153 * case of failure.
 154 */
 155static int
 156rave_sp_eeprom_page_access(struct rave_sp_eeprom *eeprom,
 157                           enum rave_sp_eeprom_access_type type,
 158                           unsigned int offset, u8 *data,
 159                           size_t data_len)
 160{
 161        const unsigned int page_offset = offset % RAVE_SP_EEPROM_PAGE_SIZE;
 162        const unsigned int page_nr     = offset / RAVE_SP_EEPROM_PAGE_SIZE;
 163        struct rave_sp_eeprom_page page;
 164        int ret;
 165
 166        /*
 167         * This function will not work if data access we've been asked
 168         * to do is crossing EEPROM page boundary. Normally this
 169         * should never happen and getting here would indicate a bug
 170         * in the code.
 171         */
 172        if (WARN_ON(data_len > sizeof(page.data) - page_offset))
 173                return -EINVAL;
 174
 175        if (type == RAVE_SP_EEPROM_WRITE) {
 176                /*
 177                 * If doing a partial write we need to do a read first
 178                 * to fill the rest of the page with correct data.
 179                 */
 180                if (data_len < RAVE_SP_EEPROM_PAGE_SIZE) {
 181                        ret = rave_sp_eeprom_io(eeprom, RAVE_SP_EEPROM_READ,
 182                                                page_nr, &page);
 183                        if (ret)
 184                                return ret;
 185                }
 186
 187                memcpy(&page.data[page_offset], data, data_len);
 188        }
 189
 190        ret = rave_sp_eeprom_io(eeprom, type, page_nr, &page);
 191        if (ret)
 192                return ret;
 193
 194        /*
 195         * Since we receive the result of the read via 'page.data'
 196         * buffer we need to copy that to 'data'
 197         */
 198        if (type == RAVE_SP_EEPROM_READ)
 199                memcpy(data, &page.data[page_offset], data_len);
 200
 201        return 0;
 202}
 203
 204/**
 205 * rave_sp_eeprom_access - Access EEPROM data
 206 *
 207 * @eeprom:     EEPROM device to access
 208 * @type:       Access type to perform (read or write)
 209 * @offset:     Offset within EEPROM to access
 210 * @data:       Data buffer
 211 * @data_len:   Size of the data buffer
 212 *
 213 * This function performs a generic access (either read or write) at
 214 * arbitrary offset (not necessary page aligned) of arbitrary length
 215 * (is not constrained by EEPROM page size).
 216 *
 217 * Returns zero in case of success or negative error code in case of
 218 * failure.
 219 */
 220static int rave_sp_eeprom_access(struct rave_sp_eeprom *eeprom,
 221                                 enum rave_sp_eeprom_access_type type,
 222                                 unsigned int offset, u8 *data,
 223                                 unsigned int data_len)
 224{
 225        unsigned int residue;
 226        unsigned int chunk;
 227        unsigned int head;
 228        int ret;
 229
 230        mutex_lock(&eeprom->mutex);
 231
 232        head    = offset % RAVE_SP_EEPROM_PAGE_SIZE;
 233        residue = data_len;
 234
 235        do {
 236                /*
 237                 * First iteration, if we are doing an access that is
 238                 * not 32-byte aligned, we need to access only data up
 239                 * to a page boundary to avoid corssing it in
 240                 * rave_sp_eeprom_page_access()
 241                 */
 242                if (unlikely(head)) {
 243                        chunk = RAVE_SP_EEPROM_PAGE_SIZE - head;
 244                        /*
 245                         * This can only happen once per
 246                         * rave_sp_eeprom_access() call, so we set
 247                         * head to zero to process all the other
 248                         * iterations normally.
 249                         */
 250                        head  = 0;
 251                } else {
 252                        chunk = RAVE_SP_EEPROM_PAGE_SIZE;
 253                }
 254
 255                /*
 256                 * We should never read more that 'residue' bytes
 257                 */
 258                chunk = min(chunk, residue);
 259                ret = rave_sp_eeprom_page_access(eeprom, type, offset,
 260                                                 data, chunk);
 261                if (ret)
 262                        goto out;
 263
 264                residue -= chunk;
 265                offset  += chunk;
 266                data    += chunk;
 267        } while (residue);
 268out:
 269        mutex_unlock(&eeprom->mutex);
 270        return ret;
 271}
 272
 273static int rave_sp_eeprom_reg_read(void *eeprom, unsigned int offset,
 274                                   void *val, size_t bytes)
 275{
 276        return rave_sp_eeprom_access(eeprom, RAVE_SP_EEPROM_READ,
 277                                     offset, val, bytes);
 278}
 279
 280static int rave_sp_eeprom_reg_write(void *eeprom, unsigned int offset,
 281                                    void *val, size_t bytes)
 282{
 283        return rave_sp_eeprom_access(eeprom, RAVE_SP_EEPROM_WRITE,
 284                                     offset, val, bytes);
 285}
 286
 287static int rave_sp_eeprom_probe(struct platform_device *pdev)
 288{
 289        struct device *dev = &pdev->dev;
 290        struct rave_sp *sp = dev_get_drvdata(dev->parent);
 291        struct device_node *np = dev->of_node;
 292        struct nvmem_config config = { 0 };
 293        struct rave_sp_eeprom *eeprom;
 294        struct nvmem_device *nvmem;
 295        u32 reg[2], size;
 296
 297        if (of_property_read_u32_array(np, "reg", reg, ARRAY_SIZE(reg))) {
 298                dev_err(dev, "Failed to parse \"reg\" property\n");
 299                return -EINVAL;
 300        }
 301
 302        size = reg[1];
 303        /*
 304         * Per ICD, we have no more than 2 bytes to specify EEPROM
 305         * page.
 306         */
 307        if (size > U16_MAX * RAVE_SP_EEPROM_PAGE_SIZE) {
 308                dev_err(dev, "Specified size is too big\n");
 309                return -EINVAL;
 310        }
 311
 312        eeprom = devm_kzalloc(dev, sizeof(*eeprom), GFP_KERNEL);
 313        if (!eeprom)
 314                return -ENOMEM;
 315
 316        eeprom->address = reg[0];
 317        eeprom->sp      = sp;
 318        eeprom->dev     = dev;
 319
 320        if (size > SZ_8K)
 321                eeprom->header_size = RAVE_SP_EEPROM_HEADER_BIG;
 322        else
 323                eeprom->header_size = RAVE_SP_EEPROM_HEADER_SMALL;
 324
 325        mutex_init(&eeprom->mutex);
 326
 327        config.id               = -1;
 328        of_property_read_string(np, "zii,eeprom-name", &config.name);
 329        config.priv             = eeprom;
 330        config.dev              = dev;
 331        config.size             = size;
 332        config.reg_read         = rave_sp_eeprom_reg_read;
 333        config.reg_write        = rave_sp_eeprom_reg_write;
 334        config.word_size        = 1;
 335        config.stride           = 1;
 336
 337        nvmem = devm_nvmem_register(dev, &config);
 338
 339        return PTR_ERR_OR_ZERO(nvmem);
 340}
 341
 342static const struct of_device_id rave_sp_eeprom_of_match[] = {
 343        { .compatible = "zii,rave-sp-eeprom" },
 344        {}
 345};
 346MODULE_DEVICE_TABLE(of, rave_sp_eeprom_of_match);
 347
 348static struct platform_driver rave_sp_eeprom_driver = {
 349        .probe = rave_sp_eeprom_probe,
 350        .driver = {
 351                .name = KBUILD_MODNAME,
 352                .of_match_table = rave_sp_eeprom_of_match,
 353        },
 354};
 355module_platform_driver(rave_sp_eeprom_driver);
 356
 357MODULE_LICENSE("GPL");
 358MODULE_AUTHOR("Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>");
 359MODULE_AUTHOR("Nikita Yushchenko <nikita.yoush@cogentembedded.com>");
 360MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
 361MODULE_DESCRIPTION("RAVE SP EEPROM driver");
 362