Skip to content

Commit 7ee39d1

Browse files
niedzwiecki-dawidjhedberg
authored andcommitted
drivers: flash: add extended operations to Andes XIP driver
Add the extended operations to the Andes XIP flash driver. The extended operations supports: - reading status registers of the flash chip - changing status registers of the flash chip - software lock of the status registers - modifying SPI read command used in memory-mapped mode Signed-off-by: Dawid Niedzwiecki <[email protected]>
1 parent 9dda88f commit 7ee39d1

File tree

3 files changed

+322
-1
lines changed

3 files changed

+322
-1
lines changed

drivers/flash/Kconfig.andes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ config FLASH_ANDES_QSPI_XIP
4848
select FLASH_HAS_PAGE_LAYOUT
4949
select FLASH_HAS_DRIVER_ENABLED
5050
select FLASH_HAS_EXPLICIT_ERASE
51+
select FLASH_HAS_EX_OP
5152
depends on XIP
5253
depends on !SPI_NOR
5354
depends on ARCH_HAS_RAMFUNC_SUPPORT

drivers/flash/flash_andes_qspi_xip.c

Lines changed: 248 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <zephyr/kernel.h>
1717
#include <zephyr/device.h>
1818
#include <zephyr/drivers/cache.h>
19+
#include <zephyr/drivers/flash/andes_flash_xip_api_ex.h>
1920
#include <zephyr/drivers/flash.h>
2021
#include <zephyr/init.h>
2122
#include <zephyr/logging/log.h>
@@ -100,10 +101,21 @@ struct flash_andes_qspi_xip_config {
100101
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
101102
};
102103

104+
struct flash_andes_qspi_xip_data {
105+
#ifdef CONFIG_FLASH_EX_OP_ENABLED
106+
/* Lock of the status registers. */
107+
bool status_lock;
108+
#endif
109+
};
110+
103111
#define flash_andes_qspi_xip_cmd_read(dev, opcode, dest, length) \
104112
flash_andes_qspi_xip_access(dev, opcode, 0, 0, dest, length)
113+
#define flash_andes_qspi_xip_cmd_write_data(dev, opcode, src, length) \
114+
flash_andes_qspi_xip_access(dev, opcode, ANDES_ACCESS_WRITE, 0, src, length)
105115
#define flash_andes_qspi_xip_cmd_write(dev, opcode) \
106116
flash_andes_qspi_xip_access(dev, opcode, ANDES_ACCESS_WRITE, 0, NULL, 0)
117+
#define flash_andes_qspi_xip_cmd_addr_read(dev, opcode, addr, dest, length) \
118+
flash_andes_qspi_xip_access(dev, opcode, ANDES_ACCESS_ADDRESSED, addr, dest, length)
107119
#define flash_andes_qspi_xip_cmd_addr_write(dev, opcode, addr, src, length) \
108120
flash_andes_qspi_xip_access(dev, opcode, ANDES_ACCESS_WRITE | ANDES_ACCESS_ADDRESSED, \
109121
addr, (void *)src, length)
@@ -486,6 +498,236 @@ static void flash_andes_qspi_xip_pages_layout(const struct device *dev,
486498
}
487499
#endif
488500

