Unverified Commit 0dbc4947 authored by Mark Brown's avatar Mark Brown
Browse files

spi support for Exynos Auto v9 SoC

Merge series from Chanho Park <chanho61.park@samsung.com>:

Add to support Exynos Auto v9 SoC's spi. By supporting USI(Universal
Serial Interface) mode, the SoC can support up to 12 spi ports. Thus, we
need to increase MAX_SPI_PORTS from 6 to 12. The spi of the SoC can
support loopback mode unlike previous exynos SoCs. To separate the
feature, we need to add .has_loopback to the s3c64xx_spi_port_config.
Furthermore, it uses 4 as the default internal clock divider. We also
need to clk_div field of the structure and assign "2" as the default
value to the existing SoC's port config.
Device tree definitions of exynosautov9-spi will be added in separated
patchset to include usi(i2c/uart/spi) nodes all together.
parents 3f977c57 9dbeef8a
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ properties:
          - samsung,s5pv210-spi # for S5PV210 and S5PC110
          - samsung,exynos4210-spi
          - samsung,exynos5433-spi
          - samsung,exynosautov9-spi
          - tesla,fsd-spi
      - const: samsung,exynos7-spi
        deprecated: true
@@ -86,7 +87,9 @@ allOf:
      properties:
        compatible:
          contains:
            const: samsung,exynos5433-spi
            enum:
              - samsung,exynos5433-spi
              - samsung,exynosautov9-spi
    then:
      properties:
        clocks:
+45 −9
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@

#include <linux/platform_data/spi-s3c64xx.h>

#define MAX_SPI_PORTS		6
#define MAX_SPI_PORTS		12
#define S3C64XX_SPI_QUIRK_POLL		(1 << 0)
#define S3C64XX_SPI_QUIRK_CS_AUTO	(1 << 1)
#define AUTOSUSPEND_TIMEOUT	2000
@@ -59,6 +59,7 @@
#define S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD	(1<<17)
#define S3C64XX_SPI_MODE_BUS_TSZ_WORD		(2<<17)
#define S3C64XX_SPI_MODE_BUS_TSZ_MASK		(3<<17)
#define S3C64XX_SPI_MODE_SELF_LOOPBACK		(1<<3)
#define S3C64XX_SPI_MODE_RXDMA_ON		(1<<2)
#define S3C64XX_SPI_MODE_TXDMA_ON		(1<<1)
#define S3C64XX_SPI_MODE_4BURST			(1<<0)
@@ -130,11 +131,13 @@ struct s3c64xx_spi_dma_data {
 * @fifo_lvl_mask: Bit-mask for {TX|RX}_FIFO_LVL bits in SPI_STATUS register.
 * @rx_lvl_offset: Bit offset of RX_FIFO_LVL bits in SPI_STATUS regiter.
 * @tx_st_done: Bit offset of TX_DONE bit in SPI_STATUS regiter.
 * @clk_div: Internal clock divider
 * @quirks: Bitmask of known quirks
 * @high_speed: True, if the controller supports HIGH_SPEED_EN bit.
 * @clk_from_cmu: True, if the controller does not include a clock mux and
 *	prescaler unit.
 * @clk_ioclk: True if clock is present on this device
 * @has_loopback: True if loopback mode can be supported
 *
 * The Samsung s3c64xx SPI controller are used on various Samsung SoC's but
 * differ in some aspects such as the size of the fifo and spi bus clock
@@ -146,9 +149,11 @@ struct s3c64xx_spi_port_config {
	int	rx_lvl_offset;
	int	tx_st_done;
	int	quirks;
	int	clk_div;
	bool	high_speed;
	bool	clk_from_cmu;
	bool	clk_ioclk;
	bool	has_loopback;
};

/**
@@ -617,6 +622,7 @@ static int s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
	void __iomem *regs = sdd->regs;
	int ret;
	u32 val;
	int div = sdd->port_conf->clk_div;

	/* Disable Clock */
	if (!sdd->port_conf->clk_from_cmu) {
@@ -659,19 +665,21 @@ static int s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
		break;
	}

	if ((sdd->cur_mode & SPI_LOOP) && sdd->port_conf->has_loopback)
		val |= S3C64XX_SPI_MODE_SELF_LOOPBACK;

	writel(val, regs + S3C64XX_SPI_MODE_CFG);

	if (sdd->port_conf->clk_from_cmu) {
		/* The src_clk clock is divided internally by 2 */
		ret = clk_set_rate(sdd->src_clk, sdd->cur_speed * 2);
		ret = clk_set_rate(sdd->src_clk, sdd->cur_speed * div);
		if (ret)
			return ret;
		sdd->cur_speed = clk_get_rate(sdd->src_clk) / 2;
		sdd->cur_speed = clk_get_rate(sdd->src_clk) / div;
	} else {
		/* Configure Clock */
		val = readl(regs + S3C64XX_SPI_CLK_CFG);
		val &= ~S3C64XX_SPI_PSR_MASK;
		val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1)
		val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / div - 1)
				& S3C64XX_SPI_PSR_MASK);
		writel(val, regs + S3C64XX_SPI_CLK_CFG);

