diff mbox

wlcore: spi: add wl18xx support

Message ID 1459343218-24536-1-git-send-email-eyalr@ti.com (mailing list archive)
State Changes Requested
Delegated to: Kalle Valo
Headers show

Commit Message

Eyal Reizer March 30, 2016, 1:06 p.m. UTC
Add support for using with both wl12xx and wl18xx.

- all wilink family needs special init command for entering wspi mode.
  extra clock cycles should be sent after the spi init command while the
  cs pin is high.
- switch to controling the cs pin from the spi driver for achieveing the
  above.
- the selected cs gpio is read from the spi device-tree node using the
  cs-gpios field and setup as a gpio.
- See the example below for specifying the cs gpio using the cs-gpios entry

&spi0	{
 	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&spi0_pins>;
	cs-gpios = <&gpio0 5 0>;
	#address-cells = <1>;
	#size-cells = <0>;
 	wlcore: wlcore@0 {
		compatible = "ti,wl1835";
		vwlan-supply = <&wlan_en_reg>;
		spi-max-frequency = <48000000>;
		reg = <0>;      /* chip select 0 on spi0, ie spi0.0 */
		interrupt-parent = <&gpio0>;
		interrupts = <27 IRQ_TYPE_EDGE_RISING>;
 	};
};

Signed-off-by: Eyal Reizer <eyalr@ti.com>
---
 drivers/net/wireless/ti/wlcore/spi.c |  176 ++++++++++++++++++++++++++++++----
 1 file changed, 157 insertions(+), 19 deletions(-)

Comments

Reizer, Eyal April 6, 2016, 7:28 a.m. UTC | #1
Ping on this patch

