Skip to content

Commit 04f7516

Browse files
chintingkuobroonie
authored andcommitted
spi: aspeed: Add support for non-spi-mem devices
The ASPEED FMC/SPI controller may be shared by spi-mem devices and other SPI peripherals that do not use the spi-mem framework. The driver currently assumes spi-mem semantics for all devices, while the controller also supports direct user mode access commonly used by non-spi-mem devices. This mismatch can result in incorrect behavior when different types of devices share the same controller. Therefore, a user mode based path for non-spi-mem devices is added by implementing the transfer_one() callback and wiring up prepare_message() and unprepare_message() so controller state is initialized and restored for user mode transfers. This allows non-spi-mem devices to operate correctly alongside spi-mem devices on a shared controller. This patch has been tested on: - AST2700 EVB + Infineon and ST SPI TPM device. - AST2x00 EVB + spidev_test utility and the output waveforms are verified with logic analyzer. - AST2x00 EVB + SPI NOR flash read/write regression. Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com> Link: https://patch.msgid.link/20260120123005.1392071-3-chin-ting_kuo@aspeedtech.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent 53f826f commit 04f7516

1 file changed

Lines changed: 128 additions & 6 deletions

File tree

drivers/spi/spi-aspeed-smc.c

Lines changed: 128 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
/* CEx Address Decoding Range Register */
4949
#define CE0_SEGMENT_ADDR_REG 0x30
5050

51+
#define FULL_DUPLEX_RX_DATA 0x1e4
52+
5153
/* CEx Read timing compensation register */
5254
#define CE0_TIMING_COMPENSATION_REG 0x94
5355

