667 lines
18 KiB
C
667 lines
18 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <linux/array_size.h>
|
|
#include <linux/bitfield.h>
|
|
#include <linux/bits.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/dev_printk.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/minmax.h>
|
|
#include <linux/netlink.h>
|
|
#include <linux/sched/signal.h>
|
|
#include <linux/sizes.h>
|
|
#include <linux/sprintf.h>
|
|
#include <linux/string.h>
|
|
#include <linux/types.h>
|
|
#include <asm/unaligned.h>
|
|
#include <net/devlink.h>
|
|
|
|
#include "core.h"
|
|
#include "devlink.h"
|
|
#include "flash.h"
|
|
|
|
#define ZL_FLASH_ERR_PFX "FW update failed: "
|
|
#define ZL_FLASH_ERR_MSG(_extack, _msg, ...) \
|
|
NL_SET_ERR_MSG_FMT_MOD((_extack), ZL_FLASH_ERR_PFX _msg, \
|
|
## __VA_ARGS__)
|
|
|
|
/**
|
|
* zl3073x_flash_download - Download image block to device memory
|
|
* @zldev: zl3073x device structure
|
|
* @component: name of the component to be downloaded
|
|
* @addr: device memory target address
|
|
* @data: pointer to data to download
|
|
* @size: size of data to download
|
|
* @extack: netlink extack pointer to report errors
|
|
*
|
|
* Return: 0 on success, <0 on error
|
|
*/
|
|
static int
|
|
zl3073x_flash_download(struct zl3073x_dev *zldev, const char *component,
|
|
u32 addr, const void *data, size_t size,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
#define ZL_CHECK_DELAY 5000 /* Check for interrupt each 5 seconds */
|
|
unsigned long check_time;
|
|
const void *ptr, *end;
|
|
int rc = 0;
|
|
|
|
dev_dbg(zldev->dev, "Downloading %zu bytes to device memory at 0x%0x\n",
|
|
size, addr);
|
|
|
|
check_time = jiffies + msecs_to_jiffies(ZL_CHECK_DELAY);
|
|
|
|
for (ptr = data, end = data + size; ptr < end; ptr += 4, addr += 4) {
|
|
/* Write current word to HW memory */
|
|
rc = zl3073x_write_hwreg(zldev, addr,
|
|
get_unaligned((u32 *)ptr));
|
|
if (rc) {
|
|
ZL_FLASH_ERR_MSG(extack,
|
|
"failed to write to memory at 0x%0x",
|
|
addr);
|
|
return rc;
|
|
}
|
|
|
|
if (time_is_before_jiffies(check_time)) {
|
|
if (signal_pending(current)) {
|
|
ZL_FLASH_ERR_MSG(extack,
|
|
"Flashing interrupted");
|
|
return -EINTR;
|
|
}
|
|
|
|
check_time = jiffies + msecs_to_jiffies(ZL_CHECK_DELAY);
|
|
}
|
|
|
|
/* Report status each 1 kB block */
|
|
if ((ptr - data) % 1024 == 0)
|
|
zl3073x_devlink_flash_notify(zldev, "Downloading image",
|
|
component, ptr - data,
|
|
size);
|
|
}
|
|
|
|
zl3073x_devlink_flash_notify(zldev, "Downloading image", component,
|
|
ptr - data, size);
|
|
|
|
dev_dbg(zldev->dev, "%zu bytes downloaded to device memory\n", size);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_flash_error_check - Check for flash utility errors
|
|
* @zldev: zl3073x device structure
|
|
* @extack: netlink extack pointer to report errors
|
|
*
|
|
* The function checks for errors detected by the flash utility and
|
|
* reports them if any were found.
|
|
*
|
|
* Return: 0 on success, -EIO when errors are detected
|
|
*/
|
|
static int
|
|
zl3073x_flash_error_check(struct zl3073x_dev *zldev,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
u32 count, cause;
|
|
int rc;
|
|
|
|
rc = zl3073x_read_u32(zldev, ZL_REG_ERROR_COUNT, &count);
|
|
if (rc)
|
|
return rc;
|
|
else if (!count)
|
|
return 0; /* No error */
|
|
|
|
rc = zl3073x_read_u32(zldev, ZL_REG_ERROR_CAUSE, &cause);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Report errors */
|
|
ZL_FLASH_ERR_MSG(extack,
|
|
"utility error occurred: count=%u cause=0x%x", count,
|
|
cause);
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_flash_wait_ready - Check or wait for utility to be ready to flash
|
|
* @zldev: zl3073x device structure
|
|
* @timeout_ms: timeout for the waiting
|
|
*
|
|
* Return: 0 on success, <0 on error
|
|
*/
|
|
static int
|
|
zl3073x_flash_wait_ready(struct zl3073x_dev *zldev, unsigned int timeout_ms)
|
|
{
|
|
#define ZL_FLASH_POLL_DELAY_MS 100
|
|
unsigned long timeout;
|
|
int rc, i;
|
|
|
|
dev_dbg(zldev->dev, "Waiting for flashing to be ready\n");
|
|
|
|
timeout = jiffies + msecs_to_jiffies(timeout_ms);
|
|
|
|
for (i = 0; time_is_after_jiffies(timeout); i++) {
|
|
u8 value;
|
|
|
|
/* Check for interrupt each 1s */
|
|
if (i > 9) {
|
|
if (signal_pending(current))
|
|
return -EINTR;
|
|
i = 0;
|
|
}
|
|
|
|
rc = zl3073x_read_u8(zldev, ZL_REG_WRITE_FLASH, &value);
|
|
if (rc)
|
|
return rc;
|
|
|
|
value = FIELD_GET(ZL_WRITE_FLASH_OP, value);
|
|
|
|
if (value == ZL_WRITE_FLASH_OP_DONE)
|
|
return 0; /* Successfully done */
|
|
|
|
msleep(ZL_FLASH_POLL_DELAY_MS);
|
|
}
|
|
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_flash_cmd_wait - Perform flash operation and wait for finish
|
|
* @zldev: zl3073x device structure
|
|
* @operation: operation to perform
|
|
* @extack: netlink extack pointer to report errors
|
|
*
|
|
* Return: 0 on success, <0 on error
|
|
*/
|
|
static int
|
|
zl3073x_flash_cmd_wait(struct zl3073x_dev *zldev, u32 operation,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
#define ZL_FLASH_PHASE1_TIMEOUT_MS 60000 /* up to 1 minute */
|
|
#define ZL_FLASH_PHASE2_TIMEOUT_MS 120000 /* up to 2 minutes */
|
|
u8 value;
|
|
int rc;
|
|
|
|
dev_dbg(zldev->dev, "Sending flash command: 0x%x\n", operation);
|
|
|
|
rc = zl3073x_flash_wait_ready(zldev, ZL_FLASH_PHASE1_TIMEOUT_MS);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Issue the requested operation */
|
|
rc = zl3073x_read_u8(zldev, ZL_REG_WRITE_FLASH, &value);
|
|
if (rc)
|
|
return rc;
|
|
|
|
value &= ~ZL_WRITE_FLASH_OP;
|
|
value |= FIELD_PREP(ZL_WRITE_FLASH_OP, operation);
|
|
|
|
rc = zl3073x_write_u8(zldev, ZL_REG_WRITE_FLASH, value);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Wait for command completion */
|
|
rc = zl3073x_flash_wait_ready(zldev, ZL_FLASH_PHASE2_TIMEOUT_MS);
|
|
if (rc)
|
|
return rc;
|
|
|
|
return zl3073x_flash_error_check(zldev, extack);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_flash_get_sector_size - Get flash sector size
|
|
* @zldev: zl3073x device structure
|
|
* @sector_size: sector size returned by the function
|
|
*
|
|
* The function reads the flash sector size detected by flash utility and
|
|
* stores it into @sector_size.
|
|
*
|
|
* Return: 0 on success, <0 on error
|
|
*/
|
|
static int
|
|
zl3073x_flash_get_sector_size(struct zl3073x_dev *zldev, size_t *sector_size)
|
|
{
|
|
u8 flash_info;
|
|
int rc;
|
|
|
|
rc = zl3073x_read_u8(zldev, ZL_REG_FLASH_INFO, &flash_info);
|
|
if (rc)
|
|
return rc;
|
|
|
|
switch (FIELD_GET(ZL_FLASH_INFO_SECTOR_SIZE, flash_info)) {
|
|
case ZL_FLASH_INFO_SECTOR_4K:
|
|
*sector_size = SZ_4K;
|
|
break;
|
|
case ZL_FLASH_INFO_SECTOR_64K:
|
|
*sector_size = SZ_64K;
|
|
break;
|
|
default:
|
|
rc = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_flash_block - Download and flash memory block
|
|
* @zldev: zl3073x device structure
|
|
* @component: component name
|
|
* @operation: flash operation to perform
|
|
* @page: destination flash page
|
|
* @addr: device memory address to load data
|
|
* @data: pointer to data to be flashed
|
|
* @size: size of data
|
|
* @extack: netlink extack pointer to report errors
|
|
*
|
|
* The function downloads the memory block given by the @data pointer and
|
|
* the size @size and flashes it into internal memory on flash page @page.
|
|
* The internal flash operation performed by the firmware is specified by
|
|
* the @operation parameter.
|
|
*
|
|
* Return: 0 on success, <0 on error
|
|
*/
|
|
static int
|
|
zl3073x_flash_block(struct zl3073x_dev *zldev, const char *component,
|
|
u32 operation, u32 page, u32 addr, const void *data,
|
|
size_t size, struct netlink_ext_ack *extack)
|
|
{
|
|
int rc;
|
|
|
|
/* Download block to device memory */
|
|
rc = zl3073x_flash_download(zldev, component, addr, data, size, extack);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Set address to flash from */
|
|
rc = zl3073x_write_u32(zldev, ZL_REG_IMAGE_START_ADDR, addr);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Set size of block to flash */
|
|
rc = zl3073x_write_u32(zldev, ZL_REG_IMAGE_SIZE, size);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Set destination page to flash */
|
|
rc = zl3073x_write_u32(zldev, ZL_REG_FLASH_INDEX_WRITE, page);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Set filling pattern */
|
|
rc = zl3073x_write_u32(zldev, ZL_REG_FILL_PATTERN, U32_MAX);
|
|
if (rc)
|
|
return rc;
|
|
|
|
zl3073x_devlink_flash_notify(zldev, "Flashing image", component, 0,
|
|
size);
|
|
|
|
dev_dbg(zldev->dev, "Flashing %zu bytes to page %u\n", size, page);
|
|
|
|
/* Execute sectors flash operation */
|
|
rc = zl3073x_flash_cmd_wait(zldev, operation, extack);
|
|
if (rc)
|
|
return rc;
|
|
|
|
zl3073x_devlink_flash_notify(zldev, "Flashing image", component, size,
|
|
size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_flash_sectors - Flash sectors
|
|
* @zldev: zl3073x device structure
|
|
* @component: component name
|
|
* @page: destination flash page
|
|
* @addr: device memory address to load data
|
|
* @data: pointer to data to be flashed
|
|
* @size: size of data
|
|
* @extack: netlink extack pointer to report errors
|
|
*
|
|
* The function flashes given @data with size of @size to the internal flash
|
|
* memory block starting from page @page. The function uses sector flash
|
|
* method and has to take into account the flash sector size reported by
|
|
* flashing utility. Input data are spliced into blocks according this
|
|
* sector size and each block is flashed separately.
|
|
*
|
|
* Return: 0 on success, <0 on error
|
|
*/
|
|
int zl3073x_flash_sectors(struct zl3073x_dev *zldev, const char *component,
|
|
u32 page, u32 addr, const void *data, size_t size,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
#define ZL_FLASH_MAX_BLOCK_SIZE 0x0001E000
|
|
#define ZL_FLASH_PAGE_SIZE 256
|
|
size_t max_block_size, block_size, sector_size;
|
|
const void *ptr, *end;
|
|
int rc;
|
|
|
|
/* Get flash sector size */
|
|
rc = zl3073x_flash_get_sector_size(zldev, §or_size);
|
|
if (rc) {
|
|
ZL_FLASH_ERR_MSG(extack, "Failed to get flash sector size");
|
|
return rc;
|
|
}
|
|
|
|
/* Determine max block size depending on sector size */
|
|
max_block_size = ALIGN_DOWN(ZL_FLASH_MAX_BLOCK_SIZE, sector_size);
|
|
|
|
for (ptr = data, end = data + size; ptr < end; ptr += block_size) {
|
|
char comp_str[32];
|
|
|
|
block_size = min_t(size_t, max_block_size, end - ptr);
|
|
|
|
/* Add suffix '-partN' if the requested component size is
|
|
* greater than max_block_size.
|
|
*/
|
|
if (max_block_size < size)
|
|
snprintf(comp_str, sizeof(comp_str), "%s-part%zu",
|
|
component, (ptr - data) / max_block_size + 1);
|
|
else
|
|
strscpy(comp_str, component);
|
|
|
|
/* Flash the memory block */
|
|
rc = zl3073x_flash_block(zldev, comp_str,
|
|
ZL_WRITE_FLASH_OP_SECTORS, page, addr,
|
|
ptr, block_size, extack);
|
|
if (rc)
|
|
goto finish;
|
|
|
|
/* Move to next page */
|
|
page += block_size / ZL_FLASH_PAGE_SIZE;
|
|
}
|
|
|
|
finish:
|
|
zl3073x_devlink_flash_notify(zldev,
|
|
rc ? "Flashing failed" : "Flashing done",
|
|
component, 0, 0);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_flash_page - Flash page
|
|
* @zldev: zl3073x device structure
|
|
* @component: component name
|
|
* @page: destination flash page
|
|
* @addr: device memory address to load data
|
|
* @data: pointer to data to be flashed
|
|
* @size: size of data
|
|
* @extack: netlink extack pointer to report errors
|
|
*
|
|
* The function flashes given @data with size of @size to the internal flash
|
|
* memory block starting with page @page.
|
|
*
|
|
* Return: 0 on success, <0 on error
|
|
*/
|
|
int zl3073x_flash_page(struct zl3073x_dev *zldev, const char *component,
|
|
u32 page, u32 addr, const void *data, size_t size,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
int rc;
|
|
|
|
/* Flash the memory block */
|
|
rc = zl3073x_flash_block(zldev, component, ZL_WRITE_FLASH_OP_PAGE, page,
|
|
addr, data, size, extack);
|
|
|
|
zl3073x_devlink_flash_notify(zldev,
|
|
rc ? "Flashing failed" : "Flashing done",
|
|
component, 0, 0);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_flash_page_copy - Copy flash page
|
|
* @zldev: zl3073x device structure
|
|
* @component: component name
|
|
* @src_page: source page to copy
|
|
* @dst_page: destination page
|
|
* @extack: netlink extack pointer to report errors
|
|
*
|
|
* The function copies one flash page specified by @src_page into the flash
|
|
* page specified by @dst_page.
|
|
*
|
|
* Return: 0 on success, <0 on error
|
|
*/
|
|
int zl3073x_flash_page_copy(struct zl3073x_dev *zldev, const char *component,
|
|
u32 src_page, u32 dst_page,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
int rc;
|
|
|
|
/* Set source page to be copied */
|
|
rc = zl3073x_write_u32(zldev, ZL_REG_FLASH_INDEX_READ, src_page);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Set destination page for the copy */
|
|
rc = zl3073x_write_u32(zldev, ZL_REG_FLASH_INDEX_WRITE, dst_page);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Perform copy operation */
|
|
rc = zl3073x_flash_cmd_wait(zldev, ZL_WRITE_FLASH_OP_COPY_PAGE, extack);
|
|
if (rc)
|
|
ZL_FLASH_ERR_MSG(extack, "Failed to copy page %u to page %u",
|
|
src_page, dst_page);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_flash_mode_verify - Check flash utility
|
|
* @zldev: zl3073x device structure
|
|
*
|
|
* Return: 0 if the flash utility is ready, <0 on error
|
|
*/
|
|
static int
|
|
zl3073x_flash_mode_verify(struct zl3073x_dev *zldev)
|
|
{
|
|
u8 family, release;
|
|
u32 hash;
|
|
int rc;
|
|
|
|
rc = zl3073x_read_u32(zldev, ZL_REG_FLASH_HASH, &hash);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = zl3073x_read_u8(zldev, ZL_REG_FLASH_FAMILY, &family);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = zl3073x_read_u8(zldev, ZL_REG_FLASH_RELEASE, &release);
|
|
if (rc)
|
|
return rc;
|
|
|
|
dev_dbg(zldev->dev,
|
|
"Flash utility check: hash 0x%08x, fam 0x%02x, rel 0x%02x\n",
|
|
hash, family, release);
|
|
|
|
/* Return success for correct family */
|
|
return (family == 0x21) ? 0 : -ENODEV;
|
|
}
|
|
|
|
static int
|
|
zl3073x_flash_host_ctrl_enable(struct zl3073x_dev *zldev)
|
|
{
|
|
u8 host_ctrl;
|
|
int rc;
|
|
|
|
/* Enable host control */
|
|
rc = zl3073x_read_u8(zldev, ZL_REG_HOST_CONTROL, &host_ctrl);
|
|
if (rc)
|
|
return rc;
|
|
|
|
host_ctrl |= ZL_HOST_CONTROL_ENABLE;
|
|
|
|
return zl3073x_write_u8(zldev, ZL_REG_HOST_CONTROL, host_ctrl);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_flash_mode_enter - Switch the device to flash mode
|
|
* @zldev: zl3073x device structure
|
|
* @util_ptr: buffer with flash utility
|
|
* @util_size: size of buffer with flash utility
|
|
* @extack: netlink extack pointer to report errors
|
|
*
|
|
* The function prepares and switches the device into flash mode.
|
|
*
|
|
* The procedure:
|
|
* 1) Stop device CPU by specific HW register sequence
|
|
* 2) Download flash utility to device memory
|
|
* 3) Resume device CPU by specific HW register sequence
|
|
* 4) Check communication with flash utility
|
|
* 5) Enable host control necessary to access flash API
|
|
* 6) Check for potential error detected by the utility
|
|
*
|
|
* The API provided by normal firmware is not available in flash mode
|
|
* so the caller has to ensure that this API is not used in this mode.
|
|
*
|
|
* After performing flash operation the caller should call
|
|
* @zl3073x_flash_mode_leave to return back to normal operation.
|
|
*
|
|
* Return: 0 on success, <0 on error.
|
|
*/
|
|
int zl3073x_flash_mode_enter(struct zl3073x_dev *zldev, const void *util_ptr,
|
|
size_t util_size, struct netlink_ext_ack *extack)
|
|
{
|
|
/* Sequence to be written prior utility download */
|
|
static const struct zl3073x_hwreg_seq_item pre_seq[] = {
|
|
HWREG_SEQ_ITEM(0x80000400, 1, BIT(0), 0),
|
|
HWREG_SEQ_ITEM(0x80206340, 1, BIT(4), 0),
|
|
HWREG_SEQ_ITEM(0x10000000, 1, BIT(2), 0),
|
|
HWREG_SEQ_ITEM(0x10000024, 0x00000001, U32_MAX, 0),
|
|
HWREG_SEQ_ITEM(0x10000020, 0x00000001, U32_MAX, 0),
|
|
HWREG_SEQ_ITEM(0x10000000, 1, BIT(10), 1000),
|
|
};
|
|
/* Sequence to be written after utility download */
|
|
static const struct zl3073x_hwreg_seq_item post_seq[] = {
|
|
HWREG_SEQ_ITEM(0x10400004, 0x000000C0, U32_MAX, 0),
|
|
HWREG_SEQ_ITEM(0x10400008, 0x00000000, U32_MAX, 0),
|
|
HWREG_SEQ_ITEM(0x10400010, 0x20000000, U32_MAX, 0),
|
|
HWREG_SEQ_ITEM(0x10400014, 0x20000004, U32_MAX, 0),
|
|
HWREG_SEQ_ITEM(0x10000000, 1, GENMASK(10, 9), 0),
|
|
HWREG_SEQ_ITEM(0x10000020, 0x00000000, U32_MAX, 0),
|
|
HWREG_SEQ_ITEM(0x10000000, 0, BIT(0), 1000),
|
|
};
|
|
int rc;
|
|
|
|
zl3073x_devlink_flash_notify(zldev, "Prepare flash mode", "utility",
|
|
0, 0);
|
|
|
|
/* Execure pre-load sequence */
|
|
rc = zl3073x_write_hwreg_seq(zldev, pre_seq, ARRAY_SIZE(pre_seq));
|
|
if (rc) {
|
|
ZL_FLASH_ERR_MSG(extack, "cannot execute pre-load sequence");
|
|
goto error;
|
|
}
|
|
|
|
/* Download utility image to device memory */
|
|
rc = zl3073x_flash_download(zldev, "utility", 0x20000000, util_ptr,
|
|
util_size, extack);
|
|
if (rc) {
|
|
ZL_FLASH_ERR_MSG(extack, "cannot download flash utility");
|
|
goto error;
|
|
}
|
|
|
|
/* Execute post-load sequence */
|
|
rc = zl3073x_write_hwreg_seq(zldev, post_seq, ARRAY_SIZE(post_seq));
|
|
if (rc) {
|
|
ZL_FLASH_ERR_MSG(extack, "cannot execute post-load sequence");
|
|
goto error;
|
|
}
|
|
|
|
/* Check that utility identifies itself correctly */
|
|
rc = zl3073x_flash_mode_verify(zldev);
|
|
if (rc) {
|
|
ZL_FLASH_ERR_MSG(extack, "flash utility check failed");
|
|
goto error;
|
|
}
|
|
|
|
/* Enable host control */
|
|
rc = zl3073x_flash_host_ctrl_enable(zldev);
|
|
if (rc) {
|
|
ZL_FLASH_ERR_MSG(extack, "cannot enable host control");
|
|
goto error;
|
|
}
|
|
|
|
zl3073x_devlink_flash_notify(zldev, "Flash mode enabled", "utility",
|
|
0, 0);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
zl3073x_flash_mode_leave(zldev, extack);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_flash_mode_leave - Leave flash mode
|
|
* @zldev: zl3073x device structure
|
|
* @extack: netlink extack pointer to report errors
|
|
*
|
|
* The function instructs the device to leave the flash mode and
|
|
* to return back to normal operation.
|
|
*
|
|
* The procedure:
|
|
* 1) Set reset flag
|
|
* 2) Reset the device CPU by specific HW register sequence
|
|
* 3) Wait for the device to be ready
|
|
* 4) Check the reset flag was cleared
|
|
*
|
|
* Return: 0 on success, <0 on error
|
|
*/
|
|
int zl3073x_flash_mode_leave(struct zl3073x_dev *zldev,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
/* Sequence to be written after flash */
|
|
static const struct zl3073x_hwreg_seq_item fw_reset_seq[] = {
|
|
HWREG_SEQ_ITEM(0x80000404, 1, BIT(0), 0),
|
|
HWREG_SEQ_ITEM(0x80000410, 1, BIT(0), 0),
|
|
};
|
|
u8 reset_status;
|
|
int rc;
|
|
|
|
zl3073x_devlink_flash_notify(zldev, "Leaving flash mode", "utility",
|
|
0, 0);
|
|
|
|
/* Read reset status register */
|
|
rc = zl3073x_read_u8(zldev, ZL_REG_RESET_STATUS, &reset_status);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Set reset bit */
|
|
reset_status |= ZL_REG_RESET_STATUS_RESET;
|
|
|
|
/* Update reset status register */
|
|
rc = zl3073x_write_u8(zldev, ZL_REG_RESET_STATUS, reset_status);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* We do not check the return value here as the sequence resets
|
|
* the device CPU and the last write always return an error.
|
|
*/
|
|
zl3073x_write_hwreg_seq(zldev, fw_reset_seq, ARRAY_SIZE(fw_reset_seq));
|
|
|
|
/* Wait for the device to be ready */
|
|
msleep(500);
|
|
|
|
/* Read again the reset status register */
|
|
rc = zl3073x_read_u8(zldev, ZL_REG_RESET_STATUS, &reset_status);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Check the reset bit was cleared */
|
|
if (reset_status & ZL_REG_RESET_STATUS_RESET) {
|
|
dev_err(zldev->dev,
|
|
"Reset not confirmed after switch to normal mode\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|