> -----Original Message-----
> From: Eyal Reizer [mailto:eyalreizer@gmail.com]
> Sent: Wednesday, March 30, 2016 4:07 PM
> To: kvalo@codeaurora.org; linux-wireless@vger.kernel.org;
> netdev@vger.kernel.org; linux-kernel@vger.kernel.org
> Cc: Reizer, Eyal
> Subject: [PATCH] wlcore: spi: add wl18xx support
> 
> Add support for using with both wl12xx and wl18xx.
> 
> - all wilink family needs special init command for entering wspi mode.
>   extra clock cycles should be sent after the spi init command while the
>   cs pin is high.
> - switch to controling the cs pin from the spi driver for achieveing the
>   above.
> - the selected cs gpio is read from the spi device-tree node using the
>   cs-gpios field and setup as a gpio.
> - See the example below for specifying the cs gpio using the cs-gpios entry
> 
> &spi0	{
>  	status = "okay";
> 	pinctrl-names = "default";
> 	pinctrl-0 = <&spi0_pins>;
> 	cs-gpios = <&gpio0 5 0>;
> 	#address-cells = <1>;
> 	#size-cells = <0>;
>  	wlcore: wlcore@0 {
> 		compatible = "ti,wl1835";
> 		vwlan-supply = <&wlan_en_reg>;
> 		spi-max-frequency = <48000000>;
> 		reg = <0>;      /* chip select 0 on spi0, ie spi0.0 */
> 		interrupt-parent = <&gpio0>;
> 		interrupts = <27 IRQ_TYPE_EDGE_RISING>;
>  	};
> };
> 
> Signed-off-by: Eyal Reizer <eyalr@ti.com>
> ---
>  drivers/net/wireless/ti/wlcore/spi.c |  176
> ++++++++++++++++++++++++++++++----
>  1 file changed, 157 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/net/wireless/ti/wlcore/spi.c
> b/drivers/net/wireless/ti/wlcore/spi.c
> index 96d9c9d..6c5a369 100644
> --- a/drivers/net/wireless/ti/wlcore/spi.c
> +++ b/drivers/net/wireless/ti/wlcore/spi.c
> @@ -32,6 +32,7 @@
>  #include <linux/platform_device.h>
>  #include <linux/of_irq.h>
>  #include <linux/regulator/consumer.h>
> +#include <linux/gpio.h>
> 
>  #include "wlcore.h"
>  #include "wl12xx_80211.h"
> @@ -70,16 +71,30 @@
>  #define WSPI_MAX_CHUNK_SIZE    4092
> 
>  /*
> - * only support SPI for 12xx - this code should be reworked when 18xx
> - * support is introduced
> + * wl18xx driver aggregation buffer size is (13 * PAGE_SIZE) compared
> + to
> + * (4 * PAGE_SIZE) for wl12xx, so use the larger buffer needed for
> + wl18xx
>   */
> -#define SPI_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
> +#define SPI_AGGR_BUFFER_SIZE (13 * PAGE_SIZE)
> 
>  /* Maximum number of SPI write chunks */  #define
> WSPI_MAX_NUM_OF_CHUNKS \
>  	((SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) + 1)
> 
> 
> +struct wilink_familiy_data {
> +	char name[8];
> +};
> +
> +const struct wilink_familiy_data *wilink_data;
> +
> +static const struct wilink_familiy_data wl18xx_data = {
> +	.name = "wl18xx",
> +};
> +
> +static const struct wilink_familiy_data wl12xx_data = {
> +	.name = "wl12xx",
> +};
> +
>  struct wl12xx_spi_glue {
>  	struct device *dev;
>  	struct platform_device *core;
> @@ -120,6 +135,8 @@ static void wl12xx_spi_init(struct device *child)
>  	struct spi_transfer t;
>  	struct spi_message m;
>  	u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
> +	struct spi_device *spi = (struct spi_device *)glue->dev;
> +	struct spi_master *master = spi->master;
> 
>  	if (!cmd) {
>  		dev_err(child->parent,
> @@ -127,6 +144,15 @@ static void wl12xx_spi_init(struct device *child)
>  		return;
>  	}
> 
> +	if (!master->cs_gpios) {
> +		dev_err(child->parent,
> +			"spi chip select pin missing in platform data!\n");
> +		return;
> +	}
> +
> +	/* Drive CS line low */
> +	gpio_direction_output(master->cs_gpios[0], 0);
> +
>  	memset(&t, 0, sizeof(t));
>  	spi_message_init(&m);
> 
> @@ -163,6 +189,26 @@ static void wl12xx_spi_init(struct device *child)
>  	spi_message_add_tail(&t, &m);
> 
>  	spi_sync(to_spi_device(glue->dev), &m);
> +
> +	/* Send extra clocks with CS high. this is required by the wilink
> +	 * family in order for successfully enter WSPI mode
> +	 */
> +	gpio_direction_output(master->cs_gpios[0], 1);
> +
> +	memset(&m, 0, sizeof(m));
> +	spi_message_init(&m);
> +
> +	cmd[0] = 0xff;
> +	cmd[1] = 0xff;
> +	cmd[2] = 0xff;
> +	cmd[3] = 0xff;
> +	swab32s((u32 *)cmd);
> +
> +	t.tx_buf = cmd;
> +	t.len = 4;
> +	spi_message_add_tail(&t, &m);
> +	spi_sync(to_spi_device(glue->dev), &m);
> +
>  	kfree(cmd);
>  }
> 
> @@ -213,6 +259,16 @@ static int __must_check
> wl12xx_spi_raw_read(struct device *child, int addr,
>  	u32 *busy_buf;
>  	u32 *cmd;
>  	u32 chunk_len;
> +	struct spi_device *spi = (struct spi_device *)glue->dev;
> +	struct spi_master *master = spi->master;
> +
> +	if (!master->cs_gpios) {
> +		dev_err(child->parent,
> +			"spi chip select pin missing in platform data!\n");
> +		return -EINVAL;
> +	}
> +	/* Drive CS line low */
> +	gpio_direction_output(master->cs_gpios[0], 0);
> 
>  	while (len > 0) {
>  		chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len); @@
> -267,25 +323,44 @@ static int __must_check wl12xx_spi_raw_read(struct
> device *child, int addr,
>  		len -= chunk_len;
>  	}
> 
> +	/* Drive CS line high */
> +	gpio_direction_output(master->cs_gpios[0], 1);
>  	return 0;
>  }
> 
> -static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
> -					     void *buf, size_t len, bool fixed)
> +static int __wl12xx_spi_raw_write(struct device *child, int addr,
> +				  void *buf, size_t len, bool fixed)
>  {
>  	struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
> -	/* SPI write buffers - 2 for each chunk */
> -	struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS];
> +	struct spi_transfer *t;
>  	struct spi_message m;
>  	u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; /* 1 command per
> chunk */
>  	u32 *cmd;
>  	u32 chunk_len;
>  	int i;
> +	struct spi_device *spi = (struct spi_device *)glue->dev;
> +	struct spi_master *master = spi->master;
> +
> +	if (!master->cs_gpios) {
> +		dev_err(child->parent,
> +			"spi chip select pin missing in platform data!\n");
> +		return -EINVAL;
> +	}
> +
> +	/* SPI write buffers - 2 for each chunk */
> +	t = kzalloc(sizeof(*t) * 2 * WSPI_MAX_NUM_OF_CHUNKS,
> GFP_KERNEL);
> +	if (!t) {
> +		dev_err(child->parent,
> +			"could not allocate spi write buffer\n");
> +		return -ENOMEM;
> +	}
> +
> +	/* Drive CS line low */
> +	gpio_direction_output(master->cs_gpios[0], 0);
> 
>  	WARN_ON(len > SPI_AGGR_BUFFER_SIZE);
> 
>  	spi_message_init(&m);
> -	memset(t, 0, sizeof(t));
> 
>  	cmd = &commands[0];
>  	i = 0;
> @@ -318,9 +393,29 @@ static int __must_check
> wl12xx_spi_raw_write(struct device *child, int addr,
> 
>  	spi_sync(to_spi_device(glue->dev), &m);
> 
> +	/* Drive CS line high */
> +	gpio_direction_output(master->cs_gpios[0], 1);
> +
> +	kfree(t);
>  	return 0;
>  }
> 
> +static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
> +					     void *buf, size_t len, bool fixed) {
> +	int ret;
> +
> +	/* The ELP wakeup write may fail the first time due to internal
> +	 * hardware latency. It is safer to send the wakeup command twice to
> +	 * avoid unexpected failures.
> +	 */
> +	if (addr == HW_ACCESS_ELP_CTRL_REG)
> +		ret = __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
> +	ret = __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
> +
> +	return ret;
> +}
> +
>  /**
>   * wl12xx_spi_set_power - power on/off the wl12xx unit
>   * @child: wl12xx device handle.
> @@ -349,17 +444,38 @@ static int wl12xx_spi_set_power(struct device
> *child, bool enable)
>  	return ret;
>  }
> 
> +/**
> + * wl12xx_spi_set_block_size
> + *
> + * This function is not needed for spi mode, but need to be present.
> + * Without it defined the wlcore fallback to use the wrong packet
> + * allignment on tx.
> + */
> +static void wl12xx_spi_set_block_size(struct device *child,
> +				      unsigned int blksz)
> +{
> +}
> +
>  static struct wl1271_if_operations spi_ops = {
>  	.read		= wl12xx_spi_raw_read,
>  	.write		= wl12xx_spi_raw_write,
>  	.reset		= wl12xx_spi_reset,
>  	.init		= wl12xx_spi_init,
>  	.power		= wl12xx_spi_set_power,
> -	.set_block_size = NULL,
> +	.set_block_size = wl12xx_spi_set_block_size,
>  };
> 
>  static const struct of_device_id wlcore_spi_of_match_table[] = {
> -	{ .compatible = "ti,wl1271" },
> +	{ .compatible = "ti,wl1271", .data = &wl12xx_data},
> +	{ .compatible = "ti,wl1273", .data = &wl12xx_data},
> +	{ .compatible = "ti,wl1281", .data = &wl12xx_data},
> +	{ .compatible = "ti,wl1283", .data = &wl12xx_data},
> +	{ .compatible = "ti,wl1801", .data = &wl18xx_data},
> +	{ .compatible = "ti,wl1805", .data = &wl18xx_data},
> +	{ .compatible = "ti,wl1807", .data = &wl18xx_data},
> +	{ .compatible = "ti,wl1831", .data = &wl18xx_data},
> +	{ .compatible = "ti,wl1835", .data = &wl18xx_data},
> +	{ .compatible = "ti,wl1837", .data = &wl18xx_data},
>  	{ }
>  };
>  MODULE_DEVICE_TABLE(of, wlcore_spi_of_match_table); @@ -375,18
> +491,24 @@ static int wlcore_probe_of(struct spi_device *spi, struct
> wl12xx_spi_glue *glue,
>  			   struct wlcore_platdev_data *pdev_data)  {
>  	struct device_node *dt_node = spi->dev.of_node;
> -	int ret;
> +	const struct of_device_id *of_id;
> +
> +	of_id = of_match_node(wlcore_spi_of_match_table, dt_node);
> +	if (!of_id)
> +		return -ENODEV;
> +
> +	wilink_data = of_id->data;
> +	dev_info(&spi->dev, "selected chip familiy is %s\n",
> +		 wilink_data->name);
> 
>  	if (of_find_property(dt_node, "clock-xtal", NULL))
>  		pdev_data->ref_clock_xtal = true;
> 
> -	ret = of_property_read_u32(dt_node, "ref-clock-frequency",
> -				   &pdev_data->ref_clock_freq);
> -	if (IS_ERR_VALUE(ret)) {
> -		dev_err(glue->dev,
> -			"can't get reference clock frequency (%d)\n", ret);
> -		return ret;
> -	}
> +	/* optional clock frequency params */
> +	of_property_read_u32(dt_node, "ref-clock-frequency",
> +			     &pdev_data->ref_clock_freq);
> +	of_property_read_u32(dt_node, "tcxo-clock-frequency",
> +			     &pdev_data->tcxo_clock_freq);
> 
>  	return 0;
>  }
> @@ -397,6 +519,7 @@ static int wl1271_probe(struct spi_device *spi)
>  	struct wlcore_platdev_data pdev_data;
>  	struct resource res[1];
>  	int ret;
> +	struct spi_master *master = spi->master;
> 
>  	memset(&pdev_data, 0x00, sizeof(pdev_data));
> 
> @@ -410,6 +533,12 @@ static int wl1271_probe(struct spi_device *spi)
> 
>  	glue->dev = &spi->dev;
> 
> +	if (!master->cs_gpios) {
> +		dev_err(glue->dev,
> +			"spi chip select pin missing in platform data!\n");
> +		return -EINVAL;
> +	}
> +
>  	spi_set_drvdata(spi, glue);
> 
>  	/* This is the only SPI value that we need to set here, the rest @@ -
> 431,15 +560,21 @@ static int wl1271_probe(struct spi_device *spi)
>  		return ret;
>  	}
> 
> +	if (gpio_request(master->cs_gpios[0], "spi1-cs0"))
> +		return -EINVAL;
> +
>  	ret = spi_setup(spi);
>  	if (ret < 0) {
>  		dev_err(glue->dev, "spi_setup failed\n");
> +		gpio_free(master->cs_gpios[0]);
>  		return ret;
>  	}
> 
> -	glue->core = platform_device_alloc("wl12xx",
> PLATFORM_DEVID_AUTO);
> +	glue->core = platform_device_alloc(wilink_data->name,
> +					   PLATFORM_DEVID_AUTO);
>  	if (!glue->core) {
>  		dev_err(glue->dev, "can't allocate platform_device\n");
> +		gpio_free(master->cs_gpios[0]);
>  		return -ENOMEM;
>  	}
> 
> @@ -474,14 +609,17 @@ static int wl1271_probe(struct spi_device *spi)
> 
>  out_dev_put:
>  	platform_device_put(glue->core);
> +	gpio_free(master->cs_gpios[0]);
>  	return ret;
>  }
> 
>  static int wl1271_remove(struct spi_device *spi)  {
>  	struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
> +	struct spi_master *master = spi->master;
> 
>  	platform_device_unregister(glue->core);
> +	gpio_free(master->cs_gpios[0]);
> 
>  	return 0;
>  }
> --
> 1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Kalle Valo April 7, 2016, 12:16 p.m. UTC | #2
"Reizer, Eyal" <eyalr@ti.com> writes:

> Ping on this patch
>
>> -----Original Message-----
>> From: Eyal Reizer [mailto:eyalreizer@gmail.com]
>> Sent: Wednesday, March 30, 2016 4:07 PM
>> To: kvalo@codeaurora.org; linux-wireless@vger.kernel.org;
>> netdev@vger.kernel.org; linux-kernel@vger.kernel.org
>> Cc: Reizer, Eyal
>> Subject: [PATCH] wlcore: spi: add wl18xx support

Please edit your quotes and don't top most. A oneliner and then followed
by almost 400 lines unnecessary text for example makes it harder to use
patchwork:

https://patchwork.kernel.org/patch/8696181/
Kalle Valo April 7, 2016, 12:24 p.m. UTC | #3
Eyal Reizer <eyalreizer@gmail.com> writes:

> Add support for using with both wl12xx and wl18xx.
>
> - all wilink family needs special init command for entering wspi mode.
>   extra clock cycles should be sent after the spi init command while the
>   cs pin is high.
> - switch to controling the cs pin from the spi driver for achieveing the
>   above.
> - the selected cs gpio is read from the spi device-tree node using the
>   cs-gpios field and setup as a gpio.
> - See the example below for specifying the cs gpio using the cs-gpios entry
>
> &spi0	{
>  	status = "okay";
> 	pinctrl-names = "default";
> 	pinctrl-0 = <&spi0_pins>;
> 	cs-gpios = <&gpio0 5 0>;
> 	#address-cells = <1>;
> 	#size-cells = <0>;
>  	wlcore: wlcore@0 {
> 		compatible = "ti,wl1835";
> 		vwlan-supply = <&wlan_en_reg>;
> 		spi-max-frequency = <48000000>;
> 		reg = <0>;      /* chip select 0 on spi0, ie spi0.0 */
> 		interrupt-parent = <&gpio0>;
> 		interrupts = <27 IRQ_TYPE_EDGE_RISING>;
>  	};
> };
>
> Signed-off-by: Eyal Reizer <eyalr@ti.com>

