spi: spi-bcm2835: Fix deadlock
[ Upstream commit c45c1e82bba130db4f19d9dbc1deefcf4ea994ed ]
The bcm2835_spi_transfer_one function can create a deadlock
if it is called while another thread already has the
CCF lock.
Signed-off-by: Alexandru Tachici <alexandru.tachici@analog.com>
Fixes: f8043872e7
("spi: add driver for BCM2835")
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Link: https://lore.kernel.org/r/20210716210245.13240-2-alexandru.tachici@analog.com
Signed-off-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
432738c974
commit
ee36bb4713
@@ -84,6 +84,7 @@ MODULE_PARM_DESC(polling_limit_us,
|
|||||||
* struct bcm2835_spi - BCM2835 SPI controller
|
* struct bcm2835_spi - BCM2835 SPI controller
|
||||||
* @regs: base address of register map
|
* @regs: base address of register map
|
||||||
* @clk: core clock, divided to calculate serial clock
|
* @clk: core clock, divided to calculate serial clock
|
||||||
|
* @clk_hz: core clock cached speed
|
||||||
* @irq: interrupt, signals TX FIFO empty or RX FIFO ¾ full
|
* @irq: interrupt, signals TX FIFO empty or RX FIFO ¾ full
|
||||||
* @tfr: SPI transfer currently processed
|
* @tfr: SPI transfer currently processed
|
||||||
* @ctlr: SPI controller reverse lookup
|
* @ctlr: SPI controller reverse lookup
|
||||||
@@ -124,6 +125,7 @@ MODULE_PARM_DESC(polling_limit_us,
|
|||||||
struct bcm2835_spi {
|
struct bcm2835_spi {
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
unsigned long clk_hz;
|
||||||
int irq;
|
int irq;
|
||||||
struct spi_transfer *tfr;
|
struct spi_transfer *tfr;
|
||||||
struct spi_controller *ctlr;
|
struct spi_controller *ctlr;
|
||||||
@@ -1082,19 +1084,18 @@ static int bcm2835_spi_transfer_one(struct spi_controller *ctlr,
|
|||||||
struct spi_transfer *tfr)
|
struct spi_transfer *tfr)
|
||||||
{
|
{
|
||||||
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
|
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
|
||||||
unsigned long spi_hz, clk_hz, cdiv;
|
unsigned long spi_hz, cdiv;
|
||||||
unsigned long hz_per_byte, byte_limit;
|
unsigned long hz_per_byte, byte_limit;
|
||||||
u32 cs = bs->prepare_cs[spi->chip_select];
|
u32 cs = bs->prepare_cs[spi->chip_select];
|
||||||
|
|
||||||
/* set clock */
|
/* set clock */
|
||||||
spi_hz = tfr->speed_hz;
|
spi_hz = tfr->speed_hz;
|
||||||
clk_hz = clk_get_rate(bs->clk);
|
|
||||||
|
|
||||||
if (spi_hz >= clk_hz / 2) {
|
if (spi_hz >= bs->clk_hz / 2) {
|
||||||
cdiv = 2; /* clk_hz/2 is the fastest we can go */
|
cdiv = 2; /* clk_hz/2 is the fastest we can go */
|
||||||
} else if (spi_hz) {
|
} else if (spi_hz) {
|
||||||
/* CDIV must be a multiple of two */
|
/* CDIV must be a multiple of two */
|
||||||
cdiv = DIV_ROUND_UP(clk_hz, spi_hz);
|
cdiv = DIV_ROUND_UP(bs->clk_hz, spi_hz);
|
||||||
cdiv += (cdiv % 2);
|
cdiv += (cdiv % 2);
|
||||||
|
|
||||||
if (cdiv >= 65536)
|
if (cdiv >= 65536)
|
||||||
@@ -1102,7 +1103,7 @@ static int bcm2835_spi_transfer_one(struct spi_controller *ctlr,
|
|||||||
} else {
|
} else {
|
||||||
cdiv = 0; /* 0 is the slowest we can go */
|
cdiv = 0; /* 0 is the slowest we can go */
|
||||||
}
|
}
|
||||||
tfr->effective_speed_hz = cdiv ? (clk_hz / cdiv) : (clk_hz / 65536);
|
tfr->effective_speed_hz = cdiv ? (bs->clk_hz / cdiv) : (bs->clk_hz / 65536);
|
||||||
bcm2835_wr(bs, BCM2835_SPI_CLK, cdiv);
|
bcm2835_wr(bs, BCM2835_SPI_CLK, cdiv);
|
||||||
|
|
||||||
/* handle all the 3-wire mode */
|
/* handle all the 3-wire mode */
|
||||||
@@ -1318,6 +1319,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
|
|||||||
return bs->irq ? bs->irq : -ENODEV;
|
return bs->irq ? bs->irq : -ENODEV;
|
||||||
|
|
||||||
clk_prepare_enable(bs->clk);
|
clk_prepare_enable(bs->clk);
|
||||||
|
bs->clk_hz = clk_get_rate(bs->clk);
|
||||||
|
|
||||||
err = bcm2835_dma_init(ctlr, &pdev->dev, bs);
|
err = bcm2835_dma_init(ctlr, &pdev->dev, bs);
|
||||||
if (err)
|
if (err)
|
||||||
|
Reference in New Issue
Block a user