diff mbox series

[v4,2/4] Bluetooth: ti-st: Add GNSS subdevice for TI Wilink chips

Message ID 20240606183032.684481-3-andreas@kemnade.info
State New
Headers show
Series Bluetooth/gnss: GNSS support for TiWi chips | expand

Commit Message

Andreas Kemnade June 6, 2024, 6:30 p.m. UTC
Some of these chips have GNSS support. GNSS support is available through
channel 9 whilst FM is through channel 8. Add a platform subdevice for
GNSS so that a driver for that functionality can be build. To avoid having
useless GNSS devices, do it only when the devicetree node name contains
gnss.

Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
---
 drivers/bluetooth/hci_ll.c   | 81 ++++++++++++++++++++++++++++++++++++
 include/linux/ti_wilink_st.h |  8 ++++
 2 files changed, 89 insertions(+)

Comments

Johan Hovold Jan. 14, 2025, 12:14 p.m. UTC | #1
On Thu, Jun 06, 2024 at 08:30:30PM +0200, Andreas Kemnade wrote:
> Some of these chips have GNSS support.

Please be more specific here.

> GNSS support is available through
> channel 9 whilst FM is through channel 8. Add a platform subdevice for
> GNSS so that a driver for that functionality can be build.

> To avoid having
> useless GNSS devices, do it only when the devicetree node name contains
> gnss.

That's seems like an unorthodox use of device tree. These devices are
primarily (WiFi and) Bluetooth controllers so should probably not have
gone about and updated the node names to 'bluetooth-gnss' as you did,
for example, here:

	https://lore.kernel.org/all/20231127200430.143231-1-andreas@kemnade.info/

and instead have used a boolean property for the optional subfunctions
like GNSS, FM radio and NFC (as Rob suggested in one of the earlier
attempts by others).
 
> Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
> Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
> ---
>  drivers/bluetooth/hci_ll.c   | 81 ++++++++++++++++++++++++++++++++++++
>  include/linux/ti_wilink_st.h |  8 ++++
>  2 files changed, 89 insertions(+)
> 
> diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
> index 4a0b5c3160c2b..09e5a4dbd2f8c 100644
> --- a/drivers/bluetooth/hci_ll.c
> +++ b/drivers/bluetooth/hci_ll.c
> @@ -32,6 +32,7 @@
>  #include <linux/signal.h>
>  #include <linux/ioctl.h>
>  #include <linux/of.h>
> +#include <linux/platform_device.h>
>  #include <linux/serdev.h>
>  #include <linux/skbuff.h>
>  #include <linux/ti_wilink_st.h>
> @@ -68,6 +69,9 @@ struct ll_device {
>  	struct gpio_desc *enable_gpio;
>  	struct clk *ext_clk;
>  	bdaddr_t bdaddr;
> +
> +	void (*gnss_recv_func)(struct device *dev, struct sk_buff *skb);
> +	struct platform_device *gnssdev;
>  };
>  
>  struct ll_struct {
> @@ -78,6 +82,8 @@ struct ll_struct {
>  	struct sk_buff_head tx_wait_q;	/* HCILL wait queue	*/
>  };
>  
> +static int ll_gnss_register(struct ll_device *lldev);
> +static int ll_gnss_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
>  /*
>   * Builds and sends an HCILL command packet.
>   * These are very simple packets with only 1 cmd byte
> @@ -411,6 +417,13 @@ static int ll_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
>  	.lsize = 0, \
>  	.maxlen = 0
>  
> +#define LL_RECV_GNSS \
> +	.type = 9, \
> +	.hlen = 3, \
> +	.loff = 1, \
> +	.lsize = 2
> +
> +
>  static const struct h4_recv_pkt ll_recv_pkts[] = {
>  	{ H4_RECV_ACL,       .recv = hci_recv_frame },
>  	{ H4_RECV_SCO,       .recv = hci_recv_frame },
> @@ -419,6 +432,7 @@ static const struct h4_recv_pkt ll_recv_pkts[] = {
>  	{ LL_RECV_SLEEP_ACK, .recv = ll_recv_frame  },
>  	{ LL_RECV_WAKE_IND,  .recv = ll_recv_frame  },
>  	{ LL_RECV_WAKE_ACK,  .recv = ll_recv_frame  },
> +	{ LL_RECV_GNSS,      .recv = ll_gnss_recv_frame },
>  };
>  
>  /* Recv data */
> @@ -677,9 +691,69 @@ static int ll_setup(struct hci_uart *hu)
>  		}
>  	}
>  
> +	if (strstr(of_node_full_name(serdev->dev.of_node), "gnss"))
> +		ll_gnss_register(lldev);