[...]

>  static const struct of_device_id wlcore_spi_of_match_table[] = {
> -	{ .compatible = "ti,wl1271" },
> +	{ .compatible = "ti,wl1271", .data = &wl12xx_data},
> +	{ .compatible = "ti,wl1273", .data = &wl12xx_data},
> +	{ .compatible = "ti,wl1281", .data = &wl12xx_data},
> +	{ .compatible = "ti,wl1283", .data = &wl12xx_data},
> +	{ .compatible = "ti,wl1801", .data = &wl18xx_data},
> +	{ .compatible = "ti,wl1805", .data = &wl18xx_data},
> +	{ .compatible = "ti,wl1807", .data = &wl18xx_data},
> +	{ .compatible = "ti,wl1831", .data = &wl18xx_data},
> +	{ .compatible = "ti,wl1835", .data = &wl18xx_data},
> +	{ .compatible = "ti,wl1837", .data = &wl18xx_data},
>  	{ }

Shouldn't you also update bindings/net/wireless/ti,wlcore,spi.txt? Now
it only mentions about ti,wl1271 and not anything about the rest.

Adding devicetree list for further comments.
Reizer, Eyal April 7, 2016, 12:45 p.m. UTC | #4
> -----Original Message-----
> From: Kalle Valo [mailto:kvalo@codeaurora.org]
> Sent: Thursday, April 07, 2016 3:25 PM
> To: Eyal Reizer
> Cc: linux-wireless@vger.kernel.org; netdev@vger.kernel.org; linux-
> kernel@vger.kernel.org; Reizer, Eyal; devicetree@vger.kernel.org
> Subject: Re: [PATCH] wlcore: spi: add wl18xx support
> 
> Eyal Reizer <eyalreizer@gmail.com> writes:
> 
> > Add support for using with both wl12xx and wl18xx.
> >
> > - all wilink family needs special init command for entering wspi mode.
> >   extra clock cycles should be sent after the spi init command while the
> >   cs pin is high.
> > - switch to controling the cs pin from the spi driver for achieveing the
> >   above.
> > - the selected cs gpio is read from the spi device-tree node using the
> >   cs-gpios field and setup as a gpio.
> > - See the example below for specifying the cs gpio using the cs-gpios
> > entry
> >
> > &spi0	{
> >  	status = "okay";
> > 	pinctrl-names = "default";
> > 	pinctrl-0 = <&spi0_pins>;
> > 	cs-gpios = <&gpio0 5 0>;
> > 	#address-cells = <1>;
> > 	#size-cells = <0>;
> >  	wlcore: wlcore@0 {
> > 		compatible = "ti,wl1835";
> > 		vwlan-supply = <&wlan_en_reg>;
> > 		spi-max-frequency = <48000000>;
> > 		reg = <0>;      /* chip select 0 on spi0, ie spi0.0 */
> > 		interrupt-parent = <&gpio0>;
> > 		interrupts = <27 IRQ_TYPE_EDGE_RISING>;
> >  	};
> > };
> >
> > Signed-off-by: Eyal Reizer <eyalr@ti.com>
> 
> [...]
> 
> >  static const struct of_device_id wlcore_spi_of_match_table[] = {
> > -	{ .compatible = "ti,wl1271" },
> > +	{ .compatible = "ti,wl1271", .data = &wl12xx_data},
> > +	{ .compatible = "ti,wl1273", .data = &wl12xx_data},
> > +	{ .compatible = "ti,wl1281", .data = &wl12xx_data},
> > +	{ .compatible = "ti,wl1283", .data = &wl12xx_data},
> > +	{ .compatible = "ti,wl1801", .data = &wl18xx_data},
> > +	{ .compatible = "ti,wl1805", .data = &wl18xx_data},
> > +	{ .compatible = "ti,wl1807", .data = &wl18xx_data},
> > +	{ .compatible = "ti,wl1831", .data = &wl18xx_data},
> > +	{ .compatible = "ti,wl1835", .data = &wl18xx_data},
> > +	{ .compatible = "ti,wl1837", .data = &wl18xx_data},
> >  	{ }
> 
> Shouldn't you also update bindings/net/wireless/ti,wlcore,spi.txt? Now it only
> mentions about ti,wl1271 and not anything about the rest.

You are right! 
Will be fixed in v2

> 
> Adding devicetree list for further comments.
> 
> --
> Kalle Valo

Best Regards,
Eyal
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Kalle Valo April 7, 2016, 2:32 p.m. UTC | #5
"Reizer, Eyal" <eyalr@ti.com> writes:

>> >  static const struct of_device_id wlcore_spi_of_match_table[] = {
>> > -	{ .compatible = "ti,wl1271" },
>> > +	{ .compatible = "ti,wl1271", .data = &wl12xx_data},
>> > +	{ .compatible = "ti,wl1273", .data = &wl12xx_data},
>> > +	{ .compatible = "ti,wl1281", .data = &wl12xx_data},
>> > +	{ .compatible = "ti,wl1283", .data = &wl12xx_data},
>> > +	{ .compatible = "ti,wl1801", .data = &wl18xx_data},
>> > +	{ .compatible = "ti,wl1805", .data = &wl18xx_data},
>> > +	{ .compatible = "ti,wl1807", .data = &wl18xx_data},
>> > +	{ .compatible = "ti,wl1831", .data = &wl18xx_data},
>> > +	{ .compatible = "ti,wl1835", .data = &wl18xx_data},
>> > +	{ .compatible = "ti,wl1837", .data = &wl18xx_data},
>> >  	{ }
>> 
>> Shouldn't you also update bindings/net/wireless/ti,wlcore,spi.txt? Now it only
>> mentions about ti,wl1271 and not anything about the rest.
>
> You are right! Will be fixed in v2

Thanks. Also remember to CC devicetree list.
diff mbox

Patch

diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c
index 96d9c9d..6c5a369 100644
--- a/drivers/net/wireless/ti/wlcore/spi.c
+++ b/drivers/net/wireless/ti/wlcore/spi.c
@@ -32,6 +32,7 @@ 
 #include <linux/platform_device.h>
 #include <linux/of_irq.h>
 #include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
 
 #include "wlcore.h"
 #include "wl12xx_80211.h"
@@ -70,16 +71,30 @@ 
 #define WSPI_MAX_CHUNK_SIZE    4092
 
 /*
- * only support SPI for 12xx - this code should be reworked when 18xx
- * support is introduced
+ * wl18xx driver aggregation buffer size is (13 * PAGE_SIZE) compared to
+ * (4 * PAGE_SIZE) for wl12xx, so use the larger buffer needed for wl18xx
  */
-#define SPI_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
+#define SPI_AGGR_BUFFER_SIZE (13 * PAGE_SIZE)
 
 /* Maximum number of SPI write chunks */
 #define WSPI_MAX_NUM_OF_CHUNKS \
 	((SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) + 1)
 
 
+struct wilink_familiy_data {
+	char name[8];
+};
+
+const struct wilink_familiy_data *wilink_data;
+
+static const struct wilink_familiy_data wl18xx_data = {
+	.name = "wl18xx",
+};
+
+static const struct wilink_familiy_data wl12xx_data = {
+	.name = "wl12xx",
+};
+
 struct wl12xx_spi_glue {
 	struct device *dev;
 	struct platform_device *core;
@@ -120,6 +135,8 @@  static void wl12xx_spi_init(struct device *child)
 	struct spi_transfer t;
 	struct spi_message m;
 	u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
+	struct spi_device *spi = (struct spi_device *)glue->dev;
+	struct spi_master *master = spi->master;
 
 	if (!cmd) {
 		dev_err(child->parent,
@@ -127,6 +144,15 @@  static void wl12xx_spi_init(struct device *child)
 		return;
 	}
 
+	if (!master->cs_gpios) {
+		dev_err(child->parent,
+			"spi chip select pin missing in platform data!\n");
+		return;
+	}
+
+	/* Drive CS line low */
+	gpio_direction_output(master->cs_gpios[0], 0);
+
 	memset(&t, 0, sizeof(t));
 	spi_message_init(&m);
 
@@ -163,6 +189,26 @@  static void wl12xx_spi_init(struct device *child)
 	spi_message_add_tail(&t, &m);
 
 	spi_sync(to_spi_device(glue->dev), &m);
+
+	/* Send extra clocks with CS high. this is required by the wilink
+	 * family in order for successfully enter WSPI mode
+	 */
+	gpio_direction_output(master->cs_gpios[0], 1);
+
+	memset(&m, 0, sizeof(m));
+	spi_message_init(&m);
+
+	cmd[0] = 0xff;
+	cmd[1] = 0xff;
+	cmd[2] = 0xff;
+	cmd[3] = 0xff;
+	swab32s((u32 *)cmd);
+
+	t.tx_buf = cmd;
+	t.len = 4;
+	spi_message_add_tail(&t, &m);
+	spi_sync(to_spi_device(glue->dev), &m);
+
 	kfree(cmd);
 }
 
@@ -213,6 +259,16 @@  static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
 	u32 *busy_buf;
 	u32 *cmd;
 	u32 chunk_len;
+	struct spi_device *spi = (struct spi_device *)glue->dev;
+	struct spi_master *master = spi->master;
+
+	if (!master->cs_gpios) {
+		dev_err(child->parent,
+			"spi chip select pin missing in platform data!\n");
+		return -EINVAL;
+	}
+	/* Drive CS line low */
+	gpio_direction_output(master->cs_gpios[0], 0);
 
 	while (len > 0) {
 		chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len);
@@ -267,25 +323,44 @@  static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
 		len -= chunk_len;
 	}
 
+	/* Drive CS line high */
+	gpio_direction_output(master->cs_gpios[0], 1);
 	return 0;
 }
 
-static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
-					     void *buf, size_t len, bool fixed)
+static int __wl12xx_spi_raw_write(struct device *child, int addr,
+				  void *buf, size_t len, bool fixed)
 {
 	struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
-	/* SPI write buffers - 2 for each chunk */
-	struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS];
+	struct spi_transfer *t;
 	struct spi_message m;
 	u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; /* 1 command per chunk */
 	u32 *cmd;
 	u32 chunk_len;
 	int i;
+	struct spi_device *spi = (struct spi_device *)glue->dev;
+	struct spi_master *master = spi->master;
+
+	if (!master->cs_gpios) {
+		dev_err(child->parent,
+			"spi chip select pin missing in platform data!\n");
+		return -EINVAL;
+	}
+
+	/* SPI write buffers - 2 for each chunk */
+	t = kzalloc(sizeof(*t) * 2 * WSPI_MAX_NUM_OF_CHUNKS, GFP_KERNEL);
+	if (!t) {
+		dev_err(child->parent,
+			"could not allocate spi write buffer\n");
+		return -ENOMEM;
+	}
+
+	/* Drive CS line low */
+	gpio_direction_output(master->cs_gpios[0], 0);
 
 	WARN_ON(len > SPI_AGGR_BUFFER_SIZE);
 
 	spi_message_init(&m);
-	memset(t, 0, sizeof(t));
 
 	cmd = &commands[0];
 	i = 0;
@@ -318,9 +393,29 @@  static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
 
 	spi_sync(to_spi_device(glue->dev), &m);
 
+	/* Drive CS line high */
+	gpio_direction_output(master->cs_gpios[0], 1);
+
+	kfree(t);
 	return 0;
 }
 
