diff options
Diffstat (limited to 'drivers/spi/spi-gpio.c')
| -rw-r--r-- | drivers/spi/spi-gpio.c | 227 | 
1 files changed, 95 insertions, 132 deletions
diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 53b35c56a557..487ee55d26f7 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -35,20 +35,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;  };  /*----------------------------------------------------------------------*/ @@ -96,12 +92,6 @@ spi_to_spi_gpio(const struct spi_device *spi)  	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 */  static inline void setsck(const struct spi_device *spi, int is_on)  { @@ -224,7 +214,7 @@ static void spi_gpio_chipselect(struct spi_device *spi, int is_active)  		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 +232,12 @@ static int spi_gpio_setup(struct spi_device *spi)  	 * 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); @@ -296,40 +288,20 @@ static void spi_gpio_cleanup(struct spi_device *spi)   * 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;  } @@ -340,142 +312,134 @@ static const struct of_device_id spi_gpio_dt_ids[] = {  };  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); - -	if (!of_id) -		return 0; +	master->dev.of_node = pdev->dev.of_node; +	master->use_gpio_descriptors = true; -	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); -	if (!pdata) -		return -ENOMEM; +	return 0; +} +#else +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; -	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; -	} +#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; -	pdata->num_chipselect = tmp; -	pdev->dev.platform_data = pdata; +	spi_gpio->cs_gpios = devm_kcalloc(dev, master->num_chipselect, +					  sizeof(*spi_gpio->cs_gpios), +					  GFP_KERNEL); +	if (!spi_gpio->cs_gpios) +		return -ENOMEM; -	return 1; +	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]); +	} -error_free: -	devm_kfree(&pdev->dev, pdata); -	return ret; +	return 0;  } -#else -static inline int spi_gpio_probe_dt(struct platform_device *pdev) + +static void spi_gpio_put(void *data)  { -	return 0; +	spi_master_put(data);  } -#endif  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; +	const struct of_device_id	*of_id; -	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 +	of_id = of_match_device(spi_gpio_dt_ids, &pdev->dev); -	master = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio)); +	master = spi_alloc_master(dev, sizeof(*spi_gpio));  	if (!master)  		return -ENOMEM; -	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; +	status = devm_add_action_or_reset(&pdev->dev, spi_gpio_put, master); +	if (status) +		return status; -	platform_set_drvdata(pdev, spi_gpio); +	if (of_id) +		status = spi_gpio_probe_dt(pdev, master); +	else +		status = spi_gpio_probe_pdata(pdev, master); -	/* Determine if we have chip selects connected */ -	spi_gpio->has_cs = !!pdata->num_chipselect; +	if (status) +		return status; -	spi_gpio->pdev = pdev; -	if (pdata) -		spi_gpio->pdata = *pdata; +	spi_gpio = spi_master_get_devdata(master); -	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_3WIRE_HIZ | SPI_CPHA | SPI_CPOL |  			    SPI_CS_HIGH; -	master->flags = master_flags; +	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; +	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; -	status = spi_bitbang_start(&spi_gpio->bitbang); +	status = spi_bitbang_init(&spi_gpio->bitbang);  	if (status) -		spi_master_put(master); - -	return status; -} - -static int spi_gpio_remove(struct platform_device *pdev) -{ -	struct spi_gpio			*spi_gpio; - -	spi_gpio = platform_get_drvdata(pdev); - -	/* stop() unregisters child devices too */ -	spi_bitbang_stop(&spi_gpio->bitbang); - -	spi_master_put(spi_gpio->bitbang.master); +		return status; -	return 0; +	return devm_spi_register_master(&pdev->dev, spi_master_get(master));  }  MODULE_ALIAS("platform:" DRIVER_NAME); @@ -486,7 +450,6 @@ static struct platform_driver spi_gpio_driver = {  		.of_match_table = of_match_ptr(spi_gpio_dt_ids),  	},  	.probe		= spi_gpio_probe, -	.remove		= spi_gpio_remove,  };  module_platform_driver(spi_gpio_driver);  | 
