drivers/spi/spi-aspeed.c (375 lines of code) (raw):

// SPDX-License-Identifier: GPL-2.0 /* * ASPEED SPI Controller Driver * * Copyright 2019-present Facebook. All Rights Reserved. * * Borrowed some code from drivers/mtd/spi-nor/aspeed-smc.c. * * Based on Ryan Chen's work in 2012: * Copyright (C) 2012-2020 ASPEED Technology Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/clk.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/ioport.h> #include <linux/module.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/wait.h> #include <linux/delay.h> #include <linux/spi/spi.h> #include <linux/device.h> #include <linux/platform_device.h> #include <linux/of.h> #define ASPEED_SPI_DRIVER "aspeed-spi" #define ASPEED_SPI_CS_NUM 2 #define ASPEED_SUPP_MODES (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH) /* * Register definitions. */ #define ASPEED_CFG_REG 0x00 #define ASPEED_CE_CTRL_REG 0x04 #define ASPEED_CTRL_REG_CE0 0x10 #define ASPEED_CTRL_REG_CE1 0x14 #define ASPEED_ADDR_RANGE_REG_CE0 0x30 #define ASPEED_ADDR_RANGE_REG_CE1 0x34 /* * Fields in SPI Flash Configuration Register SPIR00. */ #define ASPEED_CFG_ENABLE_WR_CE0 BIT(16) #define ASPEED_CFG_ENABLE_WR_CE1 BIT(17) /* * Fields in SPI CE Control Register SPIR04. */ #define ASPEED_CTRL_DIV2_TIMING_CE0 BIT(8) #define ASPEED_CTRL_DIV2_TIMING_CE1 BIT(9) /* * Fields in CE# Control register SPIR10/SPIR14. */ #define ASPEED_CTRL_NORMAL_RD_MODE 0 #define ASPEED_CTRL_RD_CMD_MODE 1 #define ASPEED_CTRL_WR_CMD_MODE 2 #define ASPEED_CTRL_USER_MODE 3 #define ASPEED_CTRL_STOP_ACTIVE BIT(2) #define ASPEED_CTRL_CLK_MODE_3 BIT(4) #define ASPEED_CTRL_CLK_DIV4_MODE BIT(13) #define ASPEED_CTRL_CLK_DIV_MAX 16 #define ASPEED_CTRL_CLK_DIV_MASK (0xF << 8) #define ASPEED_CTRL_CLK_DIV(d) (((d) & 0xF) << 8) #define ASPEED2600_CTRL_CLK_DIV_MASK ((0xF << 24) | (0xF << 8)) #define ASPEED2600_CTRL_CLK_DIV(d) (((d) & 0xF) << 8) | (((d) & 0xF0) << 20) /* * AST2520/AST2500 * The Segment Register uses a 8MB unit to encode the start address * and the end address of the mapping window of a flash SPI slave : * * | byte 1 | byte 2 | byte 3 | byte 4 | * +--------+--------+--------+--------+ * | end | start | 0 | 0 | */ #define ASPEED_2500_SEGMENT_ADDR_START(_v) ((((_v) >> 16) & 0xFF) << 23) #define ASPEED_2500_SEGMENT_ADDR_END(_v) ((((_v) >> 24) & 0xFF) << 23) /* * AST2620/AST2600 * The Segment Register uses a 1MB unit to encode the start address * and the end address of the mapping window of a flash SPI slave : * * bit: | 31-24 | 23-16 | 15-8 | 7-0 | * | byte 1 | byte 2 | byte 3 | byte 4 | * +---------+---------+-----------+-----------+ * | end msb | end lsb | start msb | start lsb | */ #define ASPEED_2600_SEGMENT_ADDR_START(_v) ((_v) & 0x0FF00000) #define ASPEED_2600_SEGMENT_ADDR_END(_v) (((_v) << 16) & 0x0FF00000) // CE0 gets 128MB CE0/CE1 get 64MB each by default #define AST2600_START_OFFSET(cs) (cs == 0 ? 0x0000000 : \ (0x8000000 + (cs - 1)*0x4000000)) #define AST2600_END_OFFSET(cs) (cs == 0 ? 0x8000000 : \ (0x8000000 + (cs)*0x4000000)) #define AST2600_SEGMENT_ADDR_VALUE(start, end) \ (((start & 0x0FF00000) >> 16) | (((end) - 1) & 0x0FF00000)) struct aspeed_spi_priv { void __iomem *reg_base; struct spi_master *master; struct device *dev; bool aspeed_g6; unsigned long ahb_clk_freq; /* * Slave device addresses. */ void __iomem *slave_base; u32 slave_mem_size; struct { void __iomem *start; u32 size; } slave_buf[ASPEED_SPI_CS_NUM]; }; static struct { u32 ctrl_reg; u32 addr_range_reg; u32 enable_write; u32 div2_mode; } cs_reg_map[ASPEED_SPI_CS_NUM] = { [0] = { /* chip select #0 */ .ctrl_reg = ASPEED_CTRL_REG_CE0, .addr_range_reg = ASPEED_ADDR_RANGE_REG_CE0, .enable_write = ASPEED_CFG_ENABLE_WR_CE0, .div2_mode = ASPEED_CTRL_DIV2_TIMING_CE0, }, [1] = { /* chip select #1 */ .ctrl_reg = ASPEED_CTRL_REG_CE1, .addr_range_reg = ASPEED_ADDR_RANGE_REG_CE1, .enable_write = ASPEED_CFG_ENABLE_WR_CE1, .div2_mode = ASPEED_CTRL_DIV2_TIMING_CE1, }, }; static void aspeed_reg_write(struct aspeed_spi_priv *priv, u32 val, u32 reg) { writel(val, priv->reg_base + reg); } static u32 aspeed_reg_read(struct aspeed_spi_priv *priv, u32 reg) { return readl(priv->reg_base + reg); } static void aspeed_activate_cs(struct aspeed_spi_priv *priv, u8 cs) { u32 ctrl_reg = cs_reg_map[cs].ctrl_reg; u32 val = aspeed_reg_read(priv, ctrl_reg); val &= ~ASPEED_CTRL_STOP_ACTIVE; aspeed_reg_write(priv, val, ctrl_reg); } static void aspeed_deactivate_cs(struct aspeed_spi_priv *priv, u8 cs) { u32 ctrl_reg = cs_reg_map[cs].ctrl_reg; u32 val = aspeed_reg_read(priv, ctrl_reg); val |= ASPEED_CTRL_STOP_ACTIVE; aspeed_reg_write(priv, val, ctrl_reg); } static void aspeed2600_set_default_range(struct aspeed_spi_priv *priv, struct resource *res, u16 num_cs) { u16 cs; u32 start_offset; for (cs = 0; cs < num_cs; cs++) { u32 addr_range_reg = cs_reg_map[cs].addr_range_reg; u32 val = aspeed_reg_read(priv, addr_range_reg); val = AST2600_SEGMENT_ADDR_VALUE(res->start + AST2600_START_OFFSET(cs), res->start + AST2600_END_OFFSET(cs)); aspeed_reg_write(priv, val, addr_range_reg); } } static bool aspeed_check_set_div2(struct aspeed_spi_priv *priv, u8 cs, u32 spi_max_freq) { u32 div, val; div = priv->ahb_clk_freq / spi_max_freq; if (div <= ASPEED_CTRL_CLK_DIV_MAX) return false; val = aspeed_reg_read(priv, ASPEED_CE_CTRL_REG); val |= cs_reg_map[cs].div2_mode; aspeed_reg_write(priv, val, ASPEED_CE_CTRL_REG); return true; } /* * Calculate spi clock frequency divisor. Refer to AST2500 datasheet, * Chapter 14, CE# Control Register, bit 11:8 for details. */ static u32 aspeed_spi_clk_div(unsigned long ahb_clk_freq, u32 spi_max_freq) { unsigned long i; u32 div_val = 0; static const u32 div_map[ASPEED_CTRL_CLK_DIV_MAX] = { 15, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 0, }; for (i = 1; i <= ASPEED_CTRL_CLK_DIV_MAX; i++) { if ((spi_max_freq * i) >= ahb_clk_freq) { div_val = div_map[i - 1]; break; } } return div_val; } /* * Calculate spi clock frequency divisor. Refer to AST2600 datasheet, * Chapter 15, CE# control registers SPIR10/14/18, bit 27:24, 11:8. */ static u32 aspeed2600_spi_clk_div(unsigned long ahb_clk_freq, u32 spi_max_freq) { unsigned long i, j; u32 div_val = 0; u8 base_div = 0; bool done = 0; static const u32 div_map[ASPEED_CTRL_CLK_DIV_MAX] = { 15, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 0, }; for (j = 0; j < 0xF; j++) { for (i = 1; i <= ASPEED_CTRL_CLK_DIV_MAX; i++) { base_div = j*16; if ((spi_max_freq * (i+base_div)) >= ahb_clk_freq) { div_val = (j << 4) | div_map[i - 1]; done = 1; break; } } if (done) break; } return div_val; } static int aspeed_spi_setup(struct spi_device *slave) { u8 cs = slave->chip_select; u32 div, val, ctrl_reg, freq; struct aspeed_spi_priv *priv = spi_master_get_devdata(slave->master); if (cs == 0 && slave->mode & SPI_CS_HIGH) { dev_err(&slave->dev, "chip_select %u cannot be active-high\n", cs); return -EINVAL; } /* * Update SPIR00 (Configuration Register). */ val = aspeed_reg_read(priv, ASPEED_CFG_REG); val |= cs_reg_map[cs].enable_write; aspeed_reg_write(priv, val, ASPEED_CFG_REG); /* * Update CE# Control Register. */ ctrl_reg = cs_reg_map[cs].ctrl_reg; val = aspeed_reg_read(priv, ctrl_reg); val &= ~ASPEED_CTRL_CLK_MODE_3; /* clock mode 3 is not supported */ if (slave->max_speed_hz != 0) { freq = slave->max_speed_hz; if (priv->aspeed_g6) { val &= ~ASPEED2600_CTRL_CLK_DIV_MASK; div = aspeed2600_spi_clk_div(priv->ahb_clk_freq, freq); val |= ASPEED2600_CTRL_CLK_DIV(div); } else { val &= ~ASPEED_CTRL_CLK_DIV_MASK; if (aspeed_check_set_div2(priv, cs, freq)) { // SPI clock divided by 4 val |= ASPEED_CTRL_CLK_DIV4_MODE; freq = freq >> 2; } div = aspeed_spi_clk_div(priv->ahb_clk_freq, freq); val |= ASPEED_CTRL_CLK_DIV(div); } } aspeed_reg_write(priv, val, ctrl_reg); return 0; } static void aspeed_spi_set_cs(struct spi_device *slave, bool level) { u8 cs = slave->chip_select; struct aspeed_spi_priv *priv = spi_master_get_devdata(slave->master); if (level) aspeed_deactivate_cs(priv, cs); else aspeed_activate_cs(priv, cs); } static void aspeed_spi_do_xfer(struct aspeed_spi_priv *priv, struct spi_transfer *xfer, u32 cs) { unsigned int i; u8 *rx_buf = xfer->rx_buf; const u8 *tx_buf = xfer->tx_buf; void *slave_buf = priv->slave_buf[cs].start; if (tx_buf != NULL) { for (i = 0; i < xfer->len; i++) writeb(tx_buf[i], slave_buf); } if (rx_buf != NULL) { for (i = 0; i < xfer->len; i++) rx_buf[i] = readb(slave_buf); } } static int aspeed_spi_xfer_one(struct spi_master *master, struct spi_device *slave, struct spi_transfer *xfer) { struct aspeed_spi_priv *priv = spi_master_get_devdata(master); u8 cs = slave->chip_select; aspeed_spi_do_xfer(priv, xfer, cs); return 0; } static void aspeed_spi_init_hw(struct aspeed_spi_priv *priv, u16 num_cs) { u16 cs; for (cs = 0; cs < num_cs; cs++) { u32 ctrl_reg = cs_reg_map[cs].ctrl_reg; u32 val = aspeed_reg_read(priv, ctrl_reg); val |= (ASPEED_CTRL_STOP_ACTIVE | ASPEED_CTRL_USER_MODE); aspeed_reg_write(priv, val, ctrl_reg); } } static int aspeed_spi_init_slave_buf(struct aspeed_spi_priv *priv, struct resource *res, u16 num_cs) { u16 cs; for (cs = 0; cs < num_cs; cs++) { u32 val, start, end, size, offset; val = aspeed_reg_read(priv, cs_reg_map[cs].addr_range_reg); if (priv->aspeed_g6) { start = res->start + ASPEED_2600_SEGMENT_ADDR_START(val); end = res->start + ASPEED_2600_SEGMENT_ADDR_END(val); } else { start = ASPEED_2500_SEGMENT_ADDR_START(val); end = ASPEED_2500_SEGMENT_ADDR_START(val); } size = end - start; if (start < res->start) { dev_err(priv->dev, "cs %u: invalid start address %#lx\n", cs, (unsigned long)start); return -EINVAL; } offset = start - res->start; if (offset + size > priv->slave_mem_size) { dev_err(priv->dev, "cs %u: invalid address range (%#lx - %#lx)\n", cs, (unsigned long)start, (unsigned long)(start + size)); return -EINVAL; } priv->slave_buf[cs].start = priv->slave_base + offset; priv->slave_buf[cs].size = size; } return 0; } static int aspeed_spi_probe(struct platform_device *pdev) { u32 slave_mem_size; struct resource *res; void __iomem *reg_base, *slave_base; struct clk *clk; struct aspeed_spi_priv *priv; struct spi_master *master; int error; unsigned long ahb_clk_freq; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); reg_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(reg_base)) return PTR_ERR(reg_base); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); slave_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(slave_base)) return PTR_ERR(slave_base); slave_mem_size = resource_size(res); clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) return PTR_ERR(clk); ahb_clk_freq = clk_get_rate(clk); devm_clk_put(&pdev->dev, clk); master = spi_alloc_master(&pdev->dev, sizeof(struct aspeed_spi_priv)); if (master == NULL) { dev_err(&pdev->dev, "failed to allocate spi_master\n"); return -ENOMEM; } master->flags = SPI_MASTER_HALF_DUPLEX; master->mode_bits = ASPEED_SUPP_MODES; master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); master->dev.of_node = pdev->dev.of_node; master->num_chipselect = ASPEED_SPI_CS_NUM; master->setup = aspeed_spi_setup; master->set_cs = aspeed_spi_set_cs; master->transfer_one = aspeed_spi_xfer_one; priv = spi_master_get_devdata(master); priv->dev = &pdev->dev; priv->master = master; priv->reg_base = reg_base; priv->slave_base = slave_base; priv->slave_mem_size = slave_mem_size; priv->ahb_clk_freq = ahb_clk_freq; platform_set_drvdata(pdev, priv); if (of_device_is_compatible(master->dev.of_node, "aspeed,ast2600-spi-master")) { priv->aspeed_g6 = true; // AST2600: CE0 gets 128MB CE0/CE1 get 64MB each by default aspeed2600_set_default_range(priv, res, master->num_chipselect); } error = aspeed_spi_init_slave_buf(priv, res, master->num_chipselect); if (error != 0) goto err_exit; aspeed_spi_init_hw(priv, master->num_chipselect); /* Register our spi controller */ error = devm_spi_register_master(&pdev->dev, master); if (error) { dev_err(&pdev->dev, "failed to register SPI master\n"); goto err_exit; } return 0; err_exit: spi_master_put(master); dev_err(&pdev->dev, "%s returned error %d\n", __func__, error); return error; } static int aspeed_spi_remove(struct platform_device *pdev) { struct aspeed_spi_priv *priv = platform_get_drvdata(pdev); if (priv == NULL) return -1; platform_set_drvdata(pdev, NULL); spi_master_put(priv->master); return 0; } static const struct of_device_id aspeed_spi_of_match[] = { { .compatible = "aspeed,ast2500-spi-master", }, { .compatible = "aspeed,ast2600-spi-master", }, {} }; MODULE_DEVICE_TABLE(of, aspeed_spi_of_match); static struct platform_driver aspeed_spi_driver = { .probe = aspeed_spi_probe, .remove = aspeed_spi_remove, .driver = { .name = ASPEED_SPI_DRIVER, .of_match_table = aspeed_spi_of_match, }, }; module_platform_driver(aspeed_spi_driver); MODULE_AUTHOR("Tao Ren <taoren@fb.com>"); MODULE_DESCRIPTION("ASPEED SPI Host Driver"); MODULE_LICENSE("GPL");