Add cs-gpio feature for LPSPI. Use fsl_lpspi_prepare_message() and fsl_lpspi_unprepare_message() to enable and control cs line. These two functions will be only called at the beginning and the ending of a message transfer. Still support using the mode without cs-gpio. It depends on if attribute cs-gpio has been configured in dts file. Signed-off-by: Clark Wang <xiaoning.wang@xxxxxxx> --- drivers/spi/spi-fsl-lpspi.c | 79 ++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index a7d01b79827b..c6fe3f94de19 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -9,6 +9,7 @@ #include <linux/completion.h> #include <linux/delay.h> #include <linux/err.h> +#include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> @@ -16,7 +17,9 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/of_gpio.h> #include <linux/pinctrl/consumer.h> +#include <linux/platform_data/spi-imx.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/slab.h> @@ -28,6 +31,10 @@ #define FSL_LPSPI_RPM_TIMEOUT 50 /* 50ms */ +#define LPSPI_CS_ACTIVE 1 +#define LPSPI_CS_INACTIVE 0 +#define LPSPI_CS_DELAY 100 + /* i.MX7ULP LPSPI registers */ #define IMX7ULP_VERID 0x0 #define IMX7ULP_PARAM 0x4 @@ -104,6 +111,8 @@ struct fsl_lpspi_data { struct completion xfer_done; bool slave_aborted; + + int chipselect[0]; }; static const struct of_device_id fsl_lpspi_dt_ids[] = { @@ -176,6 +185,48 @@ static int lpspi_unprepare_xfer_hardware(struct spi_controller *controller) return 0; } +static void fsl_lpspi_chipselect(struct spi_device *spi, bool enable) +{ + struct fsl_lpspi_data *fsl_lpspi = + spi_controller_get_devdata(spi->controller); + int gpio = fsl_lpspi->chipselect[spi->chip_select]; + + enable = (!!(spi->mode & SPI_CS_HIGH) == enable); + + if (!gpio_is_valid(gpio)) + return; + + gpio_set_value_cansleep(gpio, enable); +} + +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, + fsl_lpspi->config.mode & SPI_CS_HIGH ? 0 : 1); + } + + fsl_lpspi_chipselect(spi, LPSPI_CS_ACTIVE); + + return 0; +} + +static int fsl_lpspi_unprepare_message(struct spi_controller *controller, + struct spi_message *msg) +{ + struct spi_device *spi = msg->spi; + + fsl_lpspi_chipselect(spi, LPSPI_CS_INACTIVE); + + return 0; +} + static void fsl_lpspi_write_tx_fifo(struct fsl_lpspi_data *fsl_lpspi) { u8 txfifo_cnt; @@ -512,10 +563,13 @@ static int fsl_lpspi_init_rpm(struct fsl_lpspi_data *fsl_lpspi) 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")) @@ -539,6 +593,29 @@ static int fsl_lpspi_probe(struct platform_device *pdev) fsl_lpspi->is_slave = of_property_read_bool((&pdev->dev)->of_node, "spi-slave"); + 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->prepare_message = fsl_lpspi_prepare_message; + controller->unprepare_message = fsl_lpspi_unprepare_message; + } + controller->transfer_one_message = fsl_lpspi_transfer_one_msg; controller->prepare_transfer_hardware = lpspi_prepare_xfer_hardware; controller->unprepare_transfer_hardware = lpspi_unprepare_xfer_hardware; -- 2.17.1