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);
+ 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);
};
}
static const u32 aspeed_spi_hclk_divs[] = {
- 0xf, /* HCLK */
- 0x7, /* HCLK/2 */
- 0xe, /* HCLK/3 */
- 0x6, /* HCLK/4 */
- 0xd, /* HCLK/5 */
+ /* HCLK, HCLK/2, HCLK/3, HCLK/4, HCLK/5, ..., HCLK/16 */
+ 0xf, 0x7, 0xe, 0x6, 0xd,
+ 0x5, 0xc, 0x4, 0xb, 0x3,
+ 0xa, 0x2, 0x9, 0x1, 0x8,
+ 0x0
};
#define ASPEED_SPI_HCLK_DIV(i) \
(aspeed_spi_hclk_divs[(i) - 1] << CTRL_FREQ_SEL_SHIFT)
+/* Transfer maximum clock frequency to register setting */
+static u32 aspeed_get_clk_div_ast2400(struct aspeed_spi_chip *chip,
+ u32 max_hz)
+{
+ struct device *dev = chip->aspi->dev;
+ u32 hclk_clk = chip->aspi->clk_freq;
+ u32 div_ctl = 0;
+ u32 i;
+ bool found = false;
+
+ /* FMC/SPIR10[11:8] */
+ for (i = 1; i <= ARRAY_SIZE(aspeed_spi_hclk_divs); i++) {
+ if (hclk_clk / i <= max_hz) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ div_ctl = ASPEED_SPI_HCLK_DIV(i);
+ chip->clk_freq = hclk_clk / i;
+ }
+
+ dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n",
+ found ? "yes" : "no", hclk_clk, max_hz);
+
+ if (found) {
+ dev_dbg(dev, "h_div: 0x%08x, speed: %d\n",
+ div_ctl, chip->clk_freq);
+ }
+
+ return div_ctl;
+}
+
+static u32 aspeed_get_clk_div_ast2500(struct aspeed_spi_chip *chip,
+ u32 max_hz)
+{
+ struct device *dev = chip->aspi->dev;
+ u32 hclk_clk = chip->aspi->clk_freq;
+ u32 div_ctl = 0;
+ u32 i;
+ bool found = false;
+
+ /* FMC/SPIR10[11:8] */
+ for (i = 1; i <= ARRAY_SIZE(aspeed_spi_hclk_divs); i++) {
+ if (hclk_clk / i <= max_hz) {
+ found = true;
+ chip->clk_freq = hclk_clk / i;
+ break;
+ }
+ }
+
+ if (found) {
+ div_ctl = ASPEED_SPI_HCLK_DIV(i);
+ goto end;
+ }
+
+ for (i = 1; i <= ARRAY_SIZE(aspeed_spi_hclk_divs); i++) {
+ if (hclk_clk / (i * 4) <= max_hz) {
+ found = true;
+ chip->clk_freq = hclk_clk / (i * 4);
+ break;
+ }
+ }
+
+ if (found)
+ div_ctl = BIT(13) | ASPEED_SPI_HCLK_DIV(i);
+
+end:
+ dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n",
+ found ? "yes" : "no", hclk_clk, max_hz);
+
+ if (found) {
+ dev_dbg(dev, "h_div: 0x%08x, speed: %d\n",
+ div_ctl, chip->clk_freq);
+ }
+
+ return div_ctl;
+}
+
+static u32 aspeed_get_clk_div_ast2600(struct aspeed_spi_chip *chip,
+ u32 max_hz)
+{
+ struct device *dev = chip->aspi->dev;
+ u32 hclk_clk = chip->aspi->clk_freq;
+ u32 div_ctl = 0;
+ u32 i, j;
+ bool found = false;
+
+ /* FMC/SPIR10[27:24] */
+ for (j = 0; j < 16; j++) {
+ /* FMC/SPIR10[11:8] */
+ for (i = 1; i <= ARRAY_SIZE(aspeed_spi_hclk_divs); i++) {
+ if (j == 0 && i == 1)
+ continue;
+
+ if (hclk_clk / (j * 16 + i) <= max_hz) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ div_ctl = ((j << 24) | ASPEED_SPI_HCLK_DIV(i));
+ chip->clk_freq = hclk_clk / (j * 16 + i);
+ break;
+ }
+ }
+
+ dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n",
+ found ? "yes" : "no", hclk_clk, max_hz);
+
+ if (found) {
+ dev_dbg(dev, "h_div: 0x%08x, speed: %d\n",
+ div_ctl, chip->clk_freq);
+ }
+
+ return div_ctl;
+}
+
static int aspeed_spi_do_calibration(struct aspeed_spi_chip *chip)
{
struct aspeed_spi *aspi = chip->aspi;
const struct aspeed_spi_data *data = aspi->data;
u32 ahb_freq = aspi->clk_freq;
u32 max_freq = chip->clk_freq;
+ bool exec_calib = false;
+ u32 best_freq = 0;
u32 ctl_val;
u8 *golden_buf = NULL;
u8 *test_buf = NULL;
- int i, rc, best_div = -1;
+ int i, rc;
+ u32 div_ctl;
dev_dbg(aspi->dev, "calculate timing compensation - AHB freq: %d MHz",
ahb_freq / 1000000);
memcpy_fromio(golden_buf, chip->ahb_base, CALIBRATE_BUF_SIZE);
if (!aspeed_spi_check_calib_data(golden_buf, CALIBRATE_BUF_SIZE)) {
dev_info(aspi->dev, "Calibration area too uniform, using low speed");
- goto no_calib;
+ goto end_calib;
}
#if defined(VERBOSE_DEBUG)
#endif
/* Now we iterate the HCLK dividers until we find our breaking point */
- for (i = ARRAY_SIZE(aspeed_spi_hclk_divs); i > data->hdiv_max - 1; i--) {
+ for (i = 5; i > data->hdiv_max - 1; i--) {
u32 tv, freq;
freq = ahb_freq / i;
dev_dbg(aspi->dev, "Trying HCLK/%d [%08x] ...", i, tv);
rc = data->calibrate(chip, i, golden_buf, test_buf);
if (rc == 0)
- best_div = i;
+ best_freq = freq;
+
+ exec_calib = true;
}
- /* Nothing found ? */
- if (best_div < 0) {
- dev_warn(aspi->dev, "No good frequency, using dumb slow");
+end_calib:
+ if (!exec_calib) {
+ /* calibration process is not executed */
+ dev_warn(aspi->dev, "Force to dts configuration %dkHz.\n",
+ max_freq / 1000);
+ div_ctl = data->get_clk_div(chip, max_freq);
+ } else if (best_freq == 0) {
+ /* calibration process is executed, but no good frequency */
+ dev_warn(aspi->dev, "No good frequency, using dumb slow\n");
+ div_ctl = 0;
} else {
- dev_dbg(aspi->dev, "Found good read timings at HCLK/%d", best_div);
+ dev_dbg(aspi->dev, "Found good read timings at %dMHz.\n",
+ best_freq / 1000000);
+ div_ctl = data->get_clk_div(chip, best_freq);
+ }
- /* Record the freq */
- for (i = 0; i < ASPEED_SPI_MAX; i++)
- chip->ctl_val[i] = (chip->ctl_val[i] & data->hclk_mask) |
- ASPEED_SPI_HCLK_DIV(best_div);
+ /* Record the freq */
+ for (i = 0; i < ASPEED_SPI_MAX; i++) {
+ chip->ctl_val[i] = (chip->ctl_val[i] & data->hclk_mask) |
+ div_ctl;
}
-no_calib:
writel(chip->ctl_val[ASPEED_SPI_READ], chip->ctl);
kfree(test_buf);
return 0;
.hclk_mask = 0xfffff0ff,
.hdiv_max = 1,
.calibrate = aspeed_spi_calibrate,
+ .get_clk_div = aspeed_get_clk_div_ast2400,
.segment_start = aspeed_spi_segment_start,
.segment_end = aspeed_spi_segment_end,
.segment_reg = aspeed_spi_segment_reg,
.timing = 0x14,
.hclk_mask = 0xfffff0ff,
.hdiv_max = 1,
+ .get_clk_div = aspeed_get_clk_div_ast2400,
.calibrate = aspeed_spi_calibrate,
/* No segment registers */
};
.timing = CE0_TIMING_COMPENSATION_REG,
.hclk_mask = 0xffffd0ff,
.hdiv_max = 1,
+ .get_clk_div = aspeed_get_clk_div_ast2500,
.calibrate = aspeed_spi_calibrate,
.segment_start = aspeed_spi_segment_start,
.segment_end = aspeed_spi_segment_end,
.timing = CE0_TIMING_COMPENSATION_REG,
.hclk_mask = 0xffffd0ff,
.hdiv_max = 1,
+ .get_clk_div = aspeed_get_clk_div_ast2500,
.calibrate = aspeed_spi_calibrate,
.segment_start = aspeed_spi_segment_start,
.segment_end = aspeed_spi_segment_end,
.timing = CE0_TIMING_COMPENSATION_REG,
.hclk_mask = 0xf0fff0ff,
.hdiv_max = 2,
+ .get_clk_div = aspeed_get_clk_div_ast2600,
.calibrate = aspeed_spi_ast2600_calibrate,
.segment_start = aspeed_spi_segment_ast2600_start,
.segment_end = aspeed_spi_segment_ast2600_end,
.timing = CE0_TIMING_COMPENSATION_REG,
.hclk_mask = 0xf0fff0ff,
.hdiv_max = 2,
+ .get_clk_div = aspeed_get_clk_div_ast2600,
.calibrate = aspeed_spi_ast2600_calibrate,
.segment_start = aspeed_spi_segment_ast2600_start,
.segment_end = aspeed_spi_segment_ast2600_end,