X-Git-Url: http://git.monstr.eu/?a=blobdiff_plain;f=drivers%2Fspi%2Fspi-fsl-lpspi.c;h=33b6a8affd55f682051e74dbde56cd8fa50c0e75;hb=e1a7d1673062a6a9160b1fc94e1540b747398e01;hp=3918639140436e16707f854d8a5a5b9725515ce7;hpb=6cdfa54cd22984ae785b0d496b53405d6da9ad1d;p=linux-2.6-microblaze.git diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 391863914043..33b6a8affd55 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -8,7 +8,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -16,7 +19,12 @@ #include #include #include +#include +#include #include +#include +#include +#include #include #include #include @@ -24,6 +32,11 @@ #define DRIVER_NAME "fsl_lpspi" +#define FSL_LPSPI_RPM_TIMEOUT 50 /* 50ms */ + +/* The maximum bytes that edma can transfer once.*/ +#define FSL_LPSPI_MAX_EDMA_BYTES ((1 << 15) - 1) + /* i.MX7ULP LPSPI registers */ #define IMX7ULP_VERID 0x0 #define IMX7ULP_PARAM 0x4 @@ -57,12 +70,14 @@ #define IER_FCIE BIT(9) #define IER_RDIE BIT(1) #define IER_TDIE BIT(0) +#define DER_RDDE BIT(1) +#define DER_TDDE BIT(0) #define CFGR1_PCSCFG BIT(27) #define CFGR1_PINCFG (BIT(24)|BIT(25)) #define CFGR1_PCSPOL BIT(8) #define CFGR1_NOSTALL BIT(3) #define CFGR1_MASTER BIT(0) -#define FSR_RXCOUNT (BIT(16)|BIT(17)|BIT(18)) +#define FSR_TXCOUNT (0xFF) #define RSR_RXEMPTY BIT(1) #define TCR_CPOL BIT(31) #define TCR_CPHA BIT(30) @@ -84,8 +99,11 @@ struct lpspi_config { struct fsl_lpspi_data { struct device *dev; void __iomem *base; - struct clk *clk; + unsigned long base_phys; + struct clk *clk_ipg; + struct clk *clk_per; bool is_slave; + bool is_first_byte; void *rx_buf; const void *tx_buf; @@ -101,6 +119,13 @@ struct fsl_lpspi_data { struct completion xfer_done; bool slave_aborted; + + /* DMA */ + bool usedma; + struct completion dma_rx_completion; + struct completion dma_tx_completion; + + int chipselect[0]; }; static const struct of_device_id fsl_lpspi_dt_ids[] = { @@ -147,12 +172,48 @@ static void fsl_lpspi_intctrl(struct fsl_lpspi_data *fsl_lpspi, writel(enable, fsl_lpspi->base + IMX7ULP_IER); } +static int fsl_lpspi_bytes_per_word(const int bpw) +{ + return DIV_ROUND_UP(bpw, BITS_PER_BYTE); +} + +static bool fsl_lpspi_can_dma(struct spi_controller *controller, + struct spi_device *spi, + struct spi_transfer *transfer) +{ + unsigned int bytes_per_word; + + if (!controller->dma_rx) + return false; + + bytes_per_word = fsl_lpspi_bytes_per_word(transfer->bits_per_word); + + switch (bytes_per_word) + { + case 1: + case 2: + case 4: + break; + default: + return false; + } + + return true; +} + static int lpspi_prepare_xfer_hardware(struct spi_controller *controller) { struct fsl_lpspi_data *fsl_lpspi = spi_controller_get_devdata(controller); + int ret; - return clk_prepare_enable(fsl_lpspi->clk); + ret = pm_runtime_get_sync(fsl_lpspi->dev); + if (ret < 0) { + dev_err(fsl_lpspi->dev, "failed to enable clock\n"); + return ret; + } + + return 0; } static int lpspi_unprepare_xfer_hardware(struct spi_controller *controller) @@ -160,7 +221,22 @@ static int lpspi_unprepare_xfer_hardware(struct spi_controller *controller) struct fsl_lpspi_data *fsl_lpspi = spi_controller_get_devdata(controller); - clk_disable_unprepare(fsl_lpspi->clk); + pm_runtime_mark_last_busy(fsl_lpspi->dev); + pm_runtime_put_autosuspend(fsl_lpspi->dev); + + return 0; +} + +static int fsl_lpspi_prepare_message(struct spi_controller *controller, + struct spi_message *msg) +{ + struct fsl_lpspi_data *fsl_lpspi = + spi_controller_get_devdata(controller); + struct spi_device *spi = msg->spi; + int gpio = fsl_lpspi->chipselect[spi->chip_select]; + + if (gpio_is_valid(gpio)) + gpio_direction_output(gpio, spi->mode & SPI_CS_HIGH ? 0 : 1); return 0; } @@ -197,8 +273,7 @@ static void fsl_lpspi_read_rx_fifo(struct fsl_lpspi_data *fsl_lpspi) fsl_lpspi->rx(fsl_lpspi); } -static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi, - bool is_first_xfer) +static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi) { u32 temp = 0; @@ -213,11 +288,13 @@ static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi, * For the first transfer, clear TCR_CONTC to assert SS. * For subsequent transfer, set TCR_CONTC to keep SS asserted. */ - temp |= TCR_CONT; - if (is_first_xfer) - temp &= ~TCR_CONTC; - else - temp |= TCR_CONTC; + if (!fsl_lpspi->usedma) { + temp |= TCR_CONT; + if (fsl_lpspi->is_first_byte) + temp &= ~TCR_CONTC; + else + temp |= TCR_CONTC; + } } writel(temp, fsl_lpspi->base + IMX7ULP_TCR); @@ -228,7 +305,11 @@ static void fsl_lpspi_set_watermark(struct fsl_lpspi_data *fsl_lpspi) { u32 temp; - temp = fsl_lpspi->watermark >> 1 | (fsl_lpspi->watermark >> 1) << 16; + if (!fsl_lpspi->usedma) + temp = fsl_lpspi->watermark >> 1 | + (fsl_lpspi->watermark >> 1) << 16; + else + temp = fsl_lpspi->watermark >> 1; writel(temp, fsl_lpspi->base + IMX7ULP_FCR); @@ -241,7 +322,14 @@ static int fsl_lpspi_set_bitrate(struct fsl_lpspi_data *fsl_lpspi) unsigned int perclk_rate, scldiv; u8 prescale; - perclk_rate = clk_get_rate(fsl_lpspi->clk); + perclk_rate = clk_get_rate(fsl_lpspi->clk_per); + + if (config.speed_hz > perclk_rate / 2) { + dev_err(fsl_lpspi->dev, + "per-clk should be at least two times of transfer speed"); + return -EINVAL; + } + for (prescale = 0; prescale < 8; prescale++) { scldiv = perclk_rate / (clkdivs[prescale] * config.speed_hz) - 2; @@ -257,12 +345,59 @@ static int fsl_lpspi_set_bitrate(struct fsl_lpspi_data *fsl_lpspi) writel(scldiv | (scldiv << 8) | ((scldiv >> 1) << 16), fsl_lpspi->base + IMX7ULP_CCR); - dev_dbg(fsl_lpspi->dev, "perclk=%d, speed=%d, prescale =%d, scldiv=%d\n", + dev_dbg(fsl_lpspi->dev, "perclk=%d, speed=%d, prescale=%d, scldiv=%d\n", perclk_rate, config.speed_hz, prescale, scldiv); return 0; } +static int fsl_lpspi_dma_configure(struct spi_controller *controller) +{ + int ret; + enum dma_slave_buswidth buswidth; + struct dma_slave_config rx = {}, tx = {}; + struct fsl_lpspi_data *fsl_lpspi = + spi_controller_get_devdata(controller); + + switch (fsl_lpspi_bytes_per_word(fsl_lpspi->config.bpw)) { + case 4: + buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + case 2: + buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case 1: + buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + default: + return -EINVAL; + } + + tx.direction = DMA_MEM_TO_DEV; + tx.dst_addr = fsl_lpspi->base_phys + IMX7ULP_TDR; + tx.dst_addr_width = buswidth; + tx.dst_maxburst = 1; + ret = dmaengine_slave_config(controller->dma_tx, &tx); + if (ret) { + dev_err(fsl_lpspi->dev, "TX dma configuration failed with %d\n", + ret); + return ret; + } + + rx.direction = DMA_DEV_TO_MEM; + rx.src_addr = fsl_lpspi->base_phys + IMX7ULP_RDR; + rx.src_addr_width = buswidth; + rx.src_maxburst = 1; + ret = dmaengine_slave_config(controller->dma_rx, &rx); + if (ret) { + dev_err(fsl_lpspi->dev, "RX dma configuration failed with %d\n", + ret); + return ret; + } + + return 0; +} + static int fsl_lpspi_config(struct fsl_lpspi_data *fsl_lpspi) { u32 temp; @@ -288,18 +423,27 @@ static int fsl_lpspi_config(struct fsl_lpspi_data *fsl_lpspi) temp |= CR_RRF | CR_RTF | CR_MEN; writel(temp, fsl_lpspi->base + IMX7ULP_CR); + temp = 0; + if (fsl_lpspi->usedma) + temp = DER_TDDE | DER_RDDE; + writel(temp, fsl_lpspi->base + IMX7ULP_DER); + return 0; } -static void fsl_lpspi_setup_transfer(struct spi_device *spi, +static int fsl_lpspi_setup_transfer(struct spi_controller *controller, + struct spi_device *spi, struct spi_transfer *t) { struct fsl_lpspi_data *fsl_lpspi = spi_controller_get_devdata(spi->controller); + if (t == NULL) + return -EINVAL; + fsl_lpspi->config.mode = spi->mode; - fsl_lpspi->config.bpw = t ? t->bits_per_word : spi->bits_per_word; - fsl_lpspi->config.speed_hz = t ? t->speed_hz : spi->max_speed_hz; + fsl_lpspi->config.bpw = t->bits_per_word; + fsl_lpspi->config.speed_hz = t->speed_hz; fsl_lpspi->config.chip_select = spi->chip_select; if (!fsl_lpspi->config.speed_hz) @@ -324,7 +468,12 @@ static void fsl_lpspi_setup_transfer(struct spi_device *spi, else fsl_lpspi->watermark = fsl_lpspi->txfifosize; - fsl_lpspi_config(fsl_lpspi); + if (fsl_lpspi_can_dma(controller, spi, t)) + fsl_lpspi->usedma = 1; + else + fsl_lpspi->usedma = 0; + + return fsl_lpspi_config(fsl_lpspi); } static int fsl_lpspi_slave_abort(struct spi_controller *controller) @@ -333,7 +482,13 @@ static int fsl_lpspi_slave_abort(struct spi_controller *controller) spi_controller_get_devdata(controller); fsl_lpspi->slave_aborted = true; - complete(&fsl_lpspi->xfer_done); + if (!fsl_lpspi->usedma) + complete(&fsl_lpspi->xfer_done); + else { + complete(&fsl_lpspi->dma_tx_completion); + complete(&fsl_lpspi->dma_rx_completion); + } + return 0; } @@ -362,8 +517,10 @@ static int fsl_lpspi_reset(struct fsl_lpspi_data *fsl_lpspi) { u32 temp; - /* Disable all interrupt */ - fsl_lpspi_intctrl(fsl_lpspi, 0); + if (!fsl_lpspi->usedma) { + /* Disable all interrupt */ + fsl_lpspi_intctrl(fsl_lpspi, 0); + } /* W1C for all flags in SR */ temp = 0x3F << 8; @@ -376,8 +533,177 @@ static int fsl_lpspi_reset(struct fsl_lpspi_data *fsl_lpspi) return 0; } -static int fsl_lpspi_transfer_one(struct spi_controller *controller, - struct spi_device *spi, +static void fsl_lpspi_dma_rx_callback(void *cookie) +{ + struct fsl_lpspi_data *fsl_lpspi = (struct fsl_lpspi_data *)cookie; + + complete(&fsl_lpspi->dma_rx_completion); +} + +static void fsl_lpspi_dma_tx_callback(void *cookie) +{ + struct fsl_lpspi_data *fsl_lpspi = (struct fsl_lpspi_data *)cookie; + + complete(&fsl_lpspi->dma_tx_completion); +} + +static int fsl_lpspi_calculate_timeout(struct fsl_lpspi_data *fsl_lpspi, + int size) +{ + unsigned long timeout = 0; + + /* Time with actual data transfer and CS change delay related to HW */ + timeout = (8 + 4) * size / fsl_lpspi->config.speed_hz; + + /* Add extra second for scheduler related activities */ + timeout += 1; + + /* Double calculated timeout */ + return msecs_to_jiffies(2 * timeout * MSEC_PER_SEC); +} + +static int fsl_lpspi_dma_transfer(struct spi_controller *controller, + struct fsl_lpspi_data *fsl_lpspi, + struct spi_transfer *transfer) +{ + struct dma_async_tx_descriptor *desc_tx, *desc_rx; + unsigned long transfer_timeout; + unsigned long timeout; + struct sg_table *tx = &transfer->tx_sg, *rx = &transfer->rx_sg; + int ret; + + ret = fsl_lpspi_dma_configure(controller); + if (ret) + return ret; + + desc_rx = dmaengine_prep_slave_sg(controller->dma_rx, + rx->sgl, rx->nents, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc_rx) + return -EINVAL; + + desc_rx->callback = fsl_lpspi_dma_rx_callback; + desc_rx->callback_param = (void *)fsl_lpspi; + dmaengine_submit(desc_rx); + reinit_completion(&fsl_lpspi->dma_rx_completion); + dma_async_issue_pending(controller->dma_rx); + + desc_tx = dmaengine_prep_slave_sg(controller->dma_tx, + tx->sgl, tx->nents, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc_tx) { + dmaengine_terminate_all(controller->dma_tx); + return -EINVAL; + } + + desc_tx->callback = fsl_lpspi_dma_tx_callback; + desc_tx->callback_param = (void *)fsl_lpspi; + dmaengine_submit(desc_tx); + reinit_completion(&fsl_lpspi->dma_tx_completion); + dma_async_issue_pending(controller->dma_tx); + + fsl_lpspi->slave_aborted = false; + + if (!fsl_lpspi->is_slave) { + transfer_timeout = fsl_lpspi_calculate_timeout(fsl_lpspi, + transfer->len); + + /* Wait eDMA to finish the data transfer.*/ + timeout = wait_for_completion_timeout(&fsl_lpspi->dma_tx_completion, + transfer_timeout); + if (!timeout) { + dev_err(fsl_lpspi->dev, "I/O Error in DMA TX\n"); + dmaengine_terminate_all(controller->dma_tx); + dmaengine_terminate_all(controller->dma_rx); + fsl_lpspi_reset(fsl_lpspi); + return -ETIMEDOUT; + } + + timeout = wait_for_completion_timeout(&fsl_lpspi->dma_rx_completion, + transfer_timeout); + if (!timeout) { + dev_err(fsl_lpspi->dev, "I/O Error in DMA RX\n"); + dmaengine_terminate_all(controller->dma_tx); + dmaengine_terminate_all(controller->dma_rx); + fsl_lpspi_reset(fsl_lpspi); + return -ETIMEDOUT; + } + } else { + if (wait_for_completion_interruptible(&fsl_lpspi->dma_tx_completion) || + fsl_lpspi->slave_aborted) { + dev_dbg(fsl_lpspi->dev, + "I/O Error in DMA TX interrupted\n"); + dmaengine_terminate_all(controller->dma_tx); + dmaengine_terminate_all(controller->dma_rx); + fsl_lpspi_reset(fsl_lpspi); + return -EINTR; + } + + if (wait_for_completion_interruptible(&fsl_lpspi->dma_rx_completion) || + fsl_lpspi->slave_aborted) { + dev_dbg(fsl_lpspi->dev, + "I/O Error in DMA RX interrupted\n"); + dmaengine_terminate_all(controller->dma_tx); + dmaengine_terminate_all(controller->dma_rx); + fsl_lpspi_reset(fsl_lpspi); + return -EINTR; + } + } + + fsl_lpspi_reset(fsl_lpspi); + + return 0; +} + +static void fsl_lpspi_dma_exit(struct spi_controller *controller) +{ + if (controller->dma_rx) { + dma_release_channel(controller->dma_rx); + controller->dma_rx = NULL; + } + + if (controller->dma_tx) { + dma_release_channel(controller->dma_tx); + controller->dma_tx = NULL; + } +} + +static int fsl_lpspi_dma_init(struct device *dev, + struct fsl_lpspi_data *fsl_lpspi, + struct spi_controller *controller) +{ + int ret; + + /* Prepare for TX DMA: */ + controller->dma_tx = dma_request_slave_channel_reason(dev, "tx"); + if (IS_ERR(controller->dma_tx)) { + ret = PTR_ERR(controller->dma_tx); + dev_dbg(dev, "can't get the TX DMA channel, error %d!\n", ret); + controller->dma_tx = NULL; + goto err; + } + + /* Prepare for RX DMA: */ + controller->dma_rx = dma_request_slave_channel_reason(dev, "rx"); + if (IS_ERR(controller->dma_rx)) { + ret = PTR_ERR(controller->dma_rx); + dev_dbg(dev, "can't get the RX DMA channel, error %d\n", ret); + controller->dma_rx = NULL; + goto err; + } + + init_completion(&fsl_lpspi->dma_rx_completion); + init_completion(&fsl_lpspi->dma_tx_completion); + controller->can_dma = fsl_lpspi_can_dma; + controller->max_dma_len = FSL_LPSPI_MAX_EDMA_BYTES; + + return 0; +err: + fsl_lpspi_dma_exit(controller); + return ret; +} + +static int fsl_lpspi_pio_transfer(struct spi_controller *controller, struct spi_transfer *t) { struct fsl_lpspi_data *fsl_lpspi = @@ -402,37 +728,30 @@ static int fsl_lpspi_transfer_one(struct spi_controller *controller, return 0; } -static int fsl_lpspi_transfer_one_msg(struct spi_controller *controller, - struct spi_message *msg) +static int fsl_lpspi_transfer_one(struct spi_controller *controller, + struct spi_device *spi, + struct spi_transfer *t) { struct fsl_lpspi_data *fsl_lpspi = - spi_controller_get_devdata(controller); - struct spi_device *spi = msg->spi; - struct spi_transfer *xfer; - bool is_first_xfer = true; - int ret = 0; - - msg->status = 0; - msg->actual_length = 0; - - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - fsl_lpspi_setup_transfer(spi, xfer); - fsl_lpspi_set_cmd(fsl_lpspi, is_first_xfer); - - is_first_xfer = false; + spi_controller_get_devdata(controller); + int ret; - ret = fsl_lpspi_transfer_one(controller, spi, xfer); - if (ret < 0) - goto complete; + fsl_lpspi->is_first_byte = true; + ret = fsl_lpspi_setup_transfer(controller, spi, t); + if (ret < 0) + return ret; - msg->actual_length += xfer->len; - } + fsl_lpspi_set_cmd(fsl_lpspi); + fsl_lpspi->is_first_byte = false; -complete: - msg->status = ret; - spi_finalize_current_message(controller); + if (fsl_lpspi->usedma) + ret = fsl_lpspi_dma_transfer(controller, fsl_lpspi, t); + else + ret = fsl_lpspi_pio_transfer(controller, t); + if (ret < 0) + return ret; - return ret; + return 0; } static irqreturn_t fsl_lpspi_isr(int irq, void *dev_id) @@ -452,7 +771,7 @@ static irqreturn_t fsl_lpspi_isr(int irq, void *dev_id) } if (temp_SR & SR_MBF || - readl(fsl_lpspi->base + IMX7ULP_FSR) & FSR_RXCOUNT) { + readl(fsl_lpspi->base + IMX7ULP_FSR) & FSR_TXCOUNT) { writel(SR_FCF, fsl_lpspi->base + IMX7ULP_SR); fsl_lpspi_intctrl(fsl_lpspi, IER_FCIE); return IRQ_HANDLED; @@ -467,12 +786,54 @@ static irqreturn_t fsl_lpspi_isr(int irq, void *dev_id) return IRQ_NONE; } +static int fsl_lpspi_runtime_resume(struct device *dev) +{ + struct fsl_lpspi_data *fsl_lpspi = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(fsl_lpspi->clk_per); + if (ret) + return ret; + + ret = clk_prepare_enable(fsl_lpspi->clk_ipg); + if (ret) { + clk_disable_unprepare(fsl_lpspi->clk_per); + return ret; + } + + return 0; +} + +static int fsl_lpspi_runtime_suspend(struct device *dev) +{ + struct fsl_lpspi_data *fsl_lpspi = dev_get_drvdata(dev); + + clk_disable_unprepare(fsl_lpspi->clk_per); + clk_disable_unprepare(fsl_lpspi->clk_ipg); + + return 0; +} + +static int fsl_lpspi_init_rpm(struct fsl_lpspi_data *fsl_lpspi) +{ + struct device *dev = fsl_lpspi->dev; + + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, FSL_LPSPI_RPM_TIMEOUT); + pm_runtime_use_autosuspend(dev); + + return 0; +} + static int fsl_lpspi_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; struct fsl_lpspi_data *fsl_lpspi; struct spi_controller *controller; + struct spi_imx_master *lpspi_platform_info = + dev_get_platdata(&pdev->dev); struct resource *res; - int ret, irq; + int i, ret, irq; u32 temp; if (of_property_read_bool((&pdev->dev)->of_node, "spi-slave")) @@ -492,10 +853,34 @@ static int fsl_lpspi_probe(struct platform_device *pdev) fsl_lpspi = spi_controller_get_devdata(controller); fsl_lpspi->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, fsl_lpspi); fsl_lpspi->is_slave = of_property_read_bool((&pdev->dev)->of_node, "spi-slave"); - controller->transfer_one_message = fsl_lpspi_transfer_one_msg; + if (!fsl_lpspi->is_slave) { + for (i = 0; i < controller->num_chipselect; i++) { + int cs_gpio = of_get_named_gpio(np, "cs-gpios", i); + + if (!gpio_is_valid(cs_gpio) && lpspi_platform_info) + cs_gpio = lpspi_platform_info->chipselect[i]; + + fsl_lpspi->chipselect[i] = cs_gpio; + if (!gpio_is_valid(cs_gpio)) + continue; + + ret = devm_gpio_request(&pdev->dev, + fsl_lpspi->chipselect[i], + DRIVER_NAME); + if (ret) { + dev_err(&pdev->dev, "can't get cs gpios\n"); + goto out_controller_put; + } + } + controller->cs_gpios = fsl_lpspi->chipselect; + controller->prepare_message = fsl_lpspi_prepare_message; + } + + controller->transfer_one = fsl_lpspi_transfer_one; controller->prepare_transfer_hardware = lpspi_prepare_xfer_hardware; controller->unprepare_transfer_hardware = lpspi_unprepare_xfer_hardware; controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; @@ -512,6 +897,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev) ret = PTR_ERR(fsl_lpspi->base); goto out_controller_put; } + fsl_lpspi->base_phys = res->start; irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -526,23 +912,39 @@ static int fsl_lpspi_probe(struct platform_device *pdev) goto out_controller_put; } - fsl_lpspi->clk = devm_clk_get(&pdev->dev, "ipg"); - if (IS_ERR(fsl_lpspi->clk)) { - ret = PTR_ERR(fsl_lpspi->clk); + fsl_lpspi->clk_per = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(fsl_lpspi->clk_per)) { + ret = PTR_ERR(fsl_lpspi->clk_per); goto out_controller_put; } - ret = clk_prepare_enable(fsl_lpspi->clk); - if (ret) { - dev_err(&pdev->dev, "can't enable lpspi clock, ret=%d\n", ret); + fsl_lpspi->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(fsl_lpspi->clk_ipg)) { + ret = PTR_ERR(fsl_lpspi->clk_ipg); + goto out_controller_put; + } + + /* enable the clock */ + ret = fsl_lpspi_init_rpm(fsl_lpspi); + if (ret) goto out_controller_put; + + ret = pm_runtime_get_sync(fsl_lpspi->dev); + if (ret < 0) { + dev_err(fsl_lpspi->dev, "failed to enable clock\n"); + return ret; } temp = readl(fsl_lpspi->base + IMX7ULP_PARAM); fsl_lpspi->txfifosize = 1 << (temp & 0x0f); fsl_lpspi->rxfifosize = 1 << ((temp >> 8) & 0x0f); - clk_disable_unprepare(fsl_lpspi->clk); + ret = fsl_lpspi_dma_init(&pdev->dev, fsl_lpspi, controller); + if (ret == -EPROBE_DEFER) + goto out_controller_put; + + if (ret < 0) + dev_err(&pdev->dev, "dma setup error %d, use pio\n", ret); ret = devm_spi_register_controller(&pdev->dev, controller); if (ret < 0) { @@ -564,15 +966,50 @@ static int fsl_lpspi_remove(struct platform_device *pdev) struct fsl_lpspi_data *fsl_lpspi = spi_controller_get_devdata(controller); - clk_disable_unprepare(fsl_lpspi->clk); + pm_runtime_disable(fsl_lpspi->dev); + + spi_master_put(controller); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int fsl_lpspi_suspend(struct device *dev) +{ + int ret; + + pinctrl_pm_select_sleep_state(dev); + ret = pm_runtime_force_suspend(dev); + return ret; +} + +static int fsl_lpspi_resume(struct device *dev) +{ + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) { + dev_err(dev, "Error in resume: %d\n", ret); + return ret; + } + + pinctrl_pm_select_default_state(dev); return 0; } +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_lpspi_pm_ops = { + SET_RUNTIME_PM_OPS(fsl_lpspi_runtime_suspend, + fsl_lpspi_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(fsl_lpspi_suspend, fsl_lpspi_resume) +}; static struct platform_driver fsl_lpspi_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = fsl_lpspi_dt_ids, + .pm = &fsl_lpspi_pm_ops, }, .probe = fsl_lpspi_probe, .remove = fsl_lpspi_remove,