blob: 83452a77e3e98077df3d38b9b624b8c80f298165 [file] [log] [blame]
// 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 <linux/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, &sector_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;
}