501+
#ifdef CONFIG_FLASH_EX_OP_ENABLED
502+
static __ramfunc int flash_andes_qspi_xip_get_status(const struct device *dev,
503+
struct andes_xip_ex_ops_get_out *op_out)
504+
{
505+
int ret = 0;
506+
507+
prepare_for_flashing(dev);
508+
509+
ret = flash_andes_qspi_xip_cmd_read(dev, SPI_NOR_CMD_RDSR, &op_out->regs[0], 1);
510+
if (ret) {
511+
goto cleanup;
512+
}
513+
514+
ret = flash_andes_qspi_xip_cmd_read(dev, SPI_NOR_CMD_RDSR2, &op_out->regs[1], 1);
515+
if (ret) {
516+
goto cleanup;
517+
}
518+
519+
ret = flash_andes_qspi_xip_cmd_read(dev, SPI_NOR_CMD_RDSR3, &op_out->regs[2], 1);
520+
521+
cleanup:
522+
cleanup_after_flashing(dev, 0, 0);
523+
524+
return ret;
525+
}
526+
527+
static __ramfunc int write_status_register(const struct device *dev, uint8_t sr, uint8_t mask,
528+
uint8_t op_read, uint8_t op_write)
529+
{
530+
uint8_t sr_curr;
531+
uint8_t sr_new;
532+
int ret, ret2 = 0;
533+
534+
if (!mask) {
535+
return 0;
536+
}
537+
538+
ret = flash_andes_qspi_xip_cmd_read(dev, op_read, &sr_curr, 1);
539+
if (ret) {
540+
return ret;
541+
}
542+
sr_new = (sr_curr & ~mask) | sr;
543+
if (sr_new != sr_curr) {
544+
ret = write_protection_set(dev, false);
545+
if (ret != 0) {
546+
return ret;
547+
}
548+
ret = flash_andes_qspi_xip_cmd_write_data(dev, op_write, &sr_new, 1);
549+
ret2 = flash_andes_qspi_xip_wait_until_ready(dev);
550+
}
551+
552+
if (!ret) {
553+
ret = ret2;
554+
}
555+
556+
return ret;
557+
}
558+
559+
static __ramfunc int flash_andes_qspi_xip_set_status(const struct device *dev,
560+
struct andes_xip_ex_ops_set_in *op_out)
561+
{
562+
int ret;
563+
struct flash_andes_qspi_xip_data *data = dev->data;
564+
565+
if (data->status_lock) {
566+
return -EPERM;
567+
}
568+
569+
prepare_for_flashing(dev);
570+
571+
ret = write_status_register(dev, op_out->regs[0], op_out->masks[0], SPI_NOR_CMD_RDSR,
572+
SPI_NOR_CMD_WRSR);
573+
if (ret) {
574+
goto cleanup;
575+
}
576+
577+
ret = write_status_register(dev, op_out->regs[1], op_out->masks[1], SPI_NOR_CMD_RDSR2,
578+
SPI_NOR_CMD_WRSR2);
579+
if (ret) {
580+
goto cleanup;
581+
}
582+
583+
ret = write_status_register(dev, op_out->regs[2], op_out->masks[2], SPI_NOR_CMD_RDSR3,
584+
SPI_NOR_CMD_WRSR3);
585+
586+
cleanup:
587+
cleanup_after_flashing(dev, 0, 0);
588+
589+
return ret;
590+
}
591+
592+
static __ramfunc int flash_andes_qspi_xip_lock(const struct device *dev,
593+
struct andes_xip_ex_ops_lock_in *op_in)
594+
{
595+
struct flash_andes_qspi_xip_data *data = dev->data;
596+
597+
data->status_lock = op_in->enable;
598+
599+
return 0;
600+
}
601+
602+
static __ramfunc int flash_andes_qspi_xip_lock_state(const struct device *dev,
603+
struct andes_xip_ex_ops_lock_state_out *op_out)
604+
{
605+
struct flash_andes_qspi_xip_data *data = dev->data;
606+
607+
op_out->state = data->status_lock;
608+
609+
return 0;
610+
}
611+
612+
static __ramfunc int
613+
flash_andes_qspi_xip_set_memrdcmd(const struct device *dev,
614+
struct andes_xip_ex_ops_mem_read_cmd_in *op_in)
615+
{
616+
const struct flash_andes_qspi_xip_config *config = dev->config;
617+
struct atcspi200_regs *regs = config->regs;
618+
619+
prepare_for_flashing(dev);
620+
621+
regs->MEMCTRL = (uint32_t)op_in->cmd;
622+
while (regs->MEMCTRL & MEMCTRL_CHG) {
623+
}
624+
625+
cleanup_after_flashing(dev, 0, 0);
626+
627+
return 0;
628+
}
629+
630+
static int flash_andes_qspi_xip_ex_op(const struct device *dev, uint16_t code, const uintptr_t in,
631+
void *out)
632+
{
633+
#ifdef CONFIG_USERSPACE
634+
bool syscall_trap = z_syscall_trap();
635+
#endif
636+
int key, ret;
637+
638+
switch (code) {
639+
case FLASH_ANDES_XIP_EX_OP_GET_STATUS_REGS: {
640+
struct andes_xip_ex_ops_get_out *op_out = (struct andes_xip_ex_ops_get_out *)out;
641+
#ifdef CONFIG_USERSPACE
642+
struct andes_xip_ex_ops_get_out copy_out;
643+
644+
if (syscall_trap) {
645+
op_out = &copy_out;
646+
}
647+
#endif
648+
key = prepare_for_ramfunc();
649+
ret = flash_andes_qspi_xip_get_status(dev, op_out);
650+
cleanup_after_ramfunc(key);
651+
#ifdef CONFIG_USERSPACE
652+
if (ret == 0 && syscall_trap) {
653+
K_OOPS(k_usermode_to_copy(out, op_out, sizeof(copy_out)));
654+
}
655+
#endif
656+
break;
657+
}
658+
case FLASH_ANDES_XIP_EX_OP_SET_STATUS_REGS: {
659+
struct andes_xip_ex_ops_set_in *op_in = (struct andes_xip_ex_ops_set_in *)in;
660+
#ifdef CONFIG_USERSPACE
661+
struct andes_xip_ex_ops_set_in copy_in;
662+
663+
if (syscall_trap) {
664+
K_OOPS(k_usermode_from_copy(&copy_in, op_in, sizeof(copy_in)));
665+
op_in = &copy_in;
666+
}
667+
#endif
668+
key = prepare_for_ramfunc();
669+
ret = flash_andes_qspi_xip_set_status(dev, op_in);
670+
cleanup_after_ramfunc(key);
671+
break;
672+
}
673+
case FLASH_ANDES_XIP_EX_OP_LOCK: {
674+
struct andes_xip_ex_ops_lock_in *op_in = (struct andes_xip_ex_ops_lock_in *)in;
675+
#ifdef CONFIG_USERSPACE
676+
struct andes_xip_ex_ops_lock_in copy_in;
677+
678+
if (syscall_trap) {
679+
K_OOPS(k_usermode_from_copy(&copy_in, op_in, sizeof(copy_in)));
680+
op_in = &copy_in;
681+
}
682+
#endif
683+
ret = flash_andes_qspi_xip_lock(dev, op_in);
684+
break;
685+
}
686+
case FLASH_ANDES_XIP_EX_OP_LOCK_STATE: {
687+
struct andes_xip_ex_ops_lock_state_out *op_out =
688+
(struct andes_xip_ex_ops_lock_state_out *)out;
689+
#ifdef CONFIG_USERSPACE
690+
struct andes_xip_ex_ops_lock_state_out copy_out;
691+
692+
if (syscall_trap) {
693+
op_out = &copy_out;
694+
}
695+
#endif
696+
key = prepare_for_ramfunc();
697+
ret = flash_andes_qspi_xip_lock_state(dev, op_out);
698+
cleanup_after_ramfunc(key);
699+
#ifdef CONFIG_USERSPACE
700+
if (ret == 0 && syscall_trap) {
701+
K_OOPS(k_usermode_to_copy(out, op_out, sizeof(copy_out)));
702+
}
703+
#endif
704+
break;
705+
}
706+
case FLASH_ANDES_XIP_EX_OP_MEM_READ_CMD: {
707+
struct andes_xip_ex_ops_mem_read_cmd_in *op_in =
708+
(struct andes_xip_ex_ops_mem_read_cmd_in *)in;
709+
#ifdef CONFIG_USERSPACE
710+
struct andes_xip_ex_ops_mem_read_cmd_in copy_in;
711+
712+
if (syscall_trap) {
713+
K_OOPS(k_usermode_from_copy(&copy_in, op_in, sizeof(copy_in)));
714+
op_in = &copy_in;
715+
}
716+
#endif
717+
key = prepare_for_ramfunc();
718+
ret = flash_andes_qspi_xip_set_memrdcmd(dev, op_in);
719+
cleanup_after_ramfunc(key);
720+
break;
721+
}
722+
default:
723+
ret = -ENOTSUP;
724+
break;
725+
}
726+
727+
return ret;
728+
}
729+
#endif
730+
489731
static DEVICE_API(flash, flash_andes_qspi_xip_api) = {
490732
.read = flash_andes_qspi_xip_read,
491733
.write = flash_andes_qspi_xip_write,
@@ -494,6 +736,9 @@ static DEVICE_API(flash, flash_andes_qspi_xip_api) = {
494736
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
495737
.page_layout = flash_andes_qspi_xip_pages_layout,
496738
#endif
739+
#ifdef CONFIG_FLASH_EX_OP_ENABLED
740+
.ex_op = flash_andes_qspi_xip_ex_op,
741+
#endif
497742
};
498743

499744
#define IS_XIP(node_id) DT_SAME_NODE(node_id, DT_CHOSEN(zephyr_flash))
@@ -517,7 +762,9 @@ static DEVICE_API(flash, flash_andes_qspi_xip_api) = {
517762
.flash_size = DT_INST_PROP(n, size), \
518763
LAYOUT_PAGES_PROP(n)}; \
519764
\
520-
DEVICE_DT_INST_DEFINE(n, &flash_andes_qspi_xip_init, NULL, NULL, \
765+
static struct flash_andes_qspi_xip_data flash_andes_qspi_xip_data_##n; \
766+
\
767+
DEVICE_DT_INST_DEFINE(n, &flash_andes_qspi_xip_init, NULL, &flash_andes_qspi_xip_data_##n, \
521768
&flash_andes_qspi_xip_config_##n, POST_KERNEL, \
522769
CONFIG_FLASH_ANDES_QSPI_INIT_PRIORITY, &flash_andes_qspi_xip_api);
523770

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2025 The ChromiumOS Authors.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef __ZEPHYR_INCLUDE_DRIVERS_ANDES_FLASH_XIP_API_EX_H__
8+
#define __ZEPHYR_INCLUDE_DRIVERS_ANDES_FLASH_XIP_API_EX_H__
9+
10+
#ifdef __cplusplus
11+
extern "C" {
12+
#endif
13+
14+
#include <zephyr/drivers/flash.h>
15+
16+
enum flash_andes_xip_ex_ops {
17+
FLASH_ANDES_XIP_EX_OP_GET_STATUS_REGS = FLASH_EX_OP_VENDOR_BASE,
18+
FLASH_ANDES_XIP_EX_OP_SET_STATUS_REGS,
19+
FLASH_ANDES_XIP_EX_OP_LOCK,
20+
FLASH_ANDES_XIP_EX_OP_LOCK_STATE,
21+
FLASH_ANDES_XIP_EX_OP_MEM_READ_CMD,
22+
};
23+
24+
enum flash_andes_xip_mem_rd_cmd {
25+
FLASH_ANDES_XIP_MEM_RD_CMD_03 = 0,
26+
FLASH_ANDES_XIP_MEM_RD_CMD_0B = 1,
27+
FLASH_ANDES_XIP_MEM_RD_CMD_3B = 2,
28+
FLASH_ANDES_XIP_MEM_RD_CMD_6B = 3,
29+
FLASH_ANDES_XIP_MEM_RD_CMD_BB = 4,
30+
FLASH_ANDES_XIP_MEM_RD_CMD_EB = 5,
31+
FLASH_ANDES_XIP_MEM_RD_CMD_13 = 8,
32+
FLASH_ANDES_XIP_MEM_RD_CMD_0C = 9,
33+
FLASH_ANDES_XIP_MEM_RD_CMD_3C = 10,
34+
FLASH_ANDES_XIP_MEM_RD_CMD_6C = 11,
35+
FLASH_ANDES_XIP_MEM_RD_CMD_BC = 12,
36+
FLASH_ANDES_XIP_MEM_RD_CMD_EC = 13,
37+
};
38+
39+
/* Structure used by FLASH_ANDES_XIP_EX_OP_GET_STATUS_REGS */
40+
struct andes_xip_ex_ops_get_out {
41+
/* Buffer for read status registers. */
42+
uint8_t regs[3];
43+
};
44+
45+
/* Structure used by FLASH_ANDES_XIP_EX_OP_SET_STATUS_REGS */
46+
struct andes_xip_ex_ops_set_in {
47+
/* Status registers to write. */
48+
uint8_t regs[3];
49+
/* Mask of status registers to change. */
50+
uint8_t masks[3];
51+
};
52+
53+
/* Structures used by FLASH_ANDES_XIP_EX_OP_LOCK */
54+
struct andes_xip_ex_ops_lock_in {
55+
bool enable;
56+
};
57+
58+
/* Structures used by FLASH_ANDES_XIP_EX_OP_LOCK_STATE */
59+
struct andes_xip_ex_ops_lock_state_out {
60+
bool state;
61+
};
62+
63+
/* Structures used by FLASH_ANDES_XIP_EX_OP_MEM_READ_CMD */
64+
struct andes_xip_ex_ops_mem_read_cmd_in {
65+
/* SPI command used for memory-mapped mode. */
66+
enum flash_andes_xip_mem_rd_cmd cmd;
67+
};
68+
69+
#ifdef __cplusplus
70+
}
71+
#endif
72+
73+
#endif /* __ZEPHYR_INCLUDE_DRIVERS_ANDES_FLASH_XIP_API_EX_H__ */

0 commit comments

Comments
 (0)