u32 ahb_window_size;
u32 ctl_val[ASPEED_SPI_MAX];
u32 clk_freq;
+ bool force_user_mode;
};
struct aspeed_spi_data {
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);
+ 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,
const u8 *golden_buf, u8 *test_buf);
for (cs = aspi->num_cs; cs < aspi->data->max_cs; cs++)
aspi->chips[cs].ahb_window_size = 0;
+ if (aspi->data->adjust_window)
+ aspi->data->adjust_window(aspi);
+
return aspeed_spi_set_window(aspi);
}
+/*
+ * As the flash size grows up, we need to trim some decoding
+ * size if needed for the sake of conforming the maximum
+ * decoding size. We trim the decoding size from the rear CS
+ * to avoid affecting the default boot up sequence, usually,
+ * from CS0. Notice, if a CS decoding size is trimmed,
+ * command mode may not work perfectly on that CS, but it only
+ * affect performance and the debug function.
+ */
+static int aspeed_spi_trim_window_size(struct aspeed_spi *aspi)
+{
+ struct aspeed_spi_chip *chips = aspi->chips;
+ size_t total_sz;
+ int cs = aspi->data->max_cs - 1;
+ u32 i;
+ bool trimmed = false;
+
+ do {
+ total_sz = 0;
+ for (i = 0; i < aspi->data->max_cs; i++)
+ total_sz += chips[i].ahb_window_size;
+
+ if (cs < 0)
+ return -ENOMEM;
+
+ if (chips[cs].ahb_window_size <= aspi->data->min_window_size) {
+ cs--;
+ continue;
+ }
+
+ if (total_sz > aspi->ahb_window_size) {
+ chips[cs].ahb_window_size -=
+ aspi->data->min_window_size;
+ total_sz -= aspi->data->min_window_size;
+ /*
+ * If the ahb window size is ever trimmed, only user
+ * mode can be adopted to access the whole flash.
+ */
+ chips[cs].force_user_mode = true;
+ trimmed = true;
+ }
+ } while (total_sz > aspi->ahb_window_size);
+
+ if (trimmed) {
+ dev_warn(aspi->dev, "Window size after triming:\n");
+ for (cs = 0; cs < aspi->data->max_cs; cs++) {
+ dev_warn(aspi->dev, "CE%d: 0x%08x\n",
+ cs, chips[cs].ahb_window_size);
+ }
+ }
+
+ return 0;
+}
+
+static int aspeed_adjust_window_ast2400(struct aspeed_spi *aspi)
+{
+ int ret;
+ int cs;
+ struct aspeed_spi_chip *chips = aspi->chips;
+
+ /* Close unused CS. */
+ for (cs = aspi->num_cs; cs < aspi->data->max_cs; cs++)
+ chips[cs].ahb_window_size = 0;
+
+ ret = aspeed_spi_trim_window_size(aspi);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * For AST2500, the minimum address decoding size for each CS
+ * is 8MB. This address decoding size is mandatory for each
+ * CS no matter whether it will be used. This is a HW limitation.
+ */
+static int aspeed_adjust_window_ast2500(struct aspeed_spi *aspi)
+{
+ int ret;
+ int cs, i;
+ u32 cum_size, rem_size;
+ struct aspeed_spi_chip *chips = aspi->chips;
+
+ /* Assign min_window_sz to unused CS. */
+ for (cs = aspi->num_cs; cs < aspi->data->max_cs; cs++) {
+ if (chips[cs].ahb_window_size < aspi->data->min_window_size)
+ chips[cs].ahb_window_size =
+ aspi->data->min_window_size;
+ }
+
+ /*
+ * If command mode or normal mode is used by dirmap read, the start
+ * address of a window should be multiple of its related flash size.
+ * Namely, the total windows size from flash 0 to flash N should
+ * be multiple of the size of flash (N + 1).
+ */
+ for (cs = aspi->num_cs - 1; cs >= 0; cs--) {
+ cum_size = 0;
+ for (i = 0; i < cs; i++)
+ cum_size += chips[i].ahb_window_size;
+
+ rem_size = cum_size % chips[cs].ahb_window_size;
+ if (chips[cs].ahb_window_size != 0 && rem_size != 0)
+ chips[0].ahb_window_size +=
+ chips[cs].ahb_window_size - rem_size;
+ }
+
+ ret = aspeed_spi_trim_window_size(aspi);
+ if (ret != 0)
+ return ret;
+
+ /* The total window size of AST2500 SPI1 CS0 and CS1 must be 128MB */
+ if (aspi->data == &ast2500_spi_data)
+ chips[1].ahb_window_size =
+ 0x08000000 - chips[0].ahb_window_size;
+
+ return 0;
+}
+
+static int aspeed_adjust_window_ast2600(struct aspeed_spi *aspi)
+{
+ int ret;
+ int cs, i;
+ u32 cum_size, rem_size;
+ struct aspeed_spi_chip *chips = aspi->chips;
+
+ /* Close unused CS. */
+ for (cs = aspi->num_cs; cs < aspi->data->max_cs; cs++)
+ chips[cs].ahb_window_size = 0;
+
+ /*
+ * If command mode or normal mode is used by dirmap read, the start
+ * address of a window should be multiple of its related flash size.
+ * Namely, the total windows size from flash 0 to flash N should
+ * be multiple of the size of flash (N + 1).
+ */
+ for (cs = aspi->num_cs - 1; cs >= 0; cs--) {
+ cum_size = 0;
+ for (i = 0; i < cs; i++)
+ cum_size += chips[i].ahb_window_size;
+
+ rem_size = cum_size % chips[cs].ahb_window_size;
+ if (chips[cs].ahb_window_size != 0 && rem_size != 0)
+ chips[0].ahb_window_size +=
+ chips[cs].ahb_window_size - rem_size;
+ }
+
+ ret = aspeed_spi_trim_window_size(aspi);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
/*
* Yet to be done when possible :
* - Align mappings on flash size (we don't have the info)
u32 local_offset, u32 size)
{
struct aspeed_spi *aspi = chip->aspi;
- u32 cs;
- u32 total_window_size;
int ret;
/* No segment registers for the AST2400 SPI controller */
if (aspi->data == &ast2400_spi_data)
return 0;
- /*
- * Due to an HW issue on the AST2500 SPI controller, the CE0
- * window size should be smaller than the maximum 128MB.
- */
- if (aspi->data == &ast2500_spi_data && chip->cs == 0 && size == SZ_128M) {
- size = 120 << 20;
- dev_info(aspi->dev, "CE%d window resized to %dMB (AST2500 HW quirk)",
- chip->cs, size >> 20);
- }
-
- /*
- * The decoding size of AST2600 SPI controller should set at
- * least 2MB.
- */
- if ((aspi->data == &ast2600_spi_data || aspi->data == &ast2600_fmc_data) &&
- size < SZ_2M) {
- size = SZ_2M;
- dev_info(aspi->dev, "CE%d window resized to %dMB (AST2600 Decoding)",
- chip->cs, size >> 20);
- }
-
/* Adjust this chip window */
aspi->chips[chip->cs].ahb_window_size = size;
- total_window_size = 0;
- for (cs = 0; cs < aspi->data->max_cs; cs++)
- total_window_size += aspi->chips[cs].ahb_window_size;
-
- if (total_window_size > aspi->ahb_window_size) {
- aspi->chips[chip->cs].ahb_window_size -= (total_window_size -
- aspi->ahb_window_size);
- dev_warn(aspi->dev, "CE%d window resized to %zdMB",
- chip->cs, aspi->chips[chip->cs].ahb_window_size >> 20);
- }
+ /* Adjust the overall windows size regarding each platform */
+ if (aspi->data->adjust_window)
+ aspi->data->adjust_window(aspi);
ret = aspeed_spi_set_window(aspi);
if (ret)
struct aspeed_spi_chip *chip = &aspi->chips[spi_get_chipselect(desc->mem->spi, 0)];
/* Switch to USER command mode if mapping window is too small */
- if (chip->ahb_window_size < offset + len) {
+ if (chip->ahb_window_size < offset + len || chip->force_user_mode) {
int ret;
ret = aspeed_spi_read_user(chip, &desc->info.op_tmpl, offset, len, buf);
.segment_start = aspeed_spi_segment_start,
.segment_end = aspeed_spi_segment_end,
.segment_reg = aspeed_spi_segment_reg,
+ .adjust_window = aspeed_adjust_window_ast2400,
};
static const struct aspeed_spi_data ast2400_spi_data = {
.segment_start = aspeed_spi_segment_start,
.segment_end = aspeed_spi_segment_end,
.segment_reg = aspeed_spi_segment_reg,
+ .adjust_window = aspeed_adjust_window_ast2500,
};
static const struct aspeed_spi_data ast2500_spi_data = {
.segment_start = aspeed_spi_segment_start,
.segment_end = aspeed_spi_segment_end,
.segment_reg = aspeed_spi_segment_reg,
+ .adjust_window = aspeed_adjust_window_ast2500,
};
static const struct aspeed_spi_data ast2600_fmc_data = {
.segment_start = aspeed_spi_segment_ast2600_start,
.segment_end = aspeed_spi_segment_ast2600_end,
.segment_reg = aspeed_spi_segment_ast2600_reg,
+ .adjust_window = aspeed_adjust_window_ast2600,
};
static const struct aspeed_spi_data ast2600_spi_data = {
.segment_start = aspeed_spi_segment_ast2600_start,
.segment_end = aspeed_spi_segment_ast2600_end,
.segment_reg = aspeed_spi_segment_ast2600_reg,
+ .adjust_window = aspeed_adjust_window_ast2600,
};
static const struct of_device_id aspeed_spi_matches[] = {