From 1543e317f1da31b75942316931e8f491a8920811 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Thu, 04 Jan 2024 10:08:02 +0000 Subject: [PATCH] disable FB --- kernel/drivers/spi/spi-gpio.c | 282 +++++++++++++++++++++++++------------------------------ 1 files changed, 129 insertions(+), 153 deletions(-) diff --git a/kernel/drivers/spi/spi-gpio.c b/kernel/drivers/spi/spi-gpio.c index 341d295..3ffdab6 100644 --- a/kernel/drivers/spi/spi-gpio.c +++ b/kernel/drivers/spi/spi-gpio.c @@ -1,18 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * SPI master driver using generic bitbanged GPIO * * Copyright (C) 2006,2008 David Brownell * Copyright (C) 2017 Linus Walleij - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/kernel.h> #include <linux/module.h> @@ -35,20 +26,16 @@ * platform_device->driver_data ... points to spi_gpio * * spi->controller_state ... reserved for bitbang framework code - * spi->controller_data ... holds chipselect GPIO * * spi->master->dev.driver_data ... points to spi_gpio->bitbang */ struct spi_gpio { struct spi_bitbang bitbang; - struct spi_gpio_platform_data pdata; - struct platform_device *pdev; struct gpio_desc *sck; struct gpio_desc *miso; struct gpio_desc *mosi; struct gpio_desc **cs_gpios; - bool has_cs; }; /*----------------------------------------------------------------------*/ @@ -94,12 +81,6 @@ bang = spi_master_get_devdata(spi->master); spi_gpio = container_of(bang, struct spi_gpio, bitbang); return spi_gpio; -} - -static inline struct spi_gpio_platform_data *__pure -spi_to_pdata(const struct spi_device *spi) -{ - return &spi_to_spi_gpio(spi)->pdata; } /* These helpers are in turn called by the bitbang inlines */ @@ -224,7 +205,7 @@ gpiod_set_value_cansleep(spi_gpio->sck, spi->mode & SPI_CPOL); /* Drive chip select line, if we have one */ - if (spi_gpio->has_cs) { + if (spi_gpio->cs_gpios) { struct gpio_desc *cs = spi_gpio->cs_gpios[spi->chip_select]; /* SPI chip selects are normally active-low */ @@ -242,10 +223,12 @@ * The CS GPIOs have already been * initialized from the descriptor lookup. */ - cs = spi_gpio->cs_gpios[spi->chip_select]; - if (!spi->controller_state && cs) - status = gpiod_direction_output(cs, - !(spi->mode & SPI_CS_HIGH)); + if (spi_gpio->cs_gpios) { + cs = spi_gpio->cs_gpios[spi->chip_select]; + if (!spi->controller_state && cs) + status = gpiod_direction_output(cs, + !(spi->mode & SPI_CS_HIGH)); + } if (!status) status = spi_bitbang_setup(spi); @@ -256,11 +239,39 @@ static int spi_gpio_set_direction(struct spi_device *spi, bool output) { struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); + int ret; if (output) return gpiod_direction_output(spi_gpio->mosi, 1); - else - return gpiod_direction_input(spi_gpio->mosi); + + /* + * Only change MOSI to an input if using 3WIRE mode. + * Otherwise, MOSI could be left floating if there is + * no pull resistor connected to the I/O pin, or could + * be left logic high if there is a pull-up. Transmitting + * logic high when only clocking MISO data in can put some + * SPI devices in to a bad state. + */ + if (spi->mode & SPI_3WIRE) { + ret = gpiod_direction_input(spi_gpio->mosi); + if (ret) + return ret; + } + /* + * Send a turnaround high impedance cycle when switching + * from output to input. Theoretically there should be + * a clock delay here, but as has been noted above, the + * nsec delay function for bit-banged GPIO is simply + * {} because bit-banging just doesn't get fast enough + * anyway. + */ + if (spi->mode & SPI_3WIRE_HIZ) { + gpiod_set_value_cansleep(spi_gpio->sck, + !(spi->mode & SPI_CPOL)); + gpiod_set_value_cansleep(spi_gpio->sck, + !!(spi->mode & SPI_CPOL)); + } + return 0; } static void spi_gpio_cleanup(struct spi_device *spi) @@ -278,41 +289,18 @@ * floating signals. (A weak pulldown would save power too, but many * drivers expect to see all-ones data as the no slave "response".) */ -static int spi_gpio_request(struct device *dev, - struct spi_gpio *spi_gpio, - unsigned int num_chipselects, - u16 *mflags) +static int spi_gpio_request(struct device *dev, struct spi_gpio *spi_gpio) { - int i; - spi_gpio->mosi = devm_gpiod_get_optional(dev, "mosi", GPIOD_OUT_LOW); if (IS_ERR(spi_gpio->mosi)) return PTR_ERR(spi_gpio->mosi); - if (!spi_gpio->mosi) - /* HW configuration without MOSI pin */ - *mflags |= SPI_MASTER_NO_TX; spi_gpio->miso = devm_gpiod_get_optional(dev, "miso", GPIOD_IN); if (IS_ERR(spi_gpio->miso)) return PTR_ERR(spi_gpio->miso); - /* - * No setting SPI_MASTER_NO_RX here - if there is only a MOSI - * pin connected the host can still do RX by changing the - * direction of the line. - */ spi_gpio->sck = devm_gpiod_get(dev, "sck", GPIOD_OUT_LOW); - if (IS_ERR(spi_gpio->sck)) - return PTR_ERR(spi_gpio->sck); - - for (i = 0; i < num_chipselects; i++) { - spi_gpio->cs_gpios[i] = devm_gpiod_get_index(dev, "cs", - i, GPIOD_OUT_HIGH); - if (IS_ERR(spi_gpio->cs_gpios[i])) - return PTR_ERR(spi_gpio->cs_gpios[i]); - } - - return 0; + return PTR_ERR_OR_ZERO(spi_gpio->sck); } #ifdef CONFIG_OF @@ -322,139 +310,128 @@ }; MODULE_DEVICE_TABLE(of, spi_gpio_dt_ids); -static int spi_gpio_probe_dt(struct platform_device *pdev) +static int spi_gpio_probe_dt(struct platform_device *pdev, + struct spi_master *master) { - int ret; - u32 tmp; - struct spi_gpio_platform_data *pdata; - struct device_node *np = pdev->dev.of_node; - const struct of_device_id *of_id = - of_match_device(spi_gpio_dt_ids, &pdev->dev); + master->dev.of_node = pdev->dev.of_node; + master->use_gpio_descriptors = true; - if (!of_id) - return 0; - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - - ret = of_property_read_u32(np, "num-chipselects", &tmp); - if (ret < 0) { - dev_err(&pdev->dev, "num-chipselects property not found\n"); - goto error_free; - } - - pdata->num_chipselect = tmp; - pdev->dev.platform_data = pdata; - - return 1; - -error_free: - devm_kfree(&pdev->dev, pdata); - return ret; + return 0; } #else -static inline int spi_gpio_probe_dt(struct platform_device *pdev) +static inline int spi_gpio_probe_dt(struct platform_device *pdev, + struct spi_master *master) { return 0; } #endif + +static int spi_gpio_probe_pdata(struct platform_device *pdev, + struct spi_master *master) +{ + struct device *dev = &pdev->dev; + struct spi_gpio_platform_data *pdata = dev_get_platdata(dev); + struct spi_gpio *spi_gpio = spi_master_get_devdata(master); + int i; + +#ifdef GENERIC_BITBANG + if (!pdata || !pdata->num_chipselect) + return -ENODEV; +#endif + /* + * The master needs to think there is a chipselect even if not + * connected + */ + master->num_chipselect = pdata->num_chipselect ?: 1; + + spi_gpio->cs_gpios = devm_kcalloc(dev, master->num_chipselect, + sizeof(*spi_gpio->cs_gpios), + GFP_KERNEL); + if (!spi_gpio->cs_gpios) + return -ENOMEM; + + for (i = 0; i < master->num_chipselect; i++) { + spi_gpio->cs_gpios[i] = devm_gpiod_get_index(dev, "cs", i, + GPIOD_OUT_HIGH); + if (IS_ERR(spi_gpio->cs_gpios[i])) + return PTR_ERR(spi_gpio->cs_gpios[i]); + } + + return 0; +} static int spi_gpio_probe(struct platform_device *pdev) { int status; struct spi_master *master; struct spi_gpio *spi_gpio; - struct spi_gpio_platform_data *pdata; - u16 master_flags = 0; - bool use_of = 0; + struct device *dev = &pdev->dev; + struct spi_bitbang *bb; - status = spi_gpio_probe_dt(pdev); - if (status < 0) - return status; - if (status > 0) - use_of = 1; - - pdata = dev_get_platdata(&pdev->dev); -#ifdef GENERIC_BITBANG - if (!pdata || (!use_of && !pdata->num_chipselect)) - return -ENODEV; -#endif - - master = devm_spi_alloc_master(&pdev->dev, sizeof(*spi_gpio)); + master = devm_spi_alloc_master(dev, sizeof(*spi_gpio)); if (!master) return -ENOMEM; + if (pdev->dev.of_node) + status = spi_gpio_probe_dt(pdev, master); + else + status = spi_gpio_probe_pdata(pdev, master); + + if (status) + return status; + spi_gpio = spi_master_get_devdata(master); - spi_gpio->cs_gpios = devm_kcalloc(&pdev->dev, - pdata->num_chipselect, - sizeof(*spi_gpio->cs_gpios), - GFP_KERNEL); - if (!spi_gpio->cs_gpios) - return -ENOMEM; - - platform_set_drvdata(pdev, spi_gpio); - - /* Determine if we have chip selects connected */ - spi_gpio->has_cs = !!pdata->num_chipselect; - - spi_gpio->pdev = pdev; - if (pdata) - spi_gpio->pdata = *pdata; - - status = spi_gpio_request(&pdev->dev, spi_gpio, - pdata->num_chipselect, &master_flags); + status = spi_gpio_request(dev, spi_gpio); if (status) return status; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); - master->mode_bits = SPI_3WIRE | SPI_CPHA | SPI_CPOL | SPI_CS_HIGH; - master->flags = master_flags; + master->mode_bits = SPI_3WIRE | SPI_3WIRE_HIZ | SPI_CPHA | SPI_CPOL | + SPI_CS_HIGH; + if (!spi_gpio->mosi) { + /* HW configuration without MOSI pin + * + * No setting SPI_MASTER_NO_RX here - if there is only + * a MOSI pin connected the host can still do RX by + * changing the direction of the line. + */ + master->flags = SPI_MASTER_NO_TX; + } + master->bus_num = pdev->id; - /* The master needs to think there is a chipselect even if not connected */ - master->num_chipselect = spi_gpio->has_cs ? pdata->num_chipselect : 1; master->setup = spi_gpio_setup; master->cleanup = spi_gpio_cleanup; -#ifdef CONFIG_OF - master->dev.of_node = pdev->dev.of_node; -#endif - spi_gpio->bitbang.master = master; - spi_gpio->bitbang.chipselect = spi_gpio_chipselect; - spi_gpio->bitbang.set_line_direction = spi_gpio_set_direction; + bb = &spi_gpio->bitbang; + bb->master = master; + /* + * There is some additional business, apart from driving the CS GPIO + * line, that we need to do on selection. This makes the local + * callback for chipselect always get called. + */ + master->flags |= SPI_MASTER_GPIO_SS; + bb->chipselect = spi_gpio_chipselect; + bb->set_line_direction = spi_gpio_set_direction; - if ((master_flags & SPI_MASTER_NO_TX) == 0) { - spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0; - spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1; - spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2; - spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3; + if (master->flags & SPI_MASTER_NO_TX) { + bb->txrx_word[SPI_MODE_0] = spi_gpio_spec_txrx_word_mode0; + bb->txrx_word[SPI_MODE_1] = spi_gpio_spec_txrx_word_mode1; + bb->txrx_word[SPI_MODE_2] = spi_gpio_spec_txrx_word_mode2; + bb->txrx_word[SPI_MODE_3] = spi_gpio_spec_txrx_word_mode3; } else { - spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_spec_txrx_word_mode0; - spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_spec_txrx_word_mode1; - spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_spec_txrx_word_mode2; - spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_spec_txrx_word_mode3; + bb->txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0; + bb->txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1; + bb->txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2; + bb->txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3; } - spi_gpio->bitbang.setup_transfer = spi_bitbang_setup_transfer; + bb->setup_transfer = spi_bitbang_setup_transfer; - return spi_bitbang_start(&spi_gpio->bitbang); -} + status = spi_bitbang_init(&spi_gpio->bitbang); + if (status) + return status; -static int spi_gpio_remove(struct platform_device *pdev) -{ - struct spi_gpio *spi_gpio; - struct spi_gpio_platform_data *pdata; - - spi_gpio = platform_get_drvdata(pdev); - pdata = dev_get_platdata(&pdev->dev); - - /* stop() unregisters child devices too */ - spi_bitbang_stop(&spi_gpio->bitbang); - - spi_master_put(spi_gpio->bitbang.master); - - return 0; + return devm_spi_register_master(&pdev->dev, master); } MODULE_ALIAS("platform:" DRIVER_NAME); @@ -465,7 +442,6 @@ .of_match_table = of_match_ptr(spi_gpio_dt_ids), }, .probe = spi_gpio_probe, - .remove = spi_gpio_remove, }; module_platform_driver(spi_gpio_driver); -- Gitblit v1.6.2