uboot/board/kontron/sl28/cmds.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * sl28 extension commands
   4 *
   5 * Copyright (c) 2020 Kontron Europe GmbH
   6 */
   7
   8#include <common.h>
   9#include <command.h>
  10#include <i2c.h>
  11#include <linux/delay.h>
  12
  13#define CPLD_I2C_ADDR 0x4a
  14#define REG_UFM_CTRL 0x02
  15#define   UFM_CTRL_DCLK    BIT(1)
  16#define   UFM_CTRL_DIN     BIT(2)
  17#define   UFM_CTRL_PROGRAM BIT(3)
  18#define   UFM_CTRL_ERASE   BIT(4)
  19#define   UFM_CTRL_DSHIFT  BIT(5)
  20#define   UFM_CTRL_DOUT    BIT(6)
  21#define   UFM_CTRL_BUSY    BIT(7)
  22
  23static int ufm_shift_data(struct udevice *dev, u16 data_in, u16 *data_out)
  24{
  25        int i;
  26        int ret;
  27        u16 data = 0;
  28
  29        /* latch data */
  30        ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, 0);
  31        if (ret < 0)
  32                return ret;
  33        ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, UFM_CTRL_DCLK);
  34        if (ret < 0)
  35                return ret;
  36
  37        /* assert drshift */
  38        ret = dm_i2c_reg_write(dev, REG_UFM_CTRL,
  39                               UFM_CTRL_DSHIFT | UFM_CTRL_DCLK);
  40        if (ret < 0)
  41                return ret;
  42
  43        /* clock 16 data bits, reverse order */
  44        for (i = 15; i >= 0; i--) {
  45                u8 din = (data_in & (1 << i)) ? UFM_CTRL_DIN : 0;
  46
  47                ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, UFM_CTRL_DSHIFT
  48                                | din);
  49                if (ret < 0)
  50                        return ret;
  51                if (data_out) {
  52                        ret = dm_i2c_reg_read(dev, REG_UFM_CTRL);
  53                        if (ret < 0)
  54                                return ret;
  55                        if (ret & UFM_CTRL_DOUT)
  56                                data |= (1 << i);
  57                }
  58                ret = dm_i2c_reg_write(dev, REG_UFM_CTRL,
  59                                       UFM_CTRL_DSHIFT | UFM_CTRL_DCLK | din);
  60                if (ret < 0)
  61                        return ret;
  62        }
  63
  64        /* deassert drshift */
  65        ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, UFM_CTRL_DCLK);
  66        if (ret < 0)
  67                return ret;
  68
  69        if (data_out)
  70                *data_out = data;
  71
  72        return ret;
  73}
  74
  75static int ufm_erase(struct udevice *dev)
  76{
  77        int ret;
  78
  79        /* erase, tEPMX is 500ms */
  80        ret = dm_i2c_reg_write(dev, REG_UFM_CTRL,
  81                               UFM_CTRL_DCLK | UFM_CTRL_ERASE);
  82        if (ret < 0)
  83                return ret;
  84        ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, UFM_CTRL_DCLK);
  85        if (ret < 0)
  86                return ret;
  87        mdelay(500);
  88
  89        return 0;
  90}
  91
  92static int ufm_program(struct udevice *dev)
  93{
  94        int ret;
  95
  96        /* program, tPPMX is 100us */
  97        ret = dm_i2c_reg_write(dev, REG_UFM_CTRL,
  98                               UFM_CTRL_DCLK | UFM_CTRL_PROGRAM);
  99        if (ret < 0)
 100                return ret;
 101        ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, UFM_CTRL_DCLK);
 102        if (ret < 0)
 103                return ret;
 104        udelay(100);
 105
 106        return 0;
 107}
 108
 109static int ufm_write(struct udevice *dev, u16 data)
 110{
 111        int ret;
 112
 113        ret = ufm_shift_data(dev, data, NULL);
 114        if (ret < 0)
 115                return ret;
 116
 117        ret = ufm_erase(dev);
 118        if (ret < 0)
 119                return ret;
 120
 121        return ufm_program(dev);
 122}
 123
 124static int ufm_read(struct udevice *dev, u16 *data)
 125{
 126        return ufm_shift_data(dev, 0, data);
 127}
 128
 129static int do_sl28_nvm(struct cmd_tbl *cmdtp, int flag, int argc,
 130                       char *const argv[])
 131{
 132        struct udevice *dev;
 133        u16 nvm;
 134        int ret;
 135        char *endp;
 136
 137        if (i2c_get_chip_for_busnum(0, CPLD_I2C_ADDR, 1, &dev))
 138                return CMD_RET_FAILURE;
 139
 140        if (argc > 1) {
 141                nvm = simple_strtoul(argv[1], &endp, 16);
 142                if (*endp != '\0') {
 143                        printf("ERROR: argument is not a valid number\n");
 144                        ret = -EINVAL;
 145                        goto out;
 146                }
 147
 148                /*
 149                 * We swap all bits, because the a zero bit in hardware means the
 150                 * feature is enabled. But this is hard for the user.
 151                 */
 152                nvm ^= 0xffff;
 153
 154                ret = ufm_write(dev, nvm);
 155                if (ret)
 156                        goto out;
 157                printf("New settings will be activated after the next power cycle!\n");
 158        } else {
 159                ret = ufm_read(dev, &nvm);
 160                if (ret)
 161                        goto out;
 162                nvm ^= 0xffff;
 163
 164                printf("%04hx\n", nvm);
 165        }
 166
 167        return CMD_RET_SUCCESS;
 168
 169out:
 170        printf("command failed (%d)\n", ret);
 171        return CMD_RET_FAILURE;
 172}
 173
 174static char sl28_help_text[] =
 175        "nvm [<hex>] - display/set the 16 non-volatile bits\n";
 176
 177U_BOOT_CMD_WITH_SUBCMDS(sl28, "SMARC-sAL28 specific", sl28_help_text,
 178                        U_BOOT_SUBCMD_MKENT(nvm, 2, 1, do_sl28_nvm));
 179