+static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
+					     void *buf, size_t len, bool fixed)
+{
+	int ret;
+
+	/* The ELP wakeup write may fail the first time due to internal
+	 * hardware latency. It is safer to send the wakeup command twice to
+	 * avoid unexpected failures.
+	 */
+	if (addr == HW_ACCESS_ELP_CTRL_REG)
+		ret = __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
+	ret = __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
+
+	return ret;
+}
+
 /**
  * wl12xx_spi_set_power - power on/off the wl12xx unit
  * @child: wl12xx device handle.
@@ -349,17 +444,38 @@  static int wl12xx_spi_set_power(struct device *child, bool enable)
 	return ret;
 }
 
+/**
+ * wl12xx_spi_set_block_size
+ *
+ * This function is not needed for spi mode, but need to be present.
+ * Without it defined the wlcore fallback to use the wrong packet
+ * allignment on tx.
+ */
+static void wl12xx_spi_set_block_size(struct device *child,
+				      unsigned int blksz)
+{
+}
+
 static struct wl1271_if_operations spi_ops = {
 	.read		= wl12xx_spi_raw_read,
 	.write		= wl12xx_spi_raw_write,
 	.reset		= wl12xx_spi_reset,
 	.init		= wl12xx_spi_init,
 	.power		= wl12xx_spi_set_power,
-	.set_block_size = NULL,
+	.set_block_size = wl12xx_spi_set_block_size,
 };
 
 static const struct of_device_id wlcore_spi_of_match_table[] = {
-	{ .compatible = "ti,wl1271" },
+	{ .compatible = "ti,wl1271", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1273", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1281", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1283", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1801", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1805", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1807", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1831", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1835", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1837", .data = &wl18xx_data},
 	{ }
 };
 MODULE_DEVICE_TABLE(of, wlcore_spi_of_match_table);
@@ -375,18 +491,24 @@  static int wlcore_probe_of(struct spi_device *spi, struct wl12xx_spi_glue *glue,
 			   struct wlcore_platdev_data *pdev_data)
 {
 	struct device_node *dt_node = spi->dev.of_node;
-	int ret;
+	const struct of_device_id *of_id;
+
+	of_id = of_match_node(wlcore_spi_of_match_table, dt_node);
+	if (!of_id)
+		return -ENODEV;
+
+	wilink_data = of_id->data;
+	dev_info(&spi->dev, "selected chip familiy is %s\n",
+		 wilink_data->name);
 
 	if (of_find_property(dt_node, "clock-xtal", NULL))
 		pdev_data->ref_clock_xtal = true;
 
-	ret = of_property_read_u32(dt_node, "ref-clock-frequency",
-				   &pdev_data->ref_clock_freq);
-	if (IS_ERR_VALUE(ret)) {
-		dev_err(glue->dev,
-			"can't get reference clock frequency (%d)\n", ret);
-		return ret;
-	}
+	/* optional clock frequency params */
+	of_property_read_u32(dt_node, "ref-clock-frequency",
+			     &pdev_data->ref_clock_freq);
+	of_property_read_u32(dt_node, "tcxo-clock-frequency",
+			     &pdev_data->tcxo_clock_freq);
 
 	return 0;
 }
@@ -397,6 +519,7 @@  static int wl1271_probe(struct spi_device *spi)
 	struct wlcore_platdev_data pdev_data;
 	struct resource res[1];
 	int ret;
+	struct spi_master *master = spi->master;
 
 	memset(&pdev_data, 0x00, sizeof(pdev_data));
 
@@ -410,6 +533,12 @@  static int wl1271_probe(struct spi_device *spi)
 
 	glue->dev = &spi->dev;
 
+	if (!master->cs_gpios) {
+		dev_err(glue->dev,
+			"spi chip select pin missing in platform data!\n");
+		return -EINVAL;
+	}
+
 	spi_set_drvdata(spi, glue);
 
 	/* This is the only SPI value that we need to set here, the rest
@@ -431,15 +560,21 @@  static int wl1271_probe(struct spi_device *spi)
 		return ret;
 	}
 
+	if (gpio_request(master->cs_gpios[0], "spi1-cs0"))
+		return -EINVAL;
+
 	ret = spi_setup(spi);
 	if (ret < 0) {
 		dev_err(glue->dev, "spi_setup failed\n");
+		gpio_free(master->cs_gpios[0]);
 		return ret;
 	}
 
-	glue->core = platform_device_alloc("wl12xx", PLATFORM_DEVID_AUTO);
+	glue->core = platform_device_alloc(wilink_data->name,
+					   PLATFORM_DEVID_AUTO);
 	if (!glue->core) {
 		dev_err(glue->dev, "can't allocate platform_device\n");
+		gpio_free(master->cs_gpios[0]);
 		return -ENOMEM;
 	}
 
@@ -474,14 +609,17 @@  static int wl1271_probe(struct spi_device *spi)
 
 out_dev_put:
 	platform_device_put(glue->core);
+	gpio_free(master->cs_gpios[0]);
 	return ret;
 }
 
 static int wl1271_remove(struct spi_device *spi)
 {
 	struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
+	struct spi_master *master = spi->master;
 
 	platform_device_unregister(glue->core);
+	gpio_free(master->cs_gpios[0]);
 
 	return 0;
 }