uboot/board/CZ.NIC/turris_omnia/turris_omnia.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2017 Marek Behun <marek.behun@nic.cz>
   4 * Copyright (C) 2016 Tomas Hlavacek <tomas.hlavacek@nic.cz>
   5 *
   6 * Derived from the code for
   7 *   Marvell/db-88f6820-gp by Stefan Roese <sr@denx.de>
   8 */
   9
  10#include <common.h>
  11#include <env.h>
  12#include <i2c.h>
  13#include <init.h>
  14#include <log.h>
  15#include <miiphy.h>
  16#include <mtd.h>
  17#include <net.h>
  18#include <netdev.h>
  19#include <asm/global_data.h>
  20#include <asm/io.h>
  21#include <asm/arch/cpu.h>
  22#include <asm/arch/soc.h>
  23#include <dm/uclass.h>
  24#include <fdt_support.h>
  25#include <time.h>
  26#include <linux/bitops.h>
  27#include <u-boot/crc.h>
  28# include <atsha204a-i2c.h>
  29
  30#include "../drivers/ddr/marvell/a38x/ddr3_init.h"
  31#include <../serdes/a38x/high_speed_env_spec.h>
  32
  33DECLARE_GLOBAL_DATA_PTR;
  34
  35#define OMNIA_SPI_NOR_PATH              "/soc/spi@10600/spi-nor@0"
  36
  37#define OMNIA_I2C_BUS_NAME              "i2c@11000->i2cmux@70->i2c@0"
  38
  39#define OMNIA_I2C_MCU_CHIP_ADDR         0x2a
  40#define OMNIA_I2C_MCU_CHIP_LEN          1
  41
  42#define OMNIA_I2C_EEPROM_CHIP_ADDR      0x54
  43#define OMNIA_I2C_EEPROM_CHIP_LEN       2
  44#define OMNIA_I2C_EEPROM_MAGIC          0x0341a034
  45
  46enum mcu_commands {
  47        CMD_GET_STATUS_WORD     = 0x01,
  48        CMD_GET_RESET           = 0x09,
  49        CMD_WATCHDOG_STATE      = 0x0b,
  50};
  51
  52enum status_word_bits {
  53        CARD_DET_STSBIT         = 0x0010,
  54        MSATA_IND_STSBIT        = 0x0020,
  55};
  56
  57#define OMNIA_ATSHA204_OTP_VERSION      0
  58#define OMNIA_ATSHA204_OTP_SERIAL       1
  59#define OMNIA_ATSHA204_OTP_MAC0         3
  60#define OMNIA_ATSHA204_OTP_MAC1         4
  61
  62/*
  63 * Those values and defines are taken from the Marvell U-Boot version
  64 * "u-boot-2013.01-2014_T3.0"
  65 */
  66#define OMNIA_GPP_OUT_ENA_LOW                                   \
  67        (~(BIT(1)  | BIT(4)  | BIT(6)  | BIT(7)  | BIT(8)  | BIT(9)  |  \
  68           BIT(10) | BIT(11) | BIT(19) | BIT(22) | BIT(23) | BIT(25) |  \
  69           BIT(26) | BIT(27) | BIT(29) | BIT(30) | BIT(31)))
  70#define OMNIA_GPP_OUT_ENA_MID                                   \
  71        (~(BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(15) |       \
  72           BIT(16) | BIT(17) | BIT(18)))
  73
  74#define OMNIA_GPP_OUT_VAL_LOW   0x0
  75#define OMNIA_GPP_OUT_VAL_MID   0x0
  76#define OMNIA_GPP_POL_LOW       0x0
  77#define OMNIA_GPP_POL_MID       0x0
  78
  79static struct serdes_map board_serdes_map_pex[] = {
  80        {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0},
  81        {USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0},
  82        {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0},
  83        {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0},
  84        {PEX2, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0},
  85        {SGMII2, SERDES_SPEED_1_25_GBPS, SERDES_DEFAULT_MODE, 0, 0}
  86};
  87
  88static struct serdes_map board_serdes_map_sata[] = {
  89        {SATA0, SERDES_SPEED_6_GBPS, SERDES_DEFAULT_MODE, 0, 0},
  90        {USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0},
  91        {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0},
  92        {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0},
  93        {PEX2, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0},
  94        {SGMII2, SERDES_SPEED_1_25_GBPS, SERDES_DEFAULT_MODE, 0, 0}
  95};
  96
  97static struct udevice *omnia_get_i2c_chip(const char *name, uint addr,
  98                                          uint offset_len)
  99{
 100        struct udevice *bus, *dev;
 101        int ret;
 102
 103        ret = uclass_get_device_by_name(UCLASS_I2C, OMNIA_I2C_BUS_NAME, &bus);
 104        if (ret) {
 105                printf("Cannot get I2C bus %s: uclass_get_device_by_name failed: %i\n",
 106                       OMNIA_I2C_BUS_NAME, ret);
 107                return NULL;
 108        }
 109
 110        ret = i2c_get_chip(bus, addr, offset_len, &dev);
 111        if (ret) {
 112                printf("Cannot get %s I2C chip: i2c_get_chip failed: %i\n",
 113                       name, ret);
 114                return NULL;
 115        }
 116
 117        return dev;
 118}
 119
 120static int omnia_mcu_read(u8 cmd, void *buf, int len)
 121{
 122        struct udevice *chip;
 123
 124        chip = omnia_get_i2c_chip("MCU", OMNIA_I2C_MCU_CHIP_ADDR,
 125                                  OMNIA_I2C_MCU_CHIP_LEN);
 126        if (!chip)
 127                return -ENODEV;
 128
 129        return dm_i2c_read(chip, cmd, buf, len);
 130}
 131
 132static int omnia_mcu_write(u8 cmd, const void *buf, int len)
 133{
 134        struct udevice *chip;
 135
 136        chip = omnia_get_i2c_chip("MCU", OMNIA_I2C_MCU_CHIP_ADDR,
 137                                  OMNIA_I2C_MCU_CHIP_LEN);
 138        if (!chip)
 139                return -ENODEV;
 140
 141        return dm_i2c_write(chip, cmd, buf, len);
 142}
 143
 144static bool disable_mcu_watchdog(void)
 145{
 146        int ret;
 147
 148        puts("Disabling MCU watchdog... ");
 149
 150        ret = omnia_mcu_write(CMD_WATCHDOG_STATE, "\x00", 1);
 151        if (ret) {
 152                printf("omnia_mcu_write failed: %i\n", ret);
 153                return false;
 154        }
 155
 156        puts("disabled\n");
 157
 158        return true;
 159}
 160
 161static bool omnia_detect_sata(void)
 162{
 163        int ret;
 164        u16 stsword;
 165
 166        puts("MiniPCIe/mSATA card detection... ");
 167
 168        ret = omnia_mcu_read(CMD_GET_STATUS_WORD, &stsword, sizeof(stsword));
 169        if (ret) {
 170                printf("omnia_mcu_read failed: %i, defaulting to MiniPCIe card\n",
 171                       ret);
 172                return false;
 173        }
 174
 175        if (!(stsword & CARD_DET_STSBIT)) {
 176                puts("none\n");
 177                return false;
 178        }
 179
 180        if (stsword & MSATA_IND_STSBIT)
 181                puts("mSATA\n");
 182        else
 183                puts("MiniPCIe\n");
 184
 185        return stsword & MSATA_IND_STSBIT ? true : false;
 186}
 187
 188int hws_board_topology_load(struct serdes_map **serdes_map_array, u8 *count)
 189{
 190        if (omnia_detect_sata()) {
 191                *serdes_map_array = board_serdes_map_sata;
 192                *count = ARRAY_SIZE(board_serdes_map_sata);
 193        } else {
 194                *serdes_map_array = board_serdes_map_pex;
 195                *count = ARRAY_SIZE(board_serdes_map_pex);
 196        }
 197
 198        return 0;
 199}
 200
 201struct omnia_eeprom {
 202        u32 magic;
 203        u32 ramsize;
 204        char region[4];
 205        u32 crc;
 206};
 207
 208static bool omnia_read_eeprom(struct omnia_eeprom *oep)
 209{
 210        struct udevice *chip;
 211        u32 crc;
 212        int ret;
 213
 214        chip = omnia_get_i2c_chip("EEPROM", OMNIA_I2C_EEPROM_CHIP_ADDR,
 215                                  OMNIA_I2C_EEPROM_CHIP_LEN);
 216
 217        if (!chip)
 218                return false;
 219
 220        ret = dm_i2c_read(chip, 0, (void *)oep, sizeof(*oep));
 221        if (ret) {
 222                printf("dm_i2c_read failed: %i, cannot read EEPROM\n", ret);
 223                return false;
 224        }
 225
 226        if (oep->magic != OMNIA_I2C_EEPROM_MAGIC) {
 227                printf("bad EEPROM magic number (%08x, should be %08x)\n",
 228                       oep->magic, OMNIA_I2C_EEPROM_MAGIC);
 229                return false;
 230        }
 231
 232        crc = crc32(0, (void *)oep, sizeof(*oep) - 4);
 233        if (crc != oep->crc) {
 234                printf("bad EEPROM CRC (stored %08x, computed %08x)\n",
 235                       oep->crc, crc);
 236                return false;
 237        }
 238
 239        return true;
 240}
 241
 242static int omnia_get_ram_size_gb(void)
 243{
 244        static int ram_size;
 245        struct omnia_eeprom oep;
 246
 247        if (!ram_size) {
 248                /* Get the board config from EEPROM */
 249                if (omnia_read_eeprom(&oep)) {
 250                        debug("Memory config in EEPROM: 0x%02x\n", oep.ramsize);
 251
 252                        if (oep.ramsize == 0x2)
 253                                ram_size = 2;
 254                        else
 255                                ram_size = 1;
 256                } else {
 257                        /* Hardcoded fallback */
 258                        puts("Memory config from EEPROM read failed!\n");
 259                        puts("Falling back to default 1 GiB!\n");
 260                        ram_size = 1;
 261                }
 262        }
 263
 264        return ram_size;
 265}
 266
 267/*
 268 * Define the DDR layout / topology here in the board file. This will
 269 * be used by the DDR3 init code in the SPL U-Boot version to configure
 270 * the DDR3 controller.
 271 */
 272static struct mv_ddr_topology_map board_topology_map_1g = {
 273        DEBUG_LEVEL_ERROR,
 274        0x1, /* active interfaces */
 275        /* cs_mask, mirror, dqs_swap, ck_swap X PUPs */
 276        { { { {0x1, 0, 0, 0},
 277              {0x1, 0, 0, 0},
 278              {0x1, 0, 0, 0},
 279              {0x1, 0, 0, 0},
 280              {0x1, 0, 0, 0} },
 281            SPEED_BIN_DDR_1600K,        /* speed_bin */
 282            MV_DDR_DEV_WIDTH_16BIT,     /* memory_width */
 283            MV_DDR_DIE_CAP_4GBIT,                       /* mem_size */
 284            MV_DDR_FREQ_800,            /* frequency */
 285            0, 0,                       /* cas_wl cas_l */
 286            MV_DDR_TEMP_NORMAL,         /* temperature */
 287            MV_DDR_TIM_2T} },           /* timing */
 288        BUS_MASK_32BIT,                 /* Busses mask */
 289        MV_DDR_CFG_DEFAULT,             /* ddr configuration data source */
 290        NOT_COMBINED,                   /* ddr twin-die combined */
 291        { {0} },                        /* raw spd data */
 292        {0}                             /* timing parameters */
 293};
 294
 295static struct mv_ddr_topology_map board_topology_map_2g = {
 296        DEBUG_LEVEL_ERROR,
 297        0x1, /* active interfaces */
 298        /* cs_mask, mirror, dqs_swap, ck_swap X PUPs */
 299        { { { {0x1, 0, 0, 0},
 300              {0x1, 0, 0, 0},
 301              {0x1, 0, 0, 0},
 302              {0x1, 0, 0, 0},
 303              {0x1, 0, 0, 0} },
 304            SPEED_BIN_DDR_1600K,        /* speed_bin */
 305            MV_DDR_DEV_WIDTH_16BIT,     /* memory_width */
 306            MV_DDR_DIE_CAP_8GBIT,                       /* mem_size */
 307            MV_DDR_FREQ_800,            /* frequency */
 308            0, 0,                       /* cas_wl cas_l */
 309            MV_DDR_TEMP_NORMAL,         /* temperature */
 310            MV_DDR_TIM_2T} },           /* timing */
 311        BUS_MASK_32BIT,                 /* Busses mask */
 312        MV_DDR_CFG_DEFAULT,             /* ddr configuration data source */
 313        NOT_COMBINED,                   /* ddr twin-die combined */
 314        { {0} },                        /* raw spd data */
 315        {0}                             /* timing parameters */
 316};
 317
 318struct mv_ddr_topology_map *mv_ddr_topology_map_get(void)
 319{
 320        if (omnia_get_ram_size_gb() == 2)
 321                return &board_topology_map_2g;
 322        else
 323                return &board_topology_map_1g;
 324}
 325
 326static int set_regdomain(void)
 327{
 328        struct omnia_eeprom oep;
 329        char rd[3] = {' ', ' ', 0};
 330
 331        if (omnia_read_eeprom(&oep))
 332                memcpy(rd, &oep.region, 2);
 333        else
 334                puts("EEPROM regdomain read failed.\n");
 335
 336        printf("Regdomain set to %s\n", rd);
 337        return env_set("regdomain", rd);
 338}
 339
 340static void handle_reset_button(void)
 341{
 342        const char * const vars[1] = { "bootcmd_rescue", };
 343        int ret;
 344        u8 reset_status;
 345
 346        /*
 347         * Ensure that bootcmd_rescue has always stock value, so that running
 348         *   run bootcmd_rescue
 349         * always works correctly.
 350         */
 351        env_set_default_vars(1, (char * const *)vars, 0);
 352
 353        ret = omnia_mcu_read(CMD_GET_RESET, &reset_status, 1);
 354        if (ret) {
 355                printf("omnia_mcu_read failed: %i, reset status unknown!\n",
 356                       ret);
 357                return;
 358        }
 359
 360        env_set_ulong("omnia_reset", reset_status);
 361
 362        if (reset_status) {
 363                const char * const vars[2] = {
 364                        "bootcmd",
 365                        "distro_bootcmd",
 366                };
 367
 368                /*
 369                 * Set the above envs to their default values, in case the user
 370                 * managed to break them.
 371                 */
 372                env_set_default_vars(2, (char * const *)vars, 0);
 373
 374                /* Ensure bootcmd_rescue is used by distroboot */
 375                env_set("boot_targets", "rescue");
 376
 377                printf("RESET button was pressed, overwriting bootcmd!\n");
 378        } else {
 379                /*
 380                 * In case the user somehow managed to save environment with
 381                 * boot_targets=rescue, reset boot_targets to default value.
 382                 * This could happen in subsequent commands if bootcmd_rescue
 383                 * failed.
 384                 */
 385                if (!strcmp(env_get("boot_targets"), "rescue")) {
 386                        const char * const vars[1] = {
 387                                "boot_targets",
 388                        };
 389
 390                        env_set_default_vars(1, (char * const *)vars, 0);
 391                }
 392        }
 393}
 394
 395int board_early_init_f(void)
 396{
 397        /* Configure MPP */
 398        writel(0x11111111, MVEBU_MPP_BASE + 0x00);
 399        writel(0x11111111, MVEBU_MPP_BASE + 0x04);
 400        writel(0x11244011, MVEBU_MPP_BASE + 0x08);
 401        writel(0x22222111, MVEBU_MPP_BASE + 0x0c);
 402        writel(0x22200002, MVEBU_MPP_BASE + 0x10);
 403        writel(0x30042022, MVEBU_MPP_BASE + 0x14);
 404        writel(0x55550555, MVEBU_MPP_BASE + 0x18);
 405        writel(0x00005550, MVEBU_MPP_BASE + 0x1c);
 406
 407        /* Set GPP Out value */
 408        writel(OMNIA_GPP_OUT_VAL_LOW, MVEBU_GPIO0_BASE + 0x00);
 409        writel(OMNIA_GPP_OUT_VAL_MID, MVEBU_GPIO1_BASE + 0x00);
 410
 411        /* Set GPP Polarity */
 412        writel(OMNIA_GPP_POL_LOW, MVEBU_GPIO0_BASE + 0x0c);
 413        writel(OMNIA_GPP_POL_MID, MVEBU_GPIO1_BASE + 0x0c);
 414
 415        /* Set GPP Out Enable */
 416        writel(OMNIA_GPP_OUT_ENA_LOW, MVEBU_GPIO0_BASE + 0x04);
 417        writel(OMNIA_GPP_OUT_ENA_MID, MVEBU_GPIO1_BASE + 0x04);
 418
 419        return 0;
 420}
 421
 422void spl_board_init(void)
 423{
 424        /*
 425         * If booting from UART, disable MCU watchdog in SPL, since uploading
 426         * U-Boot proper can take too much time and trigger it.
 427         */
 428        if (get_boot_device() == BOOT_DEVICE_UART)
 429                disable_mcu_watchdog();
 430}
 431
 432int board_init(void)
 433{
 434        /* address of boot parameters */
 435        gd->bd->bi_boot_params = mvebu_sdram_bar(0) + 0x100;
 436
 437        return 0;
 438}
 439
 440int board_late_init(void)
 441{
 442        /*
 443         * If not booting from UART, MCU watchdog was not disabled in SPL,
 444         * disable it now.
 445         */
 446        if (get_boot_device() != BOOT_DEVICE_UART)
 447                disable_mcu_watchdog();
 448
 449        set_regdomain();
 450        handle_reset_button();
 451        pci_init();
 452
 453        return 0;
 454}
 455
 456static struct udevice *get_atsha204a_dev(void)
 457{
 458        static struct udevice *dev;
 459
 460        if (dev)
 461                return dev;
 462
 463        if (uclass_get_device_by_name(UCLASS_MISC, "atsha204a@64", &dev)) {
 464                puts("Cannot find ATSHA204A on I2C bus!\n");
 465                dev = NULL;
 466        }
 467
 468        return dev;
 469}
 470
 471int checkboard(void)
 472{
 473        u32 version_num, serial_num;
 474        int err = 1;
 475
 476        struct udevice *dev = get_atsha204a_dev();
 477
 478        if (dev) {
 479                err = atsha204a_wakeup(dev);
 480                if (err)
 481                        goto out;
 482
 483                err = atsha204a_read(dev, ATSHA204A_ZONE_OTP, false,
 484                                     OMNIA_ATSHA204_OTP_VERSION,
 485                                     (u8 *)&version_num);
 486                if (err)
 487                        goto out;
 488
 489                err = atsha204a_read(dev, ATSHA204A_ZONE_OTP, false,
 490                                     OMNIA_ATSHA204_OTP_SERIAL,
 491                                     (u8 *)&serial_num);
 492                if (err)
 493                        goto out;
 494
 495                atsha204a_sleep(dev);
 496        }
 497
 498out:
 499        printf("Turris Omnia:\n");
 500        printf("  RAM size: %i MiB\n", omnia_get_ram_size_gb() * 1024);
 501        if (err)
 502                printf("  Serial Number: unknown\n");
 503        else
 504                printf("  Serial Number: %08X%08X\n", be32_to_cpu(version_num),
 505                       be32_to_cpu(serial_num));
 506
 507        return 0;
 508}
 509
 510static void increment_mac(u8 *mac)
 511{
 512        int i;
 513
 514        for (i = 5; i >= 3; i--) {
 515                mac[i] += 1;
 516                if (mac[i])
 517                        break;
 518        }
 519}
 520
 521int misc_init_r(void)
 522{
 523        int err;
 524        struct udevice *dev = get_atsha204a_dev();
 525        u8 mac0[4], mac1[4], mac[6];
 526
 527        if (!dev)
 528                goto out;
 529
 530        err = atsha204a_wakeup(dev);
 531        if (err)
 532                goto out;
 533
 534        err = atsha204a_read(dev, ATSHA204A_ZONE_OTP, false,
 535                             OMNIA_ATSHA204_OTP_MAC0, mac0);
 536        if (err)
 537                goto out;
 538
 539        err = atsha204a_read(dev, ATSHA204A_ZONE_OTP, false,
 540                             OMNIA_ATSHA204_OTP_MAC1, mac1);
 541        if (err)
 542                goto out;
 543
 544        atsha204a_sleep(dev);
 545
 546        mac[0] = mac0[1];
 547        mac[1] = mac0[2];
 548        mac[2] = mac0[3];
 549        mac[3] = mac1[1];
 550        mac[4] = mac1[2];
 551        mac[5] = mac1[3];
 552
 553        if (is_valid_ethaddr(mac))
 554                eth_env_set_enetaddr("eth1addr", mac);
 555
 556        increment_mac(mac);
 557
 558        if (is_valid_ethaddr(mac))
 559                eth_env_set_enetaddr("eth2addr", mac);
 560
 561        increment_mac(mac);
 562
 563        if (is_valid_ethaddr(mac))
 564                eth_env_set_enetaddr("ethaddr", mac);
 565
 566out:
 567        return 0;
 568}
 569
 570#if defined(CONFIG_OF_BOARD_SETUP)
 571/*
 572 * I plan to generalize this function and move it to common/fdt_support.c.
 573 * This will require some more work on multiple boards, though, so for now leave
 574 * it here.
 575 */
 576static bool fixup_mtd_partitions(void *blob, int offset, struct mtd_info *mtd)
 577{
 578        struct mtd_info *slave;
 579        int parts;
 580
 581        parts = fdt_subnode_offset(blob, offset, "partitions");
 582        if (parts < 0)
 583                return false;
 584
 585        if (fdt_del_node(blob, parts) < 0)
 586                return false;
 587
 588        parts = fdt_add_subnode(blob, offset, "partitions");
 589        if (parts < 0)
 590                return false;
 591
 592        if (fdt_setprop_u32(blob, parts, "#address-cells", 1) < 0)
 593                return false;
 594
 595        if (fdt_setprop_u32(blob, parts, "#size-cells", 1) < 0)
 596                return false;
 597
 598        if (fdt_setprop_string(blob, parts, "compatible",
 599                               "fixed-partitions") < 0)
 600                return false;
 601
 602        mtd_probe_devices();
 603
 604        list_for_each_entry(slave, &mtd->partitions, node) {
 605                char name[32];
 606                int part;
 607
 608                snprintf(name, sizeof(name), "partition@%llx", slave->offset);
 609                part = fdt_add_subnode(blob, parts, name);
 610                if (part < 0)
 611                        return false;
 612
 613                if (fdt_setprop_u32(blob, part, "reg", slave->offset) < 0)
 614                        return false;
 615
 616                if (fdt_appendprop_u32(blob, part, "reg", slave->size) < 0)
 617                        return false;
 618
 619                if (fdt_setprop_string(blob, part, "label", slave->name) < 0)
 620                        return false;
 621
 622                if (!(slave->flags & MTD_WRITEABLE))
 623                        if (fdt_setprop_empty(blob, part, "read-only") < 0)
 624                                return false;
 625
 626                if (slave->flags & MTD_POWERUP_LOCK)
 627                        if (fdt_setprop_empty(blob, part, "lock") < 0)
 628                                return false;
 629        }
 630
 631        return true;
 632}
 633
 634int ft_board_setup(void *blob, struct bd_info *bd)
 635{
 636        struct mtd_info *mtd;
 637        int node;
 638
 639        mtd = get_mtd_device_nm(OMNIA_SPI_NOR_PATH);
 640        if (IS_ERR_OR_NULL(mtd))
 641                goto fail;
 642
 643        node = fdt_path_offset(blob, OMNIA_SPI_NOR_PATH);
 644        if (node < 0)
 645                goto fail;
 646
 647        if (!fixup_mtd_partitions(blob, node, mtd))
 648                goto fail;
 649
 650        put_mtd_device(mtd);
 651        return 0;
 652
 653fail:
 654        printf("Failed fixing SPI NOR partitions!\n");
 655        if (!IS_ERR_OR_NULL(mtd))
 656                put_mtd_device(mtd);
 657        return 0;
 658}
 659#endif
 660