So this should be based on a boolean property, not the node name.

> +
> +	return 0;
> +}
> +
> +struct hci_dev *st_get_hci(struct device *dev)
> +{
> +	struct ll_device *lldev = dev_get_drvdata(dev);
> +
> +	return lldev->hu.hdev;
> +}
> +EXPORT_SYMBOL(st_get_hci);

And this seems like a too raw abstraction.

Unless you can follow through with Marcel's idea of exposing these
vendor channels in Bluetooth core, I think you should use something more
driver specific here (i.e. an interface for sending data rather than
exposing the entire Bluetooth device).

> +void st_set_gnss_recv_func(struct device *dev,
> +			   void (*recv_frame)(struct device *, struct sk_buff *))
> +{
> +	struct ll_device *lldev = dev_get_drvdata(dev);
> +
> +	lldev->gnss_recv_func = recv_frame;
> +}
> +EXPORT_SYMBOL(st_set_gnss_recv_func);
> +
> +static int ll_gnss_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
> +{
> +	struct hci_uart *hu = hci_get_drvdata(hdev);
> +	struct ll_device *lldev = container_of(hu, struct ll_device, hu);
> +
> +	if (!lldev->gnssdev)
> +		return 0;
> +
> +	if (lldev->gnss_recv_func) {
> +		lldev->gnss_recv_func(&lldev->gnssdev->dev, skb);
> +		return 0;
> +	}
> +	kfree_skb(skb);
> +
>  	return 0;
>  }

Please also consider that these devices also support other subfunctions
and that any abstraction should allow for the driver to be extended with
support for things like FM radio (and NFC).

> +static int ll_gnss_register(struct ll_device *lldev)
> +{
> +	struct platform_device *pdev;
> +	int ret;
> +
> +	pdev = platform_device_alloc("ti-ai2-gnss", PLATFORM_DEVID_AUTO);
> +	if (!pdev)
> +		return -ENOMEM;

I believe believe you should be using the auxiliary device abstraction
for subfunctions.

> +
> +	pdev->dev.parent = &lldev->serdev->dev;
> +	lldev->gnssdev = pdev;
> +	ret = platform_device_add(pdev);
> +	if (ret)
> +		goto err;
> +
> +	return 0;
> +
> +err:
> +	lldev->gnssdev = NULL;
> +	platform_device_put(pdev);
> +	return ret;
> +}
> +
>  static const struct hci_uart_proto llp;
>  
>  static int hci_ti_probe(struct serdev_device *serdev)
> @@ -757,12 +831,19 @@ static int hci_ti_probe(struct serdev_device *serdev)
>  	}
>  
>  	return hci_uart_register_device(hu, &llp);
> +
> +
> +	return 0;

This change makes no sense.

>  }
>  
> +

Stray newline.