@@ -81,6 +83,7 @@ struct aspeed_spi_data {
8183
u32 hclk_mask;
8284
u32 hdiv_max;
8385
u32 min_window_size;
86+
bool full_duplex;
8487

8588
phys_addr_t (*segment_start)(struct aspeed_spi *aspi, u32 reg);
8689
phys_addr_t (*segment_end)(struct aspeed_spi *aspi, u32 reg);
@@ -105,6 +108,7 @@ struct aspeed_spi {
105108

106109
struct clk *clk;
107110
u32 clk_freq;
111+
u8 cs_change;
108112

109113
struct aspeed_spi_chip chips[ASPEED_SPI_MAX_NUM_CS];
110114
};
@@ -280,7 +284,8 @@ static ssize_t aspeed_spi_write_user(struct aspeed_spi_chip *chip,
280284
}
281285

282286
/* support for 1-1-1, 1-1-2 or 1-1-4 */
283-
static bool aspeed_spi_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
287+
static bool aspeed_spi_supports_mem_op(struct spi_mem *mem,
288+
const struct spi_mem_op *op)
284289
{
285290
if (op->cmd.buswidth > 1)
286291
return false;
@@ -305,7 +310,8 @@ static bool aspeed_spi_supports_op(struct spi_mem *mem, const struct spi_mem_op
305310

306311
static const struct aspeed_spi_data ast2400_spi_data;
307312

308-
static int do_aspeed_spi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
313+
static int do_aspeed_spi_exec_mem_op(struct spi_mem *mem,
314+
const struct spi_mem_op *op)
309315
{
310316
struct aspeed_spi *aspi = spi_controller_get_devdata(mem->spi->controller);
311317
struct aspeed_spi_chip *chip = &aspi->chips[spi_get_chipselect(mem->spi, 0)];
@@ -367,11 +373,12 @@ static int do_aspeed_spi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o
367373
return ret;
368374
}
369375

370-
static int aspeed_spi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
376+
static int aspeed_spi_exec_mem_op(struct spi_mem *mem,
377+
const struct spi_mem_op *op)
371378
{
372379
int ret;
373380

374-
ret = do_aspeed_spi_exec_op(mem, op);
381+
ret = do_aspeed_spi_exec_mem_op(mem, op);
375382
if (ret)
376383
dev_err(&mem->spi->dev, "operation failed: %d\n", ret);
377384
return ret;
@@ -773,8 +780,8 @@ static ssize_t aspeed_spi_dirmap_read(struct spi_mem_dirmap_desc *desc,
773780
}
774781

775782
static const struct spi_controller_mem_ops aspeed_spi_mem_ops = {
776-
.supports_op = aspeed_spi_supports_op,
777-
.exec_op = aspeed_spi_exec_op,
783+
.supports_op = aspeed_spi_supports_mem_op,
784+
.exec_op = aspeed_spi_exec_mem_op,
778785
.get_name = aspeed_spi_get_name,
779786
.dirmap_create = aspeed_spi_dirmap_create,
780787
.dirmap_read = aspeed_spi_dirmap_read,
@@ -843,6 +850,110 @@ static void aspeed_spi_enable(struct aspeed_spi *aspi, bool enable)
843850
aspeed_spi_chip_enable(aspi, cs, enable);
844851
}
845852

853+
static int aspeed_spi_user_prepare_msg(struct spi_controller *ctlr,
854+
struct spi_message *msg)
855+
{
856+
struct aspeed_spi *aspi =
857+
(struct aspeed_spi *)spi_controller_get_devdata(ctlr);
858+
const struct aspeed_spi_data *data = aspi->data;
859+
struct spi_device *spi = msg->spi;
860+
u32 cs = spi_get_chipselect(spi, 0);
861+
struct aspeed_spi_chip *chip = &aspi->chips[cs];
862+
u32 ctrl_val;
863+
u32 clk_div = data->get_clk_div(chip, spi->max_speed_hz);
864+
865+
ctrl_val = chip->ctl_val[ASPEED_SPI_BASE];
866+
ctrl_val &= ~CTRL_IO_MODE_MASK & data->hclk_mask;
867+
ctrl_val |= clk_div;
868+
chip->ctl_val[ASPEED_SPI_BASE] = ctrl_val;
869+
870+
if (aspi->cs_change == 0)
871+
aspeed_spi_start_user(chip);
872+
873+
return 0;
874+
}
875+
876+
static int aspeed_spi_user_unprepare_msg(struct spi_controller *ctlr,
877+
struct spi_message *msg)
878+
{
879+
struct aspeed_spi *aspi =
880+
(struct aspeed_spi *)spi_controller_get_devdata(ctlr);
881+
struct spi_device *spi = msg->spi;
882+
u32 cs = spi_get_chipselect(spi, 0);
883+
struct aspeed_spi_chip *chip = &aspi->chips[cs];
884+
885+
if (aspi->cs_change == 0)
886+
aspeed_spi_stop_user(chip);
887+
888+
return 0;
889+
}
890+
891+
static void aspeed_spi_user_transfer_tx(struct aspeed_spi *aspi,
892+
struct spi_device *spi,
893+
const u8 *tx_buf, u8 *rx_buf,
894+
void *dst, u32 len)
895+
{
896+
const struct aspeed_spi_data *data = aspi->data;
897+
bool full_duplex_transfer = data->full_duplex && tx_buf == rx_buf;
898+
u32 i;
899+
900+
if (full_duplex_transfer &&
901+
!!(spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD |
902+
SPI_RX_DUAL | SPI_RX_QUAD))) {
903+
dev_err(aspi->dev,
904+
"full duplex is only supported for single IO mode\n");
905+
return;
906+
}
907+
908+
for (i = 0; i < len; i++) {
909+
writeb(tx_buf[i], dst);
910+
if (full_duplex_transfer)
911+
rx_buf[i] = readb(aspi->regs + FULL_DUPLEX_RX_DATA);
912+
}
913+
}
914+
915+
static int aspeed_spi_user_transfer(struct spi_controller *ctlr,
916+
struct spi_device *spi,
917+
struct spi_transfer *xfer)
918+
{
919+
struct aspeed_spi *aspi =
920+
(struct aspeed_spi *)spi_controller_get_devdata(ctlr);
921+
u32 cs = spi_get_chipselect(spi, 0);
922+
struct aspeed_spi_chip *chip = &aspi->chips[cs];
923+
void __iomem *ahb_base = aspi->chips[cs].ahb_base;
924+
const u8 *tx_buf = xfer->tx_buf;
925+
u8 *rx_buf = xfer->rx_buf;
926+
927+
dev_dbg(aspi->dev,
928+
"[cs%d] xfer: width %d, len %u, tx %p, rx %p\n",
929+
cs, xfer->bits_per_word, xfer->len,
930+
tx_buf, rx_buf);
931+
932+
if (tx_buf) {
933+
if (spi->mode & SPI_TX_DUAL)
934+
aspeed_spi_set_io_mode(chip, CTRL_IO_DUAL_DATA);
935+
else if (spi->mode & SPI_TX_QUAD)
936+
aspeed_spi_set_io_mode(chip, CTRL_IO_QUAD_DATA);
937+
938+
aspeed_spi_user_transfer_tx(aspi, spi, tx_buf, rx_buf,
939+
(void *)ahb_base, xfer->len);
940+
}
941+
942+
if (rx_buf && rx_buf != tx_buf) {
943+
if (spi->mode & SPI_RX_DUAL)
944+
aspeed_spi_set_io_mode(chip, CTRL_IO_DUAL_DATA);
945+
else if (spi->mode & SPI_RX_QUAD)
946+
aspeed_spi_set_io_mode(chip, CTRL_IO_QUAD_DATA);
947+
948+
ioread8_rep(ahb_base, rx_buf, xfer->len);
949+
}
950+
951+
xfer->error = 0;
952+
aspi->cs_change = xfer->cs_change;
953+
954+
return 0;
955+
}
956+
846957
static int aspeed_spi_probe(struct platform_device *pdev)
847958
{
848959
struct device *dev = &pdev->dev;
@@ -899,6 +1010,9 @@ static int aspeed_spi_probe(struct platform_device *pdev)
8991010
ctlr->cleanup = aspeed_spi_cleanup;
9001011
ctlr->num_chipselect = of_get_available_child_count(dev->of_node);
9011012
ctlr->dev.of_node = dev->of_node;
1013+
ctlr->prepare_message = aspeed_spi_user_prepare_msg;
1014+
ctlr->unprepare_message = aspeed_spi_user_unprepare_msg;
1015+
ctlr->transfer_one = aspeed_spi_user_transfer;
9021016

9031017
aspi->num_cs = ctlr->num_chipselect;
9041018

@@ -1455,6 +1569,7 @@ static const struct aspeed_spi_data ast2400_fmc_data = {
14551569
.hclk_mask = 0xfffff0ff,
14561570
.hdiv_max = 1,
14571571
.min_window_size = 0x800000,
1572+
.full_duplex = false,
14581573
.calibrate = aspeed_spi_calibrate,
14591574
.get_clk_div = aspeed_get_clk_div_ast2400,
14601575
.segment_start = aspeed_spi_segment_start,
@@ -1471,6 +1586,7 @@ static const struct aspeed_spi_data ast2400_spi_data = {
14711586
.timing = 0x14,
14721587
.hclk_mask = 0xfffff0ff,
14731588
.hdiv_max = 1,
1589+
.full_duplex = false,
14741590
.get_clk_div = aspeed_get_clk_div_ast2400,
14751591
.calibrate = aspeed_spi_calibrate,
14761592
/* No segment registers */
@@ -1485,6 +1601,7 @@ static const struct aspeed_spi_data ast2500_fmc_data = {
14851601
.hclk_mask = 0xffffd0ff,
14861602
.hdiv_max = 1,
14871603
.min_window_size = 0x800000,
1604+
.full_duplex = false,
14881605
.get_clk_div = aspeed_get_clk_div_ast2500,
14891606
.calibrate = aspeed_spi_calibrate,
14901607
.segment_start = aspeed_spi_segment_start,
@@ -1502,6 +1619,7 @@ static const struct aspeed_spi_data ast2500_spi_data = {
15021619
.hclk_mask = 0xffffd0ff,
15031620
.hdiv_max = 1,
15041621
.min_window_size = 0x800000,
1622+
.full_duplex = false,
15051623
.get_clk_div = aspeed_get_clk_div_ast2500,
15061624
.calibrate = aspeed_spi_calibrate,
15071625
.segment_start = aspeed_spi_segment_start,
@@ -1520,6 +1638,7 @@ static const struct aspeed_spi_data ast2600_fmc_data = {
15201638
.hclk_mask = 0xf0fff0ff,
15211639
.hdiv_max = 2,
15221640
.min_window_size = 0x200000,
1641+
.full_duplex = false,
15231642
.get_clk_div = aspeed_get_clk_div_ast2600,
15241643
.calibrate = aspeed_spi_ast2600_calibrate,
15251644
.segment_start = aspeed_spi_segment_ast2600_start,
@@ -1538,6 +1657,7 @@ static const struct aspeed_spi_data ast2600_spi_data = {
15381657
.hclk_mask = 0xf0fff0ff,
15391658
.hdiv_max = 2,
15401659
.min_window_size = 0x200000,
1660+
.full_duplex = false,
15411661
.get_clk_div = aspeed_get_clk_div_ast2600,
15421662
.calibrate = aspeed_spi_ast2600_calibrate,
15431663
.segment_start = aspeed_spi_segment_ast2600_start,
@@ -1556,6 +1676,7 @@ static const struct aspeed_spi_data ast2700_fmc_data = {
15561676
.hclk_mask = 0xf0fff0ff,
15571677
.hdiv_max = 2,
15581678
.min_window_size = 0x10000,
1679+
.full_duplex = true,
15591680
.get_clk_div = aspeed_get_clk_div_ast2600,
15601681
.calibrate = aspeed_spi_ast2600_calibrate,
15611682
.segment_start = aspeed_spi_segment_ast2700_start,
@@ -1573,6 +1694,7 @@ static const struct aspeed_spi_data ast2700_spi_data = {
15731694
.hclk_mask = 0xf0fff0ff,
15741695
.hdiv_max = 2,
15751696
.min_window_size = 0x10000,
1697+
.full_duplex = true,
15761698
.get_clk_div = aspeed_get_clk_div_ast2600,
15771699
.calibrate = aspeed_spi_ast2600_calibrate,
15781700
.segment_start = aspeed_spi_segment_ast2700_start,

0 commit comments

Comments
 (0)