mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 07:33:19 +02:00
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.
This commit is contained in:
commit
0dbc49476a
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user