@@ -865,6 +873,7 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
	struct s3c64xx_spi_csinfo *cs = spi->controller_data;
	struct s3c64xx_spi_driver_data *sdd;
	int err;
	int div;

	sdd = spi_master_get_devdata(spi->master);
	if (spi->dev.of_node) {
@@ -883,22 +892,24 @@ static int s3c64xx_spi_setup(struct spi_device *spi)

	pm_runtime_get_sync(&sdd->pdev->dev);

	div = sdd->port_conf->clk_div;

	/* Check if we can provide the requested rate */
	if (!sdd->port_conf->clk_from_cmu) {
		u32 psr, speed;

		/* Max possible */
		speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1);
		speed = clk_get_rate(sdd->src_clk) / div / (0 + 1);

		if (spi->max_speed_hz > speed)
			spi->max_speed_hz = speed;

		psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1;
		psr = clk_get_rate(sdd->src_clk) / div / spi->max_speed_hz - 1;
		psr &= S3C64XX_SPI_PSR_MASK;
		if (psr == S3C64XX_SPI_PSR_MASK)
			psr--;

		speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
		speed = clk_get_rate(sdd->src_clk) / div / (psr + 1);
		if (spi->max_speed_hz < speed) {
			if (psr+1 < S3C64XX_SPI_PSR_MASK) {
				psr++;
@@ -908,7 +919,7 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
			}
		}

		speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
		speed = clk_get_rate(sdd->src_clk) / div / (psr + 1);
		if (spi->max_speed_hz >= speed) {
			spi->max_speed_hz = speed;
		} else {
@@ -1148,6 +1159,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
					SPI_BPW_MASK(8);
	/* the spi->mode bits understood by this driver: */
	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
	if (sdd->port_conf->has_loopback)
		master->mode_bits |= SPI_LOOP;
	master->auto_runtime_pm = true;
	if (!is_polling(sdd))
		master->can_dma = s3c64xx_spi_can_dma;
@@ -1388,6 +1401,7 @@ static const struct s3c64xx_spi_port_config s3c2443_spi_port_config = {
	.fifo_lvl_mask	= { 0x7f },
	.rx_lvl_offset	= 13,
	.tx_st_done	= 21,
	.clk_div	= 2,
	.high_speed	= true,
};

@@ -1395,12 +1409,14 @@ static const struct s3c64xx_spi_port_config s3c6410_spi_port_config = {
	.fifo_lvl_mask	= { 0x7f, 0x7F },
	.rx_lvl_offset	= 13,
	.tx_st_done	= 21,
	.clk_div	= 2,
};

static const struct s3c64xx_spi_port_config s5pv210_spi_port_config = {
	.fifo_lvl_mask	= { 0x1ff, 0x7F },
	.rx_lvl_offset	= 15,
	.tx_st_done	= 25,
	.clk_div	= 2,
	.high_speed	= true,
};

@@ -1408,6 +1424,7 @@ static const struct s3c64xx_spi_port_config exynos4_spi_port_config = {
	.fifo_lvl_mask	= { 0x1ff, 0x7F, 0x7F },
	.rx_lvl_offset	= 15,
	.tx_st_done	= 25,
	.clk_div	= 2,
	.high_speed	= true,
	.clk_from_cmu	= true,
	.quirks		= S3C64XX_SPI_QUIRK_CS_AUTO,
@@ -1417,6 +1434,7 @@ static const struct s3c64xx_spi_port_config exynos7_spi_port_config = {
	.fifo_lvl_mask	= { 0x1ff, 0x7F, 0x7F, 0x7F, 0x7F, 0x1ff},
	.rx_lvl_offset	= 15,
	.tx_st_done	= 25,
	.clk_div	= 2,
	.high_speed	= true,
	.clk_from_cmu	= true,
	.quirks		= S3C64XX_SPI_QUIRK_CS_AUTO,
@@ -1426,16 +1444,31 @@ static const struct s3c64xx_spi_port_config exynos5433_spi_port_config = {
	.fifo_lvl_mask	= { 0x1ff, 0x7f, 0x7f, 0x7f, 0x7f, 0x1ff},
	.rx_lvl_offset	= 15,
	.tx_st_done	= 25,
	.clk_div	= 2,
	.high_speed	= true,
	.clk_from_cmu	= true,
	.clk_ioclk	= true,
	.quirks		= S3C64XX_SPI_QUIRK_CS_AUTO,
};

static const struct s3c64xx_spi_port_config exynosautov9_spi_port_config = {
	.fifo_lvl_mask	= { 0x1ff, 0x1ff, 0x7f, 0x7f, 0x7f, 0x7f, 0x1ff, 0x7f,
			    0x7f, 0x7f, 0x7f, 0x7f},
	.rx_lvl_offset	= 15,
	.tx_st_done	= 25,
	.clk_div	= 4,
	.high_speed	= true,
	.clk_from_cmu	= true,
	.clk_ioclk	= true,
	.has_loopback	= true,
	.quirks		= S3C64XX_SPI_QUIRK_CS_AUTO,
};

static const struct s3c64xx_spi_port_config fsd_spi_port_config = {
	.fifo_lvl_mask	= { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f},
	.rx_lvl_offset	= 15,
	.tx_st_done	= 25,
	.clk_div	= 2,
	.high_speed	= true,
	.clk_from_cmu	= true,
	.clk_ioclk	= false,
@@ -1472,6 +1505,9 @@ static const struct of_device_id s3c64xx_spi_dt_match[] = {
	{ .compatible = "samsung,exynos5433-spi",
			.data = (void *)&exynos5433_spi_port_config,
	},
	{ .compatible = "samsung,exynosautov9-spi",
			.data = (void *)&exynosautov9_spi_port_config,
	},
	{ .compatible = "tesla,fsd-spi",
			.data = (void *)&fsd_spi_port_config,
	},