mirror of
https://github.com/torvalds/linux.git
synced 2026-05-05 23:05:25 -04:00
spi: aspeed: Add AST2700 SoC support and Quad SPI
Merge series from Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>: This series adds AST2700 support to the ASPEED FMC/SPI driver and bindings, introduces 64-bit address compatibility, and improves Quad SPI page programming behavior. It also implements AST2700-specific segment logic, where range adjustment is not required because the AST2700 SPI hardware controller already fixes decoding issues on the existing platforms and adopts an updated scheme.
This commit is contained in:
@@ -12,7 +12,7 @@ maintainers:
|
||||
|
||||
description: |
|
||||
This binding describes the Aspeed Static Memory Controllers (FMC and
|
||||
SPI) of the AST2400, AST2500 and AST2600 SOCs.
|
||||
SPI) of the AST2400, AST2500, AST2600 and AST2700 SOCs.
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
@@ -20,6 +20,8 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- aspeed,ast2700-fmc
|
||||
- aspeed,ast2700-spi
|
||||
- aspeed,ast2600-fmc
|
||||
- aspeed,ast2600-spi
|
||||
- aspeed,ast2500-fmc
|
||||
|
||||
@@ -82,9 +82,10 @@ struct aspeed_spi_data {
|
||||
u32 hdiv_max;
|
||||
u32 min_window_size;
|
||||
|
||||
u32 (*segment_start)(struct aspeed_spi *aspi, u32 reg);
|
||||
u32 (*segment_end)(struct aspeed_spi *aspi, u32 reg);
|
||||
u32 (*segment_reg)(struct aspeed_spi *aspi, u32 start, u32 end);
|
||||
phys_addr_t (*segment_start)(struct aspeed_spi *aspi, u32 reg);
|
||||
phys_addr_t (*segment_end)(struct aspeed_spi *aspi, u32 reg);
|
||||
u32 (*segment_reg)(struct aspeed_spi *aspi, phys_addr_t start,
|
||||
phys_addr_t end);
|
||||
int (*adjust_window)(struct aspeed_spi *aspi);
|
||||
u32 (*get_clk_div)(struct aspeed_spi_chip *chip, u32 hz);
|
||||
int (*calibrate)(struct aspeed_spi_chip *chip, u32 hdiv,
|
||||
@@ -97,7 +98,7 @@ struct aspeed_spi {
|
||||
const struct aspeed_spi_data *data;
|
||||
|
||||
void __iomem *regs;
|
||||
u32 ahb_base_phy;
|
||||
phys_addr_t ahb_base_phy;
|
||||
u32 ahb_window_size;
|
||||
u32 num_cs;
|
||||
struct device *dev;
|
||||
@@ -263,11 +264,15 @@ static ssize_t aspeed_spi_write_user(struct aspeed_spi_chip *chip,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
int ret;
|
||||
int io_mode = aspeed_spi_get_io_mode(op);
|
||||
|
||||
aspeed_spi_start_user(chip);
|
||||
ret = aspeed_spi_send_cmd_addr(chip, op->addr.nbytes, op->addr.val, op->cmd.opcode);
|
||||
if (ret < 0)
|
||||
goto stop_user;
|
||||
|
||||
aspeed_spi_set_io_mode(chip, io_mode);
|
||||
|
||||
aspeed_spi_write_to_ahb(chip->ahb_base, op->data.buf.out, op->data.nbytes);
|
||||
stop_user:
|
||||
aspeed_spi_stop_user(chip);
|
||||
@@ -480,9 +485,9 @@ static int aspeed_spi_chip_set_default_window(struct aspeed_spi *aspi)
|
||||
/* Assign the minimum window size to each CS */
|
||||
for (cs = 0; cs < aspi->num_cs; cs++) {
|
||||
aspi->chips[cs].ahb_window_size = aspi->data->min_window_size;
|
||||
dev_dbg(aspi->dev, "CE%d default window [ 0x%.8x - 0x%.8x ]",
|
||||
cs, aspi->ahb_base_phy + aspi->data->min_window_size * cs,
|
||||
aspi->ahb_base_phy + aspi->data->min_window_size * cs - 1);
|
||||
dev_dbg(aspi->dev, "CE%d default window [ 0x%.9llx - 0x%.9llx ]",
|
||||
cs, (u64)(aspi->ahb_base_phy + aspi->data->min_window_size * cs),
|
||||
(u64)(aspi->ahb_base_phy + aspi->data->min_window_size * cs - 1));
|
||||
}
|
||||
|
||||
/* Close unused CS */
|
||||
@@ -926,17 +931,18 @@ static void aspeed_spi_remove(struct platform_device *pdev)
|
||||
* The address range is encoded with absolute addresses in the overall
|
||||
* mapping window.
|
||||
*/
|
||||
static u32 aspeed_spi_segment_start(struct aspeed_spi *aspi, u32 reg)
|
||||
static phys_addr_t aspeed_spi_segment_start(struct aspeed_spi *aspi, u32 reg)
|
||||
{
|
||||
return ((reg >> 16) & 0xFF) << 23;
|
||||
}
|
||||
|
||||
static u32 aspeed_spi_segment_end(struct aspeed_spi *aspi, u32 reg)
|
||||
static phys_addr_t aspeed_spi_segment_end(struct aspeed_spi *aspi, u32 reg)
|
||||
{
|
||||
return ((reg >> 24) & 0xFF) << 23;
|
||||
}
|
||||
|
||||
static u32 aspeed_spi_segment_reg(struct aspeed_spi *aspi, u32 start, u32 end)
|
||||
static u32 aspeed_spi_segment_reg(struct aspeed_spi *aspi,
|
||||
phys_addr_t start, phys_addr_t end)
|
||||
{
|
||||
return (((start >> 23) & 0xFF) << 16) | (((end >> 23) & 0xFF) << 24);
|
||||
}
|
||||
@@ -948,16 +954,16 @@ static u32 aspeed_spi_segment_reg(struct aspeed_spi *aspi, u32 start, u32 end)
|
||||
|
||||
#define AST2600_SEG_ADDR_MASK 0x0ff00000
|
||||
|
||||
static u32 aspeed_spi_segment_ast2600_start(struct aspeed_spi *aspi,
|
||||
u32 reg)
|
||||
static phys_addr_t aspeed_spi_segment_ast2600_start(struct aspeed_spi *aspi,
|
||||
u32 reg)
|
||||
{
|
||||
u32 start_offset = (reg << 16) & AST2600_SEG_ADDR_MASK;
|
||||
|
||||
return aspi->ahb_base_phy + start_offset;
|
||||
}
|
||||
|
||||
static u32 aspeed_spi_segment_ast2600_end(struct aspeed_spi *aspi,
|
||||
u32 reg)
|
||||
static phys_addr_t aspeed_spi_segment_ast2600_end(struct aspeed_spi *aspi,
|
||||
u32 reg)
|
||||
{
|
||||
u32 end_offset = reg & AST2600_SEG_ADDR_MASK;
|
||||
|
||||
@@ -969,7 +975,7 @@ static u32 aspeed_spi_segment_ast2600_end(struct aspeed_spi *aspi,
|
||||
}
|
||||
|
||||
static u32 aspeed_spi_segment_ast2600_reg(struct aspeed_spi *aspi,
|
||||
u32 start, u32 end)
|
||||
phys_addr_t start, phys_addr_t end)
|
||||
{
|
||||
/* disable zero size segments */
|
||||
if (start == end)
|
||||
@@ -979,6 +985,41 @@ static u32 aspeed_spi_segment_ast2600_reg(struct aspeed_spi *aspi,
|
||||
((end - 1) & AST2600_SEG_ADDR_MASK);
|
||||
}
|
||||
|
||||
/* The Segment Registers of the AST2700 use a 64KB unit. */
|
||||
#define AST2700_SEG_ADDR_MASK 0x7fff0000
|
||||
|
||||
static phys_addr_t aspeed_spi_segment_ast2700_start(struct aspeed_spi *aspi,
|
||||
u32 reg)
|
||||
{
|
||||
u64 start_offset = (reg << 16) & AST2700_SEG_ADDR_MASK;
|
||||
|
||||
if (!start_offset)
|
||||
return aspi->ahb_base_phy;
|
||||
|
||||
return aspi->ahb_base_phy + start_offset;
|
||||
}
|
||||
|
||||
static phys_addr_t aspeed_spi_segment_ast2700_end(struct aspeed_spi *aspi,
|
||||
u32 reg)
|
||||
{
|
||||
u64 end_offset = reg & AST2700_SEG_ADDR_MASK;
|
||||
|
||||
if (!end_offset)
|
||||
return aspi->ahb_base_phy;
|
||||
|
||||
return aspi->ahb_base_phy + end_offset;
|
||||
}
|
||||
|
||||
static u32 aspeed_spi_segment_ast2700_reg(struct aspeed_spi *aspi,
|
||||
phys_addr_t start, phys_addr_t end)
|
||||
{
|
||||
if (start == end)
|
||||
return 0;
|
||||
|
||||
return (u32)(((start & AST2700_SEG_ADDR_MASK) >> 16) |
|
||||
(end & AST2700_SEG_ADDR_MASK));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read timing compensation sequences
|
||||
*/
|
||||
@@ -1505,6 +1546,40 @@ static const struct aspeed_spi_data ast2600_spi_data = {
|
||||
.adjust_window = aspeed_adjust_window_ast2600,
|
||||
};
|
||||
|
||||
static const struct aspeed_spi_data ast2700_fmc_data = {
|
||||
.max_cs = 3,
|
||||
.hastype = false,
|
||||
.mode_bits = SPI_RX_QUAD | SPI_TX_QUAD,
|
||||
.we0 = 16,
|
||||
.ctl0 = CE0_CTRL_REG,
|
||||
.timing = CE0_TIMING_COMPENSATION_REG,
|
||||
.hclk_mask = 0xf0fff0ff,
|
||||
.hdiv_max = 2,
|
||||
.min_window_size = 0x10000,
|
||||
.get_clk_div = aspeed_get_clk_div_ast2600,
|
||||
.calibrate = aspeed_spi_ast2600_calibrate,
|
||||
.segment_start = aspeed_spi_segment_ast2700_start,
|
||||
.segment_end = aspeed_spi_segment_ast2700_end,
|
||||
.segment_reg = aspeed_spi_segment_ast2700_reg,
|
||||
};
|
||||
|
||||
static const struct aspeed_spi_data ast2700_spi_data = {
|
||||
.max_cs = 2,
|
||||
.hastype = false,
|
||||
.mode_bits = SPI_RX_QUAD | SPI_TX_QUAD,
|
||||
.we0 = 16,
|
||||
.ctl0 = CE0_CTRL_REG,
|
||||
.timing = CE0_TIMING_COMPENSATION_REG,
|
||||
.hclk_mask = 0xf0fff0ff,
|
||||
.hdiv_max = 2,
|
||||
.min_window_size = 0x10000,
|
||||
.get_clk_div = aspeed_get_clk_div_ast2600,
|
||||
.calibrate = aspeed_spi_ast2600_calibrate,
|
||||
.segment_start = aspeed_spi_segment_ast2700_start,
|
||||
.segment_end = aspeed_spi_segment_ast2700_end,
|
||||
.segment_reg = aspeed_spi_segment_ast2700_reg,
|
||||
};
|
||||
|
||||
static const struct of_device_id aspeed_spi_matches[] = {
|
||||
{ .compatible = "aspeed,ast2400-fmc", .data = &ast2400_fmc_data },
|
||||
{ .compatible = "aspeed,ast2400-spi", .data = &ast2400_spi_data },
|
||||
@@ -1512,6 +1587,8 @@ static const struct of_device_id aspeed_spi_matches[] = {
|
||||
{ .compatible = "aspeed,ast2500-spi", .data = &ast2500_spi_data },
|
||||
{ .compatible = "aspeed,ast2600-fmc", .data = &ast2600_fmc_data },
|
||||
{ .compatible = "aspeed,ast2600-spi", .data = &ast2600_spi_data },
|
||||
{ .compatible = "aspeed,ast2700-fmc", .data = &ast2700_fmc_data },
|
||||
{ .compatible = "aspeed,ast2700-spi", .data = &ast2700_spi_data },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, aspeed_spi_matches);
|
||||
|
||||
Reference in New Issue
Block a user