>  static void hci_ti_remove(struct serdev_device *serdev)
>  {
>  	struct ll_device *lldev = serdev_device_get_drvdata(serdev);
>  
> +	if (lldev->gnssdev)
> +		platform_device_unregister(lldev->gnssdev);
> +
>  	hci_uart_unregister_device(&lldev->hu);
>  }
>  
> diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h
> index 10642d4844f0c..eccc2db004069 100644
> --- a/include/linux/ti_wilink_st.h
> +++ b/include/linux/ti_wilink_st.h
> @@ -381,6 +381,14 @@ unsigned long st_ll_getstate(struct st_data_s *);
>  unsigned long st_ll_sleep_state(struct st_data_s *, unsigned char);
>  void st_ll_wakeup(struct st_data_s *);
>  
> +/**

This is not kernel doc.

> + * various funcs used to interact between FM, GPS and BT
> + */
> +struct hci_dev *st_get_hci(struct device *dev);
> +void st_set_gnss_recv_func(struct device *dev,
> +			   void (*recv_frame)(struct device *, struct sk_buff *));
> +
> +
>  /*
>   * header information used by st_core.c for FM and GPS
>   * packet parsing, the bluetooth headers are already available

Johan
Andreas Kemnade Jan. 14, 2025, 1:05 p.m. UTC | #2
Am Tue, 14 Jan 2025 13:14:45 +0100
schrieb Johan Hovold <johan@kernel.org>:

> > GNSS support is available through
> > channel 9 whilst FM is through channel 8. Add a platform subdevice for
> > GNSS so that a driver for that functionality can be build.  
> 
> > To avoid having
> > useless GNSS devices, do it only when the devicetree node name contains
> > gnss.  
> 
> That's seems like an unorthodox use of device tree. These devices are
> primarily (WiFi and) Bluetooth controllers so should probably not have
> gone about and updated the node names to 'bluetooth-gnss' as you did,
> for example, here:

yes, the matching of the node name is a bit unorthodox. How do you
define primary? The old design with ti-st driver and bluetooth and
other functions on top does not look like anything primary. If you look
at the current situation with the GNSS stuff sitting on to of
bluetooth, the picture is different, but that is implementation. As the
devicetree is describing hardware, having the nodenames describe things
seems like the right way to do.
But I agree with you that the driver should not care about the node
name, but use a boolean property.

Regards,
Andreas
Johan Hovold Jan. 14, 2025, 3:26 p.m. UTC | #3
On Tue, Jan 14, 2025 at 02:05:25PM +0100, Andreas Kemnade wrote:
> Am Tue, 14 Jan 2025 13:14:45 +0100
> schrieb Johan Hovold <johan@kernel.org>:
> 
> > > GNSS support is available through
> > > channel 9 whilst FM is through channel 8. Add a platform subdevice for
> > > GNSS so that a driver for that functionality can be build.  
> > 
> > > To avoid having
> > > useless GNSS devices, do it only when the devicetree node name contains
> > > gnss.  
> > 
> > That's seems like an unorthodox use of device tree. These devices are
> > primarily (WiFi and) Bluetooth controllers so should probably not have
> > gone about and updated the node names to 'bluetooth-gnss' as you did,
> > for example, here:
> 
> yes, the matching of the node name is a bit unorthodox. How do you
> define primary? The old design with ti-st driver and bluetooth and
> other functions on top does not look like anything primary. If you look
> at the current situation with the GNSS stuff sitting on to of
> bluetooth, the picture is different, but that is implementation.

I call it primary based on (my understanding of) the architecture,
protocol and chip family. It look to me like the FM, GPS and NFC
functionality is bolted on top of the Bluetooth one for which a protocol
already existed (and which is also used by standalone non-wilink
Bluetooth controllers).

> As the
> devicetree is describing hardware, having the nodenames describe things
> seems like the right way to do.

We have serial controllers which also implements a gpio controller, but
no one would expect those to be named 'serial-gpio' for example. But we
can still describe that with a 'gpio-controller' property (and the
compatible string indicates what the device is capable of).

Since these chips do not come without Bluetooth (e.g. only wifi + gps)
and the Bluetooth protocol is used to access the GPS function I think
it makes perfect sense to just leave them named 'bluetooth'. That also
avoids having to add all the permutations of

	bluetooth-gnss-fmradio-nfc

etc. (and having to name them such also when there is no gnss antenna
connected).

> But I agree with you that the driver should not care about the node
> name, but use a boolean property.

Johan
Andreas Kemnade Jan. 14, 2025, 4:26 p.m. UTC | #4
Am Tue, 14 Jan 2025 16:26:55 +0100
schrieb Johan Hovold <johan@kernel.org>:

> On Tue, Jan 14, 2025 at 02:05:25PM +0100, Andreas Kemnade wrote:
> > Am Tue, 14 Jan 2025 13:14:45 +0100
> > schrieb Johan Hovold <johan@kernel.org>:
> >   
> > > > GNSS support is available through
> > > > channel 9 whilst FM is through channel 8. Add a platform subdevice for
> > > > GNSS so that a driver for that functionality can be build.    
> > >   
> > > > To avoid having
> > > > useless GNSS devices, do it only when the devicetree node name contains
> > > > gnss.    
> > > 
> > > That's seems like an unorthodox use of device tree. These devices are
> > > primarily (WiFi and) Bluetooth controllers so should probably not have
> > > gone about and updated the node names to 'bluetooth-gnss' as you did,
> > > for example, here:  
> > 
> > yes, the matching of the node name is a bit unorthodox. How do you
> > define primary? The old design with ti-st driver and bluetooth and
> > other functions on top does not look like anything primary. If you look
> > at the current situation with the GNSS stuff sitting on to of
> > bluetooth, the picture is different, but that is implementation.  
> 
> I call it primary based on (my understanding of) the architecture,
> protocol and chip family. It look to me like the FM, GPS and NFC
> functionality is bolted on top of the Bluetooth one for which a protocol
> already existed (and which is also used by standalone non-wilink
> Bluetooth controllers).
>
Well, it is a bit of a definition thing. The ti-st (the older driver)
stands for shared transport, so sharing multiple things over the same
line, by dispatching the TLV structures accordingly to the relevant
protocol drivers and at that point you say that some type values are
Bluetooth, some are GNSS, some are generic power management + fw
download.
AFAIK using any approach based on the now-removed ti-st driver, no
bluetooth would be needed in kernel for using GPS. How could something
be called
"bolted on top of Bluetooth" if Bluetooth is not required.

But then there are devices with some type values which provide
functionality related to Bluetooth. For those the h4_recv_pkt
infrastructure is there. And this happens to be also useable for the
jobs done by the ti-st driver. So it could be obsoleted and
now removed. Probably this h4_recv_pkt stuff could also be used for
other non-bluetooth-related stuff.

So it is a definition thing if either say regarding that TLV structure:
- everything is Bluetooth
- some type values are some Bluetooth vendor channels.
- these vendor channels include GNSS/FM/whatever
or 
- some type values are Bluetooth
- some other are GNSS/FM/whatever
- some are some kind of generic management (firmware download, power
  management).

The further is what is reflected by the hci_ll approach, the latter is
what is used by the ti-st approach.

It is a bit of a headache to forcefully draw in Bluetooth stuff, but as
I am using Bluetooth anyway for keyboards, I am not eager to venture
on any revive of the ti-st stuff to avoid that Bluetooth dependency 

Regards,
Andreas
diff mbox series

Patch

diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
index 4a0b5c3160c2b..09e5a4dbd2f8c 100644
--- a/drivers/bluetooth/hci_ll.c
+++ b/drivers/bluetooth/hci_ll.c
@@ -32,6 +32,7 @@ 
 #include <linux/signal.h>
 #include <linux/ioctl.h>
 #include <linux/of.h>
+#include <linux/platform_device.h>
 #include <linux/serdev.h>
 #include <linux/skbuff.h>
 #include <linux/ti_wilink_st.h>
@@ -68,6 +69,9 @@  struct ll_device {
 	struct gpio_desc *enable_gpio;
 	struct clk *ext_clk;
 	bdaddr_t bdaddr;
+
+	void (*gnss_recv_func)(struct device *dev, struct sk_buff *skb);
+	struct platform_device *gnssdev;
 };
 
 struct ll_struct {
@@ -78,6 +82,8 @@  struct ll_struct {
 	struct sk_buff_head tx_wait_q;	/* HCILL wait queue	*/
 };
 
