diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 1 | ||||
-rw-r--r-- | drivers/spi/spi-altera.c | 163 | ||||
-rw-r--r-- | drivers/spi/spi-ath79.c | 13 | ||||
-rw-r--r-- | drivers/spi/spi-bcm-qspi.c | 89 | ||||
-rw-r--r-- | drivers/spi/spi-bcm63xx-hsspi.c | 6 | ||||
-rw-r--r-- | drivers/spi/spi.c | 83 |
6 files changed, 160 insertions, 195 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 9b31351fe429..acf566f06241 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -55,7 +55,6 @@ comment "SPI Master Controller Drivers" config SPI_ALTERA tristate "Altera SPI Controller" - select SPI_BITBANG help This is the driver for the Altera SPI Controller. diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index b95010e72452..a5adf0d868fc 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -18,7 +18,6 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> -#include <linux/spi/spi_bitbang.h> #include <linux/io.h> #include <linux/of.h> @@ -45,10 +44,6 @@ #define ALTERA_SPI_CONTROL_SSO_MSK 0x400 struct altera_spi { - /* bitbang has to be first */ - struct spi_bitbang bitbang; - struct completion done; - void __iomem *base; int irq; int len; @@ -66,59 +61,64 @@ static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev) return spi_master_get_devdata(sdev->master); } -static void altera_spi_chipsel(struct spi_device *spi, int value) +static void altera_spi_set_cs(struct spi_device *spi, bool is_high) { struct altera_spi *hw = altera_spi_to_hw(spi); - if (spi->mode & SPI_CS_HIGH) { - switch (value) { - case BITBANG_CS_INACTIVE: - writel(1 << spi->chip_select, - hw->base + ALTERA_SPI_SLAVE_SEL); - hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK; - writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); - break; - - case BITBANG_CS_ACTIVE: - hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK; - writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); - writel(0, hw->base + ALTERA_SPI_SLAVE_SEL); - break; - } + if (is_high) { + hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK; + writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); + writel(0, hw->base + ALTERA_SPI_SLAVE_SEL); } else { - switch (value) { - case BITBANG_CS_INACTIVE: - hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK; - writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); - break; + writel(BIT(spi->chip_select), hw->base + ALTERA_SPI_SLAVE_SEL); + hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK; + writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); + } +} + +static void altera_spi_tx_word(struct altera_spi *hw) +{ + unsigned int txd = 0; - case BITBANG_CS_ACTIVE: - writel(1 << spi->chip_select, - hw->base + ALTERA_SPI_SLAVE_SEL); - hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK; - writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); + if (hw->tx) { + switch (hw->bytes_per_word) { + case 1: + txd = hw->tx[hw->count]; + break; + case 2: + txd = (hw->tx[hw->count * 2] + | (hw->tx[hw->count * 2 + 1] << 8)); break; } } + + writel(txd, hw->base + ALTERA_SPI_TXDATA); } -static inline unsigned int hw_txbyte(struct altera_spi *hw, int count) +static void altera_spi_rx_word(struct altera_spi *hw) { - if (hw->tx) { + unsigned int rxd; + + rxd = readl(hw->base + ALTERA_SPI_RXDATA); + if (hw->rx) { switch (hw->bytes_per_word) { case 1: - return hw->tx[count]; + hw->rx[hw->count] = rxd; + break; case 2: - return (hw->tx[count * 2] - | (hw->tx[count * 2 + 1] << 8)); + hw->rx[hw->count * 2] = rxd; + hw->rx[hw->count * 2 + 1] = rxd >> 8; + break; } } - return 0; + + hw->count++; } -static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t) +static int altera_spi_txrx(struct spi_master *master, + struct spi_device *spi, struct spi_transfer *t) { - struct altera_spi *hw = altera_spi_to_hw(spi); + struct altera_spi *hw = spi_master_get_devdata(master); hw->tx = t->tx_buf; hw->rx = t->rx_buf; @@ -132,67 +132,39 @@ static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t) writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); /* send the first byte */ - writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA); - - wait_for_completion(&hw->done); - /* disable receive interrupt */ - hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK; - writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); + altera_spi_tx_word(hw); } else { while (hw->count < hw->len) { - unsigned int rxd; - - writel(hw_txbyte(hw, hw->count), - hw->base + ALTERA_SPI_TXDATA); + altera_spi_tx_word(hw); while (!(readl(hw->base + ALTERA_SPI_STATUS) & ALTERA_SPI_STATUS_RRDY_MSK)) cpu_relax(); - rxd = readl(hw->base + ALTERA_SPI_RXDATA); - if (hw->rx) { - switch (hw->bytes_per_word) { - case 1: - hw->rx[hw->count] = rxd; - break; - case 2: - hw->rx[hw->count * 2] = rxd; - hw->rx[hw->count * 2 + 1] = rxd >> 8; - break; - } - } - - hw->count++; + altera_spi_rx_word(hw); } + spi_finalize_current_transfer(master); } - return hw->count * hw->bytes_per_word; + return t->len; } static irqreturn_t altera_spi_irq(int irq, void *dev) { - struct altera_spi *hw = dev; - unsigned int rxd; + struct spi_master *master = dev; + struct altera_spi *hw = spi_master_get_devdata(master); - rxd = readl(hw->base + ALTERA_SPI_RXDATA); - if (hw->rx) { - switch (hw->bytes_per_word) { - case 1: - hw->rx[hw->count] = rxd; - break; - case 2: - hw->rx[hw->count * 2] = rxd; - hw->rx[hw->count * 2 + 1] = rxd >> 8; - break; - } - } + altera_spi_rx_word(hw); - hw->count++; + if (hw->count < hw->len) { + altera_spi_tx_word(hw); + } else { + /* disable receive interrupt */ + hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK; + writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); - if (hw->count < hw->len) - writel(hw_txbyte(hw, hw->count), hw->base + ALTERA_SPI_TXDATA); - else - complete(&hw->done); + spi_finalize_current_transfer(master); + } return IRQ_HANDLED; } @@ -214,14 +186,10 @@ static int altera_spi_probe(struct platform_device *pdev) master->mode_bits = SPI_CS_HIGH; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16); master->dev.of_node = pdev->dev.of_node; + master->transfer_one = altera_spi_txrx; + master->set_cs = altera_spi_set_cs; hw = spi_master_get_devdata(master); - platform_set_drvdata(pdev, hw); - - /* setup the state for the bitbang driver */ - hw->bitbang.master = master; - hw->bitbang.chipselect = altera_spi_chipsel; - hw->bitbang.txrx_bufs = altera_spi_txrx; /* find and map our resources */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -239,15 +207,13 @@ static int altera_spi_probe(struct platform_device *pdev) /* irq is optional */ hw->irq = platform_get_irq(pdev, 0); if (hw->irq >= 0) { - init_completion(&hw->done); err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0, - pdev->name, hw); + pdev->name, master); if (err) goto exit; } - /* register our spi controller */ - err = spi_bitbang_start(&hw->bitbang); + err = devm_spi_register_master(&pdev->dev, master); if (err) goto exit; dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq); @@ -258,16 +224,6 @@ exit: return err; } -static int altera_spi_remove(struct platform_device *dev) -{ - struct altera_spi *hw = platform_get_drvdata(dev); - struct spi_master *master = hw->bitbang.master; - - spi_bitbang_stop(&hw->bitbang); - spi_master_put(master); - return 0; -} - #ifdef CONFIG_OF static const struct of_device_id altera_spi_match[] = { { .compatible = "ALTR,spi-1.0", }, @@ -279,7 +235,6 @@ MODULE_DEVICE_TABLE(of, altera_spi_match); static struct platform_driver altera_spi_driver = { .probe = altera_spi_probe, - .remove = altera_spi_remove, .driver = { .name = DRV_NAME, .pm = NULL, diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index b89cee11f418..0719bd484891 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -39,15 +39,15 @@ struct ath79_spi { u32 reg_ctrl; void __iomem *base; struct clk *clk; - unsigned rrw_delay; + unsigned int rrw_delay; }; -static inline u32 ath79_spi_rr(struct ath79_spi *sp, unsigned reg) +static inline u32 ath79_spi_rr(struct ath79_spi *sp, unsigned int reg) { return ioread32(sp->base + reg); } -static inline void ath79_spi_wr(struct ath79_spi *sp, unsigned reg, u32 val) +static inline void ath79_spi_wr(struct ath79_spi *sp, unsigned int reg, u32 val) { iowrite32(val, sp->base + reg); } @@ -57,7 +57,7 @@ static inline struct ath79_spi *ath79_spidev_to_sp(struct spi_device *spi) return spi_master_get_devdata(spi->master); } -static inline void ath79_spi_delay(struct ath79_spi *sp, unsigned nsecs) +static inline void ath79_spi_delay(struct ath79_spi *sp, unsigned int nsecs) { if (nsecs > sp->rrw_delay) ndelay(nsecs - sp->rrw_delay); @@ -148,9 +148,8 @@ static int ath79_spi_setup_cs(struct spi_device *spi) static void ath79_spi_cleanup_cs(struct spi_device *spi) { - if (gpio_is_valid(spi->cs_gpio)) { + if (gpio_is_valid(spi->cs_gpio)) gpio_free(spi->cs_gpio); - } } static int ath79_spi_setup(struct spi_device *spi) @@ -176,7 +175,7 @@ static void ath79_spi_cleanup(struct spi_device *spi) spi_bitbang_cleanup(spi); } -static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned nsecs, +static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned int nsecs, u32 word, u8 bits) { struct ath79_spi *sp = ath79_spidev_to_sp(spi); diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index b19722ba908c..6ef6c44f39f5 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -25,7 +25,6 @@ #include <linux/ioport.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mtd/spi-nor.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/platform_device.h> @@ -349,76 +348,60 @@ static void bcm_qspi_bspi_set_xfer_params(struct bcm_qspi *qspi, u8 cmd_byte, bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE, flex_mode); } -static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int width, - int addrlen, int hp) +static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, + struct spi_flash_read_message *msg, + int hp) { int bpc = 0, bpp = 0; - u8 command = SPINOR_OP_READ_FAST; - int flex_mode = 1, rv = 0; - bool spans_4byte = false; + u8 command = msg->read_opcode; + int width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE; + int addrlen = msg->addr_width; + int addr_nbits = msg->addr_nbits ? msg->addr_nbits : SPI_NBITS_SINGLE; + int flex_mode = 1; dev_dbg(&qspi->pdev->dev, "set flex mode w %x addrlen %x hp %d\n", width, addrlen, hp); - if (addrlen == BSPI_ADDRLEN_4BYTES) { + if (addrlen == BSPI_ADDRLEN_4BYTES) bpp = BSPI_BPP_ADDR_SELECT_MASK; - spans_4byte = true; - } - bpp |= 8; + bpp |= msg->dummy_bytes * (8/addr_nbits); switch (width) { case SPI_NBITS_SINGLE: if (addrlen == BSPI_ADDRLEN_3BYTES) /* default mode, does not need flex_cmd */ flex_mode = 0; - else - command = SPINOR_OP_READ_FAST_4B; break; case SPI_NBITS_DUAL: bpc = 0x00000001; if (hp) { bpc |= 0x00010100; /* address and mode are 2-bit */ bpp = BSPI_BPP_MODE_SELECT_MASK; - command = OPCODE_DIOR; - if (spans_4byte) - command = OPCODE_DIOR_4B; - } else { - command = SPINOR_OP_READ_1_1_2; - if (spans_4byte) - command = SPINOR_OP_READ_1_1_2_4B; } break; case SPI_NBITS_QUAD: bpc = 0x00000002; if (hp) { bpc |= 0x00020200; /* address and mode are 4-bit */ - bpp = 4; /* dummy cycles */ - bpp |= BSPI_BPP_ADDR_SELECT_MASK; - command = OPCODE_QIOR; - if (spans_4byte) - command = OPCODE_QIOR_4B; - } else { - command = SPINOR_OP_READ_1_1_4; - if (spans_4byte) - command = SPINOR_OP_READ_1_1_4_4B; + bpp |= BSPI_BPP_MODE_SELECT_MASK; } break; default: - rv = -EINVAL; - break; + return -EINVAL; } - if (rv == 0) - bcm_qspi_bspi_set_xfer_params(qspi, command, bpp, bpc, - flex_mode); + bcm_qspi_bspi_set_xfer_params(qspi, command, bpp, bpc, flex_mode); - return rv; + return 0; } -static int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi, int width, - int addrlen, int hp) +static int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi, + struct spi_flash_read_message *msg, + int hp) { + int width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE; + int addrlen = msg->addr_width; u32 data = bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL); dev_dbg(&qspi->pdev->dev, "set override mode w %x addrlen %x hp %d\n", @@ -430,7 +413,6 @@ static int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi, int width, data &= ~(BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD | BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL); break; - case SPI_NBITS_QUAD: /* clear dual mode and set quad mode */ data &= ~BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL; @@ -455,15 +437,17 @@ static int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi, int width, /* set the override mode */ data |= BSPI_STRAP_OVERRIDE_CTRL_OVERRIDE; bcm_qspi_write(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL, data); - bcm_qspi_bspi_set_xfer_params(qspi, SPINOR_OP_READ_FAST, 0, 0, 0); + bcm_qspi_bspi_set_xfer_params(qspi, msg->read_opcode, 0, 0, 0); return 0; } static int bcm_qspi_bspi_set_mode(struct bcm_qspi *qspi, - int width, int addrlen, int hp) + struct spi_flash_read_message *msg, int hp) { int error = 0; + int width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE; + int addrlen = msg->addr_width; /* default mode */ qspi->xfer_mode.flex_mode = true; @@ -475,23 +459,13 @@ static int bcm_qspi_bspi_set_mode(struct bcm_qspi *qspi, mask = BSPI_STRAP_OVERRIDE_CTRL_OVERRIDE; if (val & mask || qspi->s3_strap_override_ctrl & mask) { qspi->xfer_mode.flex_mode = false; - bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE, - 0); - - if ((val | qspi->s3_strap_override_ctrl) & - BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL) - width = SPI_NBITS_DUAL; - else if ((val | qspi->s3_strap_override_ctrl) & - BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD) - width = SPI_NBITS_QUAD; - - error = bcm_qspi_bspi_set_override(qspi, width, addrlen, - hp); + bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE, 0); + error = bcm_qspi_bspi_set_override(qspi, msg, hp); } } if (qspi->xfer_mode.flex_mode) - error = bcm_qspi_bspi_set_flex_mode(qspi, width, addrlen, hp); + error = bcm_qspi_bspi_set_flex_mode(qspi, msg, hp); if (error) { dev_warn(&qspi->pdev->dev, @@ -981,7 +955,7 @@ static int bcm_qspi_flash_read(struct spi_device *spi, struct bcm_qspi *qspi = spi_master_get_devdata(spi->master); int ret = 0; bool mspi_read = false; - u32 io_width, addrlen, addr, len; + u32 addr, len; u_char *buf; buf = msg->buf; @@ -1010,9 +984,7 @@ static int bcm_qspi_flash_read(struct spi_device *spi, if (mspi_read) return bcm_qspi_mspi_flash_read(spi, msg); - io_width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE; - addrlen = msg->addr_width; - ret = bcm_qspi_bspi_set_mode(qspi, io_width, addrlen, -1); + ret = bcm_qspi_bspi_set_mode(qspi, msg, -1); if (!ret) ret = bcm_qspi_bspi_flash_read(spi, msg); @@ -1422,6 +1394,11 @@ static int __maybe_unused bcm_qspi_suspend(struct device *dev) { struct bcm_qspi *qspi = dev_get_drvdata(dev); + /* store the override strap value */ + if (!bcm_qspi_bspi_ver_three(qspi)) + qspi->s3_strap_override_ctrl = + bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL); + spi_master_suspend(qspi->master); clk_disable(qspi->clk); bcm_qspi_hw_uninit(qspi); diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c index b03fc1850ae9..cbcba614b253 100644 --- a/drivers/spi/spi-bcm63xx-hsspi.c +++ b/drivers/spi/spi-bcm63xx-hsspi.c @@ -108,7 +108,7 @@ struct bcm63xx_hsspi { u8 cs_polarity; }; -static void bcm63xx_hsspi_set_cs(struct bcm63xx_hsspi *bs, unsigned cs, +static void bcm63xx_hsspi_set_cs(struct bcm63xx_hsspi *bs, unsigned int cs, bool active) { u32 reg; @@ -127,7 +127,7 @@ static void bcm63xx_hsspi_set_cs(struct bcm63xx_hsspi *bs, unsigned cs, static void bcm63xx_hsspi_set_clk(struct bcm63xx_hsspi *bs, struct spi_device *spi, int hz) { - unsigned profile = spi->chip_select; + unsigned int profile = spi->chip_select; u32 reg; reg = DIV_ROUND_UP(2048, DIV_ROUND_UP(bs->speed_hz, hz)); @@ -154,7 +154,7 @@ static void bcm63xx_hsspi_set_clk(struct bcm63xx_hsspi *bs, static int bcm63xx_hsspi_do_txrx(struct spi_device *spi, struct spi_transfer *t) { struct bcm63xx_hsspi *bs = spi_master_get_devdata(spi->master); - unsigned chip_select = spi->chip_select; + unsigned int chip_select = spi->chip_select; u16 opcode = 0; int pending = t->len; int step_size = HSSPI_BUFFER_LEN; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 4fcbb0aa71d3..f47d1ccdf292 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -40,9 +40,13 @@ #include <linux/ioport.h> #include <linux/acpi.h> #include <linux/highmem.h> +#include <linux/idr.h> #define CREATE_TRACE_POINTS #include <trace/events/spi.h> +#define SPI_DYN_FIRST_BUS_NUM 0 + +static DEFINE_IDR(spi_master_idr); static void spidev_release(struct device *dev) { @@ -321,8 +325,7 @@ static int spi_uevent(struct device *dev, struct kobj_uevent_env *env) if (rc != -ENODEV) return rc; - add_uevent_var(env, "MODALIAS=%s%s", SPI_MODULE_PREFIX, spi->modalias); - return 0; + return add_uevent_var(env, "MODALIAS=%s%s", SPI_MODULE_PREFIX, spi->modalias); } struct bus_type spi_bus_type = { @@ -421,6 +424,7 @@ static LIST_HEAD(spi_controller_list); /* * Used to protect add/del opertion for board_info list and * spi_controller list, and their matching process + * also used to protect object of type struct idr */ static DEFINE_MUTEX(board_lock); @@ -1533,15 +1537,15 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, int rc; /* Mode (clock phase/polarity/etc.) */ - if (of_find_property(nc, "spi-cpha", NULL)) + if (of_property_read_bool(nc, "spi-cpha")) spi->mode |= SPI_CPHA; - if (of_find_property(nc, "spi-cpol", NULL)) + if (of_property_read_bool(nc, "spi-cpol")) spi->mode |= SPI_CPOL; - if (of_find_property(nc, "spi-cs-high", NULL)) + if (of_property_read_bool(nc, "spi-cs-high")) spi->mode |= SPI_CS_HIGH; - if (of_find_property(nc, "spi-3wire", NULL)) + if (of_property_read_bool(nc, "spi-3wire")) spi->mode |= SPI_3WIRE; - if (of_find_property(nc, "spi-lsb-first", NULL)) + if (of_property_read_bool(nc, "spi-lsb-first")) spi->mode |= SPI_LSB_FIRST; /* Device DUAL/QUAD mode */ @@ -2052,11 +2056,10 @@ static int of_spi_register_master(struct spi_controller *ctlr) */ int spi_register_controller(struct spi_controller *ctlr) { - static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1); struct device *dev = ctlr->dev.parent; struct boardinfo *bi; int status = -ENODEV; - int dynamic = 0; + int id; if (!dev) return -ENODEV; @@ -2072,19 +2075,28 @@ int spi_register_controller(struct spi_controller *ctlr) */ if (ctlr->num_chipselect == 0) return -EINVAL; - - if ((ctlr->bus_num < 0) && ctlr->dev.of_node) - ctlr->bus_num = of_alias_get_id(ctlr->dev.of_node, "spi"); - - /* convention: dynamically assigned bus IDs count down from the max */ + /* allocate dynamic bus number using Linux idr */ + if ((ctlr->bus_num < 0) && ctlr->dev.of_node) { + id = of_alias_get_id(ctlr->dev.of_node, "spi"); + if (id >= 0) { + ctlr->bus_num = id; + mutex_lock(&board_lock); + id = idr_alloc(&spi_master_idr, ctlr, ctlr->bus_num, + ctlr->bus_num + 1, GFP_KERNEL); + mutex_unlock(&board_lock); + if (WARN(id < 0, "couldn't get idr")) + return id == -ENOSPC ? -EBUSY : id; + } + } if (ctlr->bus_num < 0) { - /* FIXME switch to an IDR based scheme, something like - * I2C now uses, so we can't run out of "dynamic" IDs - */ - ctlr->bus_num = atomic_dec_return(&dyn_bus_id); - dynamic = 1; + mutex_lock(&board_lock); + id = idr_alloc(&spi_master_idr, ctlr, SPI_DYN_FIRST_BUS_NUM, 0, + GFP_KERNEL); + mutex_unlock(&board_lock); + if (WARN(id < 0, "couldn't get idr")) + return id; + ctlr->bus_num = id; } - INIT_LIST_HEAD(&ctlr->queue); spin_lock_init(&ctlr->queue_lock); spin_lock_init(&ctlr->bus_lock_spinlock); @@ -2100,11 +2112,16 @@ int spi_register_controller(struct spi_controller *ctlr) */ dev_set_name(&ctlr->dev, "spi%u", ctlr->bus_num); status = device_add(&ctlr->dev); - if (status < 0) + if (status < 0) { + /* free bus id */ + mutex_lock(&board_lock); + idr_remove(&spi_master_idr, ctlr->bus_num); + mutex_unlock(&board_lock); goto done; - dev_dbg(dev, "registered %s %s%s\n", + } + dev_dbg(dev, "registered %s %s\n", spi_controller_is_slave(ctlr) ? "slave" : "master", - dev_name(&ctlr->dev), dynamic ? " (dynamic)" : ""); + dev_name(&ctlr->dev)); /* If we're using a queued driver, start the queue */ if (ctlr->transfer) @@ -2113,6 +2130,10 @@ int spi_register_controller(struct spi_controller *ctlr) status = spi_controller_initialize_queue(ctlr); if (status) { device_del(&ctlr->dev); + /* free bus id */ + mutex_lock(&board_lock); + idr_remove(&spi_master_idr, ctlr->bus_num); + mutex_unlock(&board_lock); goto done; } } @@ -2191,19 +2212,33 @@ static int __unregister(struct device *dev, void *null) */ void spi_unregister_controller(struct spi_controller *ctlr) { + struct spi_controller *found; int dummy; + /* First make sure that this controller was ever added */ + mutex_lock(&board_lock); + found = idr_find(&spi_master_idr, ctlr->bus_num); + mutex_unlock(&board_lock); + if (found != ctlr) { + dev_dbg(&ctlr->dev, + "attempting to delete unregistered controller [%s]\n", + dev_name(&ctlr->dev)); + return; + } if (ctlr->queued) { if (spi_destroy_queue(ctlr)) dev_err(&ctlr->dev, "queue remove failed\n"); } - mutex_lock(&board_lock); list_del(&ctlr->list); mutex_unlock(&board_lock); dummy = device_for_each_child(&ctlr->dev, NULL, __unregister); device_unregister(&ctlr->dev); + /* free bus id */ + mutex_lock(&board_lock); + idr_remove(&spi_master_idr, ctlr->bus_num); + mutex_unlock(&board_lock); } EXPORT_SYMBOL_GPL(spi_unregister_controller); |