Merge remote-tracking branches 'spi/topic/ath79', 'spi/topic/atmel' and 'spi/topic/davinci' into spi-next
This commit is contained in:
commit
9a8d141d5a
|
@ -0,0 +1,24 @@
|
||||||
|
Binding for Qualcomm Atheros AR7xxx/AR9xxx SPI controller
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: has to be "qca,<soc-type>-spi", "qca,ar7100-spi" as fallback.
|
||||||
|
- reg: Base address and size of the controllers memory area
|
||||||
|
- clocks: phandle to the AHB clock.
|
||||||
|
- clock-names: has to be "ahb".
|
||||||
|
- #address-cells: <1>, as required by generic SPI binding.
|
||||||
|
- #size-cells: <0>, also as required by generic SPI binding.
|
||||||
|
|
||||||
|
Child nodes as per the generic SPI binding.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
spi@1F000000 {
|
||||||
|
compatible = "qca,ar9132-spi", "qca,ar7100-spi";
|
||||||
|
reg = <0x1F000000 0x10>;
|
||||||
|
|
||||||
|
clocks = <&pll 2>;
|
||||||
|
clock-names = "ahb";
|
||||||
|
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
};
|
|
@ -4,11 +4,16 @@ Required properties:
|
||||||
- compatible : should be "atmel,at91rm9200-spi".
|
- compatible : should be "atmel,at91rm9200-spi".
|
||||||
- reg: Address and length of the register set for the device
|
- reg: Address and length of the register set for the device
|
||||||
- interrupts: Should contain spi interrupt
|
- interrupts: Should contain spi interrupt
|
||||||
- cs-gpios: chipselects
|
- cs-gpios: chipselects (optional for SPI controller version >= 2 with the
|
||||||
|
Chip Select Active After Transfer feature).
|
||||||
- clock-names: tuple listing input clock names.
|
- clock-names: tuple listing input clock names.
|
||||||
Required elements: "spi_clk"
|
Required elements: "spi_clk"
|
||||||
- clocks: phandles to input clocks.
|
- clocks: phandles to input clocks.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- atmel,fifo-size: maximum number of data the RX and TX FIFOs can store for FIFO
|
||||||
|
capable SPI controllers.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
spi1: spi@fffcc000 {
|
spi1: spi@fffcc000 {
|
||||||
|
@ -20,6 +25,7 @@ spi1: spi@fffcc000 {
|
||||||
clocks = <&spi1_clk>;
|
clocks = <&spi1_clk>;
|
||||||
clock-names = "spi_clk";
|
clock-names = "spi_clk";
|
||||||
cs-gpios = <&pioB 3 0>;
|
cs-gpios = <&pioB 3 0>;
|
||||||
|
atmel,fifo-size = <32>;
|
||||||
status = "okay";
|
status = "okay";
|
||||||
|
|
||||||
mmc-slot@0 {
|
mmc-slot@0 {
|
||||||
|
|
|
@ -16,8 +16,4 @@ struct ath79_spi_platform_data {
|
||||||
unsigned num_chipselect;
|
unsigned num_chipselect;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ath79_spi_controller_data {
|
|
||||||
unsigned gpio;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _ATH79_SPI_PLATFORM_H */
|
#endif /* _ATH79_SPI_PLATFORM_H */
|
||||||
|
|
|
@ -79,10 +79,8 @@ static void ath79_spi_chipselect(struct spi_device *spi, int is_active)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spi->chip_select) {
|
if (spi->chip_select) {
|
||||||
struct ath79_spi_controller_data *cdata = spi->controller_data;
|
|
||||||
|
|
||||||
/* SPI is normally active-low */
|
/* SPI is normally active-low */
|
||||||
gpio_set_value(cdata->gpio, cs_high);
|
gpio_set_value(spi->cs_gpio, cs_high);
|
||||||
} else {
|
} else {
|
||||||
if (cs_high)
|
if (cs_high)
|
||||||
sp->ioc_base |= AR71XX_SPI_IOC_CS0;
|
sp->ioc_base |= AR71XX_SPI_IOC_CS0;
|
||||||
|
@ -117,11 +115,10 @@ static void ath79_spi_disable(struct ath79_spi *sp)
|
||||||
|
|
||||||
static int ath79_spi_setup_cs(struct spi_device *spi)
|
static int ath79_spi_setup_cs(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct ath79_spi_controller_data *cdata;
|
struct ath79_spi *sp = ath79_spidev_to_sp(spi);
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
cdata = spi->controller_data;
|
if (spi->chip_select && !gpio_is_valid(spi->cs_gpio))
|
||||||
if (spi->chip_select && !cdata)
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
status = 0;
|
status = 0;
|
||||||
|
@ -134,8 +131,15 @@ static int ath79_spi_setup_cs(struct spi_device *spi)
|
||||||
else
|
else
|
||||||
flags |= GPIOF_INIT_HIGH;
|
flags |= GPIOF_INIT_HIGH;
|
||||||
|
|
||||||
status = gpio_request_one(cdata->gpio, flags,
|
status = gpio_request_one(spi->cs_gpio, flags,
|
||||||
dev_name(&spi->dev));
|
dev_name(&spi->dev));
|
||||||
|
} else {
|
||||||
|
if (spi->mode & SPI_CS_HIGH)
|
||||||
|
sp->ioc_base &= ~AR71XX_SPI_IOC_CS0;
|
||||||
|
else
|
||||||
|
sp->ioc_base |= AR71XX_SPI_IOC_CS0;
|
||||||
|
|
||||||
|
ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
@ -144,8 +148,7 @@ static int ath79_spi_setup_cs(struct spi_device *spi)
|
||||||
static void ath79_spi_cleanup_cs(struct spi_device *spi)
|
static void ath79_spi_cleanup_cs(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
if (spi->chip_select) {
|
if (spi->chip_select) {
|
||||||
struct ath79_spi_controller_data *cdata = spi->controller_data;
|
gpio_free(spi->cs_gpio);
|
||||||
gpio_free(cdata->gpio);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,6 +220,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
sp = spi_master_get_devdata(master);
|
sp = spi_master_get_devdata(master);
|
||||||
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
platform_set_drvdata(pdev, sp);
|
platform_set_drvdata(pdev, sp);
|
||||||
|
|
||||||
pdata = dev_get_platdata(&pdev->dev);
|
pdata = dev_get_platdata(&pdev->dev);
|
||||||
|
@ -253,7 +257,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
|
||||||
goto err_put_master;
|
goto err_put_master;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = clk_enable(sp->clk);
|
ret = clk_prepare_enable(sp->clk);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_put_master;
|
goto err_put_master;
|
||||||
|
|
||||||
|
@ -277,7 +281,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
|
||||||
err_disable:
|
err_disable:
|
||||||
ath79_spi_disable(sp);
|
ath79_spi_disable(sp);
|
||||||
err_clk_disable:
|
err_clk_disable:
|
||||||
clk_disable(sp->clk);
|
clk_disable_unprepare(sp->clk);
|
||||||
err_put_master:
|
err_put_master:
|
||||||
spi_master_put(sp->bitbang.master);
|
spi_master_put(sp->bitbang.master);
|
||||||
|
|
||||||
|
@ -290,7 +294,7 @@ static int ath79_spi_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
spi_bitbang_stop(&sp->bitbang);
|
spi_bitbang_stop(&sp->bitbang);
|
||||||
ath79_spi_disable(sp);
|
ath79_spi_disable(sp);
|
||||||
clk_disable(sp->clk);
|
clk_disable_unprepare(sp->clk);
|
||||||
spi_master_put(sp->bitbang.master);
|
spi_master_put(sp->bitbang.master);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -301,12 +305,18 @@ static void ath79_spi_shutdown(struct platform_device *pdev)
|
||||||
ath79_spi_remove(pdev);
|
ath79_spi_remove(pdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id ath79_spi_of_match[] = {
|
||||||
|
{ .compatible = "qca,ar7100-spi", },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
|
||||||
static struct platform_driver ath79_spi_driver = {
|
static struct platform_driver ath79_spi_driver = {
|
||||||
.probe = ath79_spi_probe,
|
.probe = ath79_spi_probe,
|
||||||
.remove = ath79_spi_remove,
|
.remove = ath79_spi_remove,
|
||||||
.shutdown = ath79_spi_shutdown,
|
.shutdown = ath79_spi_shutdown,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRV_NAME,
|
.name = DRV_NAME,
|
||||||
|
.of_match_table = ath79_spi_of_match,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
module_platform_driver(ath79_spi_driver);
|
module_platform_driver(ath79_spi_driver);
|
||||||
|
|
|
@ -41,6 +41,8 @@
|
||||||
#define SPI_CSR1 0x0034
|
#define SPI_CSR1 0x0034
|
||||||
#define SPI_CSR2 0x0038
|
#define SPI_CSR2 0x0038
|
||||||
#define SPI_CSR3 0x003c
|
#define SPI_CSR3 0x003c
|
||||||
|
#define SPI_FMR 0x0040
|
||||||
|
#define SPI_FLR 0x0044
|
||||||
#define SPI_VERSION 0x00fc
|
#define SPI_VERSION 0x00fc
|
||||||
#define SPI_RPR 0x0100
|
#define SPI_RPR 0x0100
|
||||||
#define SPI_RCR 0x0104
|
#define SPI_RCR 0x0104
|
||||||
|
@ -62,6 +64,14 @@
|
||||||
#define SPI_SWRST_SIZE 1
|
#define SPI_SWRST_SIZE 1
|
||||||
#define SPI_LASTXFER_OFFSET 24
|
#define SPI_LASTXFER_OFFSET 24
|
||||||
#define SPI_LASTXFER_SIZE 1
|
#define SPI_LASTXFER_SIZE 1
|
||||||
|
#define SPI_TXFCLR_OFFSET 16
|
||||||
|
#define SPI_TXFCLR_SIZE 1
|
||||||
|
#define SPI_RXFCLR_OFFSET 17
|
||||||
|
#define SPI_RXFCLR_SIZE 1
|
||||||
|
#define SPI_FIFOEN_OFFSET 30
|
||||||
|
#define SPI_FIFOEN_SIZE 1
|
||||||
|
#define SPI_FIFODIS_OFFSET 31
|
||||||
|
#define SPI_FIFODIS_SIZE 1
|
||||||
|
|
||||||
/* Bitfields in MR */
|
/* Bitfields in MR */
|
||||||
#define SPI_MSTR_OFFSET 0
|
#define SPI_MSTR_OFFSET 0
|
||||||
|
@ -114,6 +124,22 @@
|
||||||
#define SPI_TXEMPTY_SIZE 1
|
#define SPI_TXEMPTY_SIZE 1
|
||||||
#define SPI_SPIENS_OFFSET 16
|
#define SPI_SPIENS_OFFSET 16
|
||||||
#define SPI_SPIENS_SIZE 1
|
#define SPI_SPIENS_SIZE 1
|
||||||
|
#define SPI_TXFEF_OFFSET 24
|
||||||
|
#define SPI_TXFEF_SIZE 1
|
||||||
|
#define SPI_TXFFF_OFFSET 25
|
||||||
|
#define SPI_TXFFF_SIZE 1
|
||||||
|
#define SPI_TXFTHF_OFFSET 26
|
||||||
|
#define SPI_TXFTHF_SIZE 1
|
||||||
|
#define SPI_RXFEF_OFFSET 27
|
||||||
|
#define SPI_RXFEF_SIZE 1
|
||||||
|
#define SPI_RXFFF_OFFSET 28
|
||||||
|
#define SPI_RXFFF_SIZE 1
|
||||||
|
#define SPI_RXFTHF_OFFSET 29
|
||||||
|
#define SPI_RXFTHF_SIZE 1
|
||||||
|
#define SPI_TXFPTEF_OFFSET 30
|
||||||
|
#define SPI_TXFPTEF_SIZE 1
|
||||||
|
#define SPI_RXFPTEF_OFFSET 31
|
||||||
|
#define SPI_RXFPTEF_SIZE 1
|
||||||
|
|
||||||
/* Bitfields in CSR0 */
|
/* Bitfields in CSR0 */
|
||||||
#define SPI_CPOL_OFFSET 0
|
#define SPI_CPOL_OFFSET 0
|
||||||
|
@ -157,6 +183,22 @@
|
||||||
#define SPI_TXTDIS_OFFSET 9
|
#define SPI_TXTDIS_OFFSET 9
|
||||||
#define SPI_TXTDIS_SIZE 1
|
#define SPI_TXTDIS_SIZE 1
|
||||||
|
|
||||||
|
/* Bitfields in FMR */
|
||||||
|
#define SPI_TXRDYM_OFFSET 0
|
||||||
|
#define SPI_TXRDYM_SIZE 2
|
||||||
|
#define SPI_RXRDYM_OFFSET 4
|
||||||
|
#define SPI_RXRDYM_SIZE 2
|
||||||
|
#define SPI_TXFTHRES_OFFSET 16
|
||||||
|
#define SPI_TXFTHRES_SIZE 6
|
||||||
|
#define SPI_RXFTHRES_OFFSET 24
|
||||||
|
#define SPI_RXFTHRES_SIZE 6
|
||||||
|
|
||||||
|
/* Bitfields in FLR */
|
||||||
|
#define SPI_TXFL_OFFSET 0
|
||||||
|
#define SPI_TXFL_SIZE 6
|
||||||
|
#define SPI_RXFL_OFFSET 16
|
||||||
|
#define SPI_RXFL_SIZE 6
|
||||||
|
|
||||||
/* Constants for BITS */
|
/* Constants for BITS */
|
||||||
#define SPI_BITS_8_BPT 0
|
#define SPI_BITS_8_BPT 0
|
||||||
#define SPI_BITS_9_BPT 1
|
#define SPI_BITS_9_BPT 1
|
||||||
|
@ -167,6 +209,9 @@
|
||||||
#define SPI_BITS_14_BPT 6
|
#define SPI_BITS_14_BPT 6
|
||||||
#define SPI_BITS_15_BPT 7
|
#define SPI_BITS_15_BPT 7
|
||||||
#define SPI_BITS_16_BPT 8
|
#define SPI_BITS_16_BPT 8
|
||||||
|
#define SPI_ONE_DATA 0
|
||||||
|
#define SPI_TWO_DATA 1
|
||||||
|
#define SPI_FOUR_DATA 2
|
||||||
|
|
||||||
/* Bit manipulation macros */
|
/* Bit manipulation macros */
|
||||||
#define SPI_BIT(name) \
|
#define SPI_BIT(name) \
|
||||||
|
@ -185,11 +230,31 @@
|
||||||
__raw_readl((port)->regs + SPI_##reg)
|
__raw_readl((port)->regs + SPI_##reg)
|
||||||
#define spi_writel(port, reg, value) \
|
#define spi_writel(port, reg, value) \
|
||||||
__raw_writel((value), (port)->regs + SPI_##reg)
|
__raw_writel((value), (port)->regs + SPI_##reg)
|
||||||
|
|
||||||
|
#define spi_readw(port, reg) \
|
||||||
|
__raw_readw((port)->regs + SPI_##reg)
|
||||||
|
#define spi_writew(port, reg, value) \
|
||||||
|
__raw_writew((value), (port)->regs + SPI_##reg)
|
||||||
|
|
||||||
|
#define spi_readb(port, reg) \
|
||||||
|
__raw_readb((port)->regs + SPI_##reg)
|
||||||
|
#define spi_writeb(port, reg, value) \
|
||||||
|
__raw_writeb((value), (port)->regs + SPI_##reg)
|
||||||
#else
|
#else
|
||||||
#define spi_readl(port, reg) \
|
#define spi_readl(port, reg) \
|
||||||
readl_relaxed((port)->regs + SPI_##reg)
|
readl_relaxed((port)->regs + SPI_##reg)
|
||||||
#define spi_writel(port, reg, value) \
|
#define spi_writel(port, reg, value) \
|
||||||
writel_relaxed((value), (port)->regs + SPI_##reg)
|
writel_relaxed((value), (port)->regs + SPI_##reg)
|
||||||
|
|
||||||
|
#define spi_readw(port, reg) \
|
||||||
|
readw_relaxed((port)->regs + SPI_##reg)
|
||||||
|
#define spi_writew(port, reg, value) \
|
||||||
|
writew_relaxed((value), (port)->regs + SPI_##reg)
|
||||||
|
|
||||||
|
#define spi_readb(port, reg) \
|
||||||
|
readb_relaxed((port)->regs + SPI_##reg)
|
||||||
|
#define spi_writeb(port, reg, value) \
|
||||||
|
writeb_relaxed((value), (port)->regs + SPI_##reg)
|
||||||
#endif
|
#endif
|
||||||
/* use PIO for small transfers, avoiding DMA setup/teardown overhead and
|
/* use PIO for small transfers, avoiding DMA setup/teardown overhead and
|
||||||
* cache operations; better heuristics consider wordsize and bitrate.
|
* cache operations; better heuristics consider wordsize and bitrate.
|
||||||
|
@ -246,11 +311,14 @@ struct atmel_spi {
|
||||||
|
|
||||||
bool use_dma;
|
bool use_dma;
|
||||||
bool use_pdc;
|
bool use_pdc;
|
||||||
|
bool use_cs_gpios;
|
||||||
/* dmaengine data */
|
/* dmaengine data */
|
||||||
struct atmel_spi_dma dma;
|
struct atmel_spi_dma dma;
|
||||||
|
|
||||||
bool keep_cs;
|
bool keep_cs;
|
||||||
bool cs_active;
|
bool cs_active;
|
||||||
|
|
||||||
|
u32 fifo_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Controller-specific per-slave state */
|
/* Controller-specific per-slave state */
|
||||||
|
@ -321,6 +389,7 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
|
||||||
}
|
}
|
||||||
|
|
||||||
mr = spi_readl(as, MR);
|
mr = spi_readl(as, MR);
|
||||||
|
if (as->use_cs_gpios)
|
||||||
gpio_set_value(asd->npcs_pin, active);
|
gpio_set_value(asd->npcs_pin, active);
|
||||||
} else {
|
} else {
|
||||||
u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0;
|
u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0;
|
||||||
|
@ -337,7 +406,7 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
|
||||||
|
|
||||||
mr = spi_readl(as, MR);
|
mr = spi_readl(as, MR);
|
||||||
mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr);
|
mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr);
|
||||||
if (spi->chip_select != 0)
|
if (as->use_cs_gpios && spi->chip_select != 0)
|
||||||
gpio_set_value(asd->npcs_pin, active);
|
gpio_set_value(asd->npcs_pin, active);
|
||||||
spi_writel(as, MR, mr);
|
spi_writel(as, MR, mr);
|
||||||
}
|
}
|
||||||
|
@ -366,7 +435,9 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi)
|
||||||
asd->npcs_pin, active ? " (low)" : "",
|
asd->npcs_pin, active ? " (low)" : "",
|
||||||
mr);
|
mr);
|
||||||
|
|
||||||
if (atmel_spi_is_v2(as) || spi->chip_select != 0)
|
if (!as->use_cs_gpios)
|
||||||
|
spi_writel(as, CR, SPI_BIT(LASTXFER));
|
||||||
|
else if (atmel_spi_is_v2(as) || spi->chip_select != 0)
|
||||||
gpio_set_value(asd->npcs_pin, !active);
|
gpio_set_value(asd->npcs_pin, !active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,6 +477,20 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as,
|
||||||
slave_config->dst_maxburst = 1;
|
slave_config->dst_maxburst = 1;
|
||||||
slave_config->device_fc = false;
|
slave_config->device_fc = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This driver uses fixed peripheral select mode (PS bit set to '0' in
|
||||||
|
* the Mode Register).
|
||||||
|
* So according to the datasheet, when FIFOs are available (and
|
||||||
|
* enabled), the Transmit FIFO operates in Multiple Data Mode.
|
||||||
|
* In this mode, up to 2 data, not 4, can be written into the Transmit
|
||||||
|
* Data Register in a single access.
|
||||||
|
* However, the first data has to be written into the lowest 16 bits and
|
||||||
|
* the second data into the highest 16 bits of the Transmit
|
||||||
|
* Data Register. For 8bit data (the most frequent case), it would
|
||||||
|
* require to rework tx_buf so each data would actualy fit 16 bits.
|
||||||
|
* So we'd rather write only one data at the time. Hence the transmit
|
||||||
|
* path works the same whether FIFOs are available (and enabled) or not.
|
||||||
|
*/
|
||||||
slave_config->direction = DMA_MEM_TO_DEV;
|
slave_config->direction = DMA_MEM_TO_DEV;
|
||||||
if (dmaengine_slave_config(as->dma.chan_tx, slave_config)) {
|
if (dmaengine_slave_config(as->dma.chan_tx, slave_config)) {
|
||||||
dev_err(&as->pdev->dev,
|
dev_err(&as->pdev->dev,
|
||||||
|
@ -413,6 +498,14 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as,
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This driver configures the spi controller for master mode (MSTR bit
|
||||||
|
* set to '1' in the Mode Register).
|
||||||
|
* So according to the datasheet, when FIFOs are available (and
|
||||||
|
* enabled), the Receive FIFO operates in Single Data Mode.
|
||||||
|
* So the receive path works the same whether FIFOs are available (and
|
||||||
|
* enabled) or not.
|
||||||
|
*/
|
||||||
slave_config->direction = DMA_DEV_TO_MEM;
|
slave_config->direction = DMA_DEV_TO_MEM;
|
||||||
if (dmaengine_slave_config(as->dma.chan_rx, slave_config)) {
|
if (dmaengine_slave_config(as->dma.chan_rx, slave_config)) {
|
||||||
dev_err(&as->pdev->dev,
|
dev_err(&as->pdev->dev,
|
||||||
|
@ -502,9 +595,9 @@ static void dma_callback(void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Next transfer using PIO.
|
* Next transfer using PIO without FIFO.
|
||||||
*/
|
*/
|
||||||
static void atmel_spi_next_xfer_pio(struct spi_master *master,
|
static void atmel_spi_next_xfer_single(struct spi_master *master,
|
||||||
struct spi_transfer *xfer)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
struct atmel_spi *as = spi_master_get_devdata(master);
|
struct atmel_spi *as = spi_master_get_devdata(master);
|
||||||
|
@ -537,6 +630,99 @@ static void atmel_spi_next_xfer_pio(struct spi_master *master,
|
||||||
spi_writel(as, IER, SPI_BIT(RDRF) | SPI_BIT(OVRES));
|
spi_writel(as, IER, SPI_BIT(RDRF) | SPI_BIT(OVRES));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Next transfer using PIO with FIFO.
|
||||||
|
*/
|
||||||
|
static void atmel_spi_next_xfer_fifo(struct spi_master *master,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
struct atmel_spi *as = spi_master_get_devdata(master);
|
||||||
|
u32 current_remaining_data, num_data;
|
||||||
|
u32 offset = xfer->len - as->current_remaining_bytes;
|
||||||
|
const u16 *words = (const u16 *)((u8 *)xfer->tx_buf + offset);
|
||||||
|
const u8 *bytes = (const u8 *)((u8 *)xfer->tx_buf + offset);
|
||||||
|
u16 td0, td1;
|
||||||
|
u32 fifomr;
|
||||||
|
|
||||||
|
dev_vdbg(master->dev.parent, "atmel_spi_next_xfer_fifo\n");
|
||||||
|
|
||||||
|
/* Compute the number of data to transfer in the current iteration */
|
||||||
|
current_remaining_data = ((xfer->bits_per_word > 8) ?
|
||||||
|
((u32)as->current_remaining_bytes >> 1) :
|
||||||
|
(u32)as->current_remaining_bytes);
|
||||||
|
num_data = min(current_remaining_data, as->fifo_size);
|
||||||
|
|
||||||
|
/* Flush RX and TX FIFOs */
|
||||||
|
spi_writel(as, CR, SPI_BIT(RXFCLR) | SPI_BIT(TXFCLR));
|
||||||
|
while (spi_readl(as, FLR))
|
||||||
|
cpu_relax();
|
||||||
|
|
||||||
|
/* Set RX FIFO Threshold to the number of data to transfer */
|
||||||
|
fifomr = spi_readl(as, FMR);
|
||||||
|
spi_writel(as, FMR, SPI_BFINS(RXFTHRES, num_data, fifomr));
|
||||||
|
|
||||||
|
/* Clear FIFO flags in the Status Register, especially RXFTHF */
|
||||||
|
(void)spi_readl(as, SR);
|
||||||
|
|
||||||
|
/* Fill TX FIFO */
|
||||||
|
while (num_data >= 2) {
|
||||||
|
if (xfer->tx_buf) {
|
||||||
|
if (xfer->bits_per_word > 8) {
|
||||||
|
td0 = *words++;
|
||||||
|
td1 = *words++;
|
||||||
|
} else {
|
||||||
|
td0 = *bytes++;
|
||||||
|
td1 = *bytes++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
td0 = 0;
|
||||||
|
td1 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_writel(as, TDR, (td1 << 16) | td0);
|
||||||
|
num_data -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_data) {
|
||||||
|
if (xfer->tx_buf) {
|
||||||
|
if (xfer->bits_per_word > 8)
|
||||||
|
td0 = *words++;
|
||||||
|
else
|
||||||
|
td0 = *bytes++;
|
||||||
|
} else {
|
||||||
|
td0 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_writew(as, TDR, td0);
|
||||||
|
num_data--;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(master->dev.parent,
|
||||||
|
" start fifo xfer %p: len %u tx %p rx %p bitpw %d\n",
|
||||||
|
xfer, xfer->len, xfer->tx_buf, xfer->rx_buf,
|
||||||
|
xfer->bits_per_word);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable RX FIFO Threshold Flag interrupt to be notified about
|
||||||
|
* transfer completion.
|
||||||
|
*/
|
||||||
|
spi_writel(as, IER, SPI_BIT(RXFTHF) | SPI_BIT(OVRES));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Next transfer using PIO.
|
||||||
|
*/
|
||||||
|
static void atmel_spi_next_xfer_pio(struct spi_master *master,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
struct atmel_spi *as = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
if (as->fifo_size)
|
||||||
|
atmel_spi_next_xfer_fifo(master, xfer);
|
||||||
|
else
|
||||||
|
atmel_spi_next_xfer_single(master, xfer);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Submit next transfer for DMA.
|
* Submit next transfer for DMA.
|
||||||
*/
|
*/
|
||||||
|
@ -839,13 +1025,8 @@ static void atmel_spi_disable_pdc_transfer(struct atmel_spi *as)
|
||||||
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
|
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called from IRQ
|
|
||||||
*
|
|
||||||
* Must update "current_remaining_bytes" to keep track of data
|
|
||||||
* to transfer.
|
|
||||||
*/
|
|
||||||
static void
|
static void
|
||||||
atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
|
atmel_spi_pump_single_data(struct atmel_spi *as, struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
u8 *rxp;
|
u8 *rxp;
|
||||||
u16 *rxp16;
|
u16 *rxp16;
|
||||||
|
@ -872,6 +1053,57 @@ atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
atmel_spi_pump_fifo_data(struct atmel_spi *as, struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
u32 fifolr = spi_readl(as, FLR);
|
||||||
|
u32 num_bytes, num_data = SPI_BFEXT(RXFL, fifolr);
|
||||||
|
u32 offset = xfer->len - as->current_remaining_bytes;
|
||||||
|
u16 *words = (u16 *)((u8 *)xfer->rx_buf + offset);
|
||||||
|
u8 *bytes = (u8 *)((u8 *)xfer->rx_buf + offset);
|
||||||
|
u16 rd; /* RD field is the lowest 16 bits of RDR */
|
||||||
|
|
||||||
|
/* Update the number of remaining bytes to transfer */
|
||||||
|
num_bytes = ((xfer->bits_per_word > 8) ?
|
||||||
|
(num_data << 1) :
|
||||||
|
num_data);
|
||||||
|
|
||||||
|
if (as->current_remaining_bytes > num_bytes)
|
||||||
|
as->current_remaining_bytes -= num_bytes;
|
||||||
|
else
|
||||||
|
as->current_remaining_bytes = 0;
|
||||||
|
|
||||||
|
/* Handle odd number of bytes when data are more than 8bit width */
|
||||||
|
if (xfer->bits_per_word > 8)
|
||||||
|
as->current_remaining_bytes &= ~0x1;
|
||||||
|
|
||||||
|
/* Read data */
|
||||||
|
while (num_data) {
|
||||||
|
rd = spi_readl(as, RDR);
|
||||||
|
if (xfer->rx_buf) {
|
||||||
|
if (xfer->bits_per_word > 8)
|
||||||
|
*words++ = rd;
|
||||||
|
else
|
||||||
|
*bytes++ = rd;
|
||||||
|
}
|
||||||
|
num_data--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called from IRQ
|
||||||
|
*
|
||||||
|
* Must update "current_remaining_bytes" to keep track of data
|
||||||
|
* to transfer.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
if (as->fifo_size)
|
||||||
|
atmel_spi_pump_fifo_data(as, xfer);
|
||||||
|
else
|
||||||
|
atmel_spi_pump_single_data(as, xfer);
|
||||||
|
}
|
||||||
|
|
||||||
/* Interrupt
|
/* Interrupt
|
||||||
*
|
*
|
||||||
* No need for locking in this Interrupt handler: done_status is the
|
* No need for locking in this Interrupt handler: done_status is the
|
||||||
|
@ -912,7 +1144,7 @@ atmel_spi_pio_interrupt(int irq, void *dev_id)
|
||||||
|
|
||||||
complete(&as->xfer_completion);
|
complete(&as->xfer_completion);
|
||||||
|
|
||||||
} else if (pending & SPI_BIT(RDRF)) {
|
} else if (pending & (SPI_BIT(RDRF) | SPI_BIT(RXFTHF))) {
|
||||||
atmel_spi_lock(as);
|
atmel_spi_lock(as);
|
||||||
|
|
||||||
if (as->current_remaining_bytes) {
|
if (as->current_remaining_bytes) {
|
||||||
|
@ -996,6 +1228,8 @@ static int atmel_spi_setup(struct spi_device *spi)
|
||||||
csr |= SPI_BIT(CPOL);
|
csr |= SPI_BIT(CPOL);
|
||||||
if (!(spi->mode & SPI_CPHA))
|
if (!(spi->mode & SPI_CPHA))
|
||||||
csr |= SPI_BIT(NCPHA);
|
csr |= SPI_BIT(NCPHA);
|
||||||
|
if (!as->use_cs_gpios)
|
||||||
|
csr |= SPI_BIT(CSAAT);
|
||||||
|
|
||||||
/* DLYBS is mostly irrelevant since we manage chipselect using GPIOs.
|
/* DLYBS is mostly irrelevant since we manage chipselect using GPIOs.
|
||||||
*
|
*
|
||||||
|
@ -1009,7 +1243,9 @@ static int atmel_spi_setup(struct spi_device *spi)
|
||||||
/* chipselect must have been muxed as GPIO (e.g. in board setup) */
|
/* chipselect must have been muxed as GPIO (e.g. in board setup) */
|
||||||
npcs_pin = (unsigned long)spi->controller_data;
|
npcs_pin = (unsigned long)spi->controller_data;
|
||||||
|
|
||||||
if (gpio_is_valid(spi->cs_gpio))
|
if (!as->use_cs_gpios)
|
||||||
|
npcs_pin = spi->chip_select;
|
||||||
|
else if (gpio_is_valid(spi->cs_gpio))
|
||||||
npcs_pin = spi->cs_gpio;
|
npcs_pin = spi->cs_gpio;
|
||||||
|
|
||||||
asd = spi->controller_state;
|
asd = spi->controller_state;
|
||||||
|
@ -1018,15 +1254,19 @@ static int atmel_spi_setup(struct spi_device *spi)
|
||||||
if (!asd)
|
if (!asd)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (as->use_cs_gpios) {
|
||||||
ret = gpio_request(npcs_pin, dev_name(&spi->dev));
|
ret = gpio_request(npcs_pin, dev_name(&spi->dev));
|
||||||
if (ret) {
|
if (ret) {
|
||||||
kfree(asd);
|
kfree(asd);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gpio_direction_output(npcs_pin,
|
||||||
|
!(spi->mode & SPI_CS_HIGH));
|
||||||
|
}
|
||||||
|
|
||||||
asd->npcs_pin = npcs_pin;
|
asd->npcs_pin = npcs_pin;
|
||||||
spi->controller_state = asd;
|
spi->controller_state = asd;
|
||||||
gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
asd->csr = csr;
|
asd->csr = csr;
|
||||||
|
@ -1338,6 +1578,13 @@ static int atmel_spi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
atmel_get_caps(as);
|
atmel_get_caps(as);
|
||||||
|
|
||||||
|
as->use_cs_gpios = true;
|
||||||
|
if (atmel_spi_is_v2(as) &&
|
||||||
|
!of_get_property(pdev->dev.of_node, "cs-gpios", NULL)) {
|
||||||
|
as->use_cs_gpios = false;
|
||||||
|
master->num_chipselect = 4;
|
||||||
|
}
|
||||||
|
|
||||||
as->use_dma = false;
|
as->use_dma = false;
|
||||||
as->use_pdc = false;
|
as->use_pdc = false;
|
||||||
if (as->caps.has_dma_support) {
|
if (as->caps.has_dma_support) {
|
||||||
|
@ -1380,6 +1627,13 @@ static int atmel_spi_probe(struct platform_device *pdev)
|
||||||
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
|
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
|
||||||
spi_writel(as, CR, SPI_BIT(SPIEN));
|
spi_writel(as, CR, SPI_BIT(SPIEN));
|
||||||
|
|
||||||
|
as->fifo_size = 0;
|
||||||
|
if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size",
|
||||||
|
&as->fifo_size)) {
|
||||||
|
dev_info(&pdev->dev, "Using FIFO (%u data)\n", as->fifo_size);
|
||||||
|
spi_writel(as, CR, SPI_BIT(FIFOEN));
|
||||||
|
}
|
||||||
|
|
||||||
/* go! */
|
/* go! */
|
||||||
dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n",
|
dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n",
|
||||||
(unsigned long)regs->start, irq);
|
(unsigned long)regs->start, irq);
|
||||||
|
|
|
@ -265,7 +265,7 @@ static inline int davinci_spi_get_prescale(struct davinci_spi *dspi,
|
||||||
|
|
||||||
ret = DIV_ROUND_UP(clk_get_rate(dspi->clk), max_speed_hz);
|
ret = DIV_ROUND_UP(clk_get_rate(dspi->clk), max_speed_hz);
|
||||||
|
|
||||||
if (ret < 3 || ret > 256)
|
if (ret < 1 || ret > 256)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return ret - 1;
|
return ret - 1;
|
||||||
|
|
Loading…
Reference in New Issue