+static int ll_gnss_register(struct ll_device *lldev);
+static int ll_gnss_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
 /*
  * Builds and sends an HCILL command packet.
  * These are very simple packets with only 1 cmd byte
@@ -411,6 +417,13 @@  static int ll_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
 	.lsize = 0, \
 	.maxlen = 0
 
+#define LL_RECV_GNSS \
+	.type = 9, \
+	.hlen = 3, \
+	.loff = 1, \
+	.lsize = 2
+
+
 static const struct h4_recv_pkt ll_recv_pkts[] = {
 	{ H4_RECV_ACL,       .recv = hci_recv_frame },
 	{ H4_RECV_SCO,       .recv = hci_recv_frame },
@@ -419,6 +432,7 @@  static const struct h4_recv_pkt ll_recv_pkts[] = {
 	{ LL_RECV_SLEEP_ACK, .recv = ll_recv_frame  },
 	{ LL_RECV_WAKE_IND,  .recv = ll_recv_frame  },
 	{ LL_RECV_WAKE_ACK,  .recv = ll_recv_frame  },
+	{ LL_RECV_GNSS,      .recv = ll_gnss_recv_frame },
 };
 
 /* Recv data */
@@ -677,9 +691,69 @@  static int ll_setup(struct hci_uart *hu)
 		}
 	}
 
+	if (strstr(of_node_full_name(serdev->dev.of_node), "gnss"))
+		ll_gnss_register(lldev);
+
+	return 0;
+}
+
+struct hci_dev *st_get_hci(struct device *dev)
+{
+	struct ll_device *lldev = dev_get_drvdata(dev);
+
+	return lldev->hu.hdev;
+}
+EXPORT_SYMBOL(st_get_hci);
+
+void st_set_gnss_recv_func(struct device *dev,
+			   void (*recv_frame)(struct device *, struct sk_buff *))
+{
+	struct ll_device *lldev = dev_get_drvdata(dev);
+
+	lldev->gnss_recv_func = recv_frame;
+}
+EXPORT_SYMBOL(st_set_gnss_recv_func);
+
+static int ll_gnss_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct hci_uart *hu = hci_get_drvdata(hdev);
+	struct ll_device *lldev = container_of(hu, struct ll_device, hu);
+
+	if (!lldev->gnssdev)
+		return 0;
+
+	if (lldev->gnss_recv_func) {
+		lldev->gnss_recv_func(&lldev->gnssdev->dev, skb);
+		return 0;
+	}
+	kfree_skb(skb);
+
 	return 0;
 }
 
+static int ll_gnss_register(struct ll_device *lldev)
+{
+	struct platform_device *pdev;
+	int ret;
+
+	pdev = platform_device_alloc("ti-ai2-gnss", PLATFORM_DEVID_AUTO);
+	if (!pdev)
+		return -ENOMEM;
+
+	pdev->dev.parent = &lldev->serdev->dev;
+	lldev->gnssdev = pdev;
+	ret = platform_device_add(pdev);
+	if (ret)
+		goto err;
+
+	return 0;
+
+err:
+	lldev->gnssdev = NULL;
+	platform_device_put(pdev);
+	return ret;
+}
+
 static const struct hci_uart_proto llp;
 
 static int hci_ti_probe(struct serdev_device *serdev)
@@ -757,12 +831,19 @@  static int hci_ti_probe(struct serdev_device *serdev)
 	}
 
 	return hci_uart_register_device(hu, &llp);
+
+
+	return 0;
 }
 
+
 static void hci_ti_remove(struct serdev_device *serdev)
 {
 	struct ll_device *lldev = serdev_device_get_drvdata(serdev);
 
+	if (lldev->gnssdev)
+		platform_device_unregister(lldev->gnssdev);
+
 	hci_uart_unregister_device(&lldev->hu);
 }
 
diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h
index 10642d4844f0c..eccc2db004069 100644
--- a/include/linux/ti_wilink_st.h
+++ b/include/linux/ti_wilink_st.h
@@ -381,6 +381,14 @@  unsigned long st_ll_getstate(struct st_data_s *);
 unsigned long st_ll_sleep_state(struct st_data_s *, unsigned char);
 void st_ll_wakeup(struct st_data_s *);
 
+/**
+ * various funcs used to interact between FM, GPS and BT
+ */
+struct hci_dev *st_get_hci(struct device *dev);
+void st_set_gnss_recv_func(struct device *dev,
+			   void (*recv_frame)(struct device *, struct sk_buff *));
+
+
 /*
  * header information used by st_core.c for FM and GPS
  * packet parsing, the bluetooth headers are already available