[openwrt/openwrt] ramips: add Semtech SX9512 touch input driver

LEDE Commits lede-commits at lists.infradead.org
Sun May 11 02:56:09 PDT 2025


blocktrron pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/0842e793381a1e60f8acdcbea0e8c59be1ac9a88

commit 0842e793381a1e60f8acdcbea0e8c59be1ac9a88
Author: David Bauer <mail at david-bauer.net>
AuthorDate: Sun May 4 04:47:32 2025 +0200

    ramips: add Semtech SX9512 touch input driver
    
    This driver is required for the touch-inputs as well as some LEDs on the
    Genexis Pulse EX400.
    
    The driver was also sent upstream. As the EX400 is currently the only
    consumer, the driver is added target-specific for ramips.
    
    Once the driver has been accepted upstream and is provided in a kernel
    release used by OpenWrt, the package should be moved to the global input
    drivers.
    
    Link: https://www.spinics.net/lists/kernel/msg5669349.html
    
    Signed-off-by: David Bauer <mail at david-bauer.net>
---
 target/linux/ramips/modules.mk                     |  18 +
 ...t-sx951x-add-Semtech-SX9512-SX9513-driver.patch | 560 +++++++++++++++++++++
 2 files changed, 578 insertions(+)

diff --git a/target/linux/ramips/modules.mk b/target/linux/ramips/modules.mk
index 175ba94eff..22a9660b0f 100644
--- a/target/linux/ramips/modules.mk
+++ b/target/linux/ramips/modules.mk
@@ -152,3 +152,21 @@ define KernelPackage/sound-mt7620/description
 endef
 
 $(eval $(call KernelPackage,sound-mt7620))
+
+
+define KernelPackage/keyboard-sx951x
+  SUBMENU:=Other modules
+  TITLE:=Semtech SX9512/SX9513
+  DEPENDS:=@TARGET_ramips_mt7621 +kmod-input-core
+  KCONFIG:= \
+	CONFIG_KEYBOARD_SX951X \
+	CONFIG_INPUT_KEYBOARD=y
+  FILES:=$(LINUX_DIR)/drivers/input/keyboard/sx951x.ko
+  AUTOLOAD:=$(call AutoProbe,sx951x)
+endef
+
+define KernelPackage/keyboard-sx951x/description
+ Enable support for SX9512/SX9513 capacitive touch controllers
+endef
+
+$(eval $(call KernelPackage,keyboard-sx951x))
diff --git a/target/linux/ramips/patches-6.6/870-Input-sx951x-add-Semtech-SX9512-SX9513-driver.patch b/target/linux/ramips/patches-6.6/870-Input-sx951x-add-Semtech-SX9512-SX9513-driver.patch
new file mode 100644
index 0000000000..340fb3c342
--- /dev/null
+++ b/target/linux/ramips/patches-6.6/870-Input-sx951x-add-Semtech-SX9512-SX9513-driver.patch
@@ -0,0 +1,560 @@
+From ba92c0187006e2a6eae9573a569d275b0bd31732 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail at david-bauer.net>
+Date: Fri, 2 May 2025 23:04:27 +0200
+Subject: [PATCH] Input sx951x: add Semtech SX9512/SX9513 driver
+
+The Semtech SX9512/SX9513 is a family of capacitive touch-keyboard
+controllers.
+
+All chips offer 8 channel touch sensitive inputs with one LED driver per
+output channel.
+
+The also SX9512 supports proximity detection which is currently not
+supported with the driver.
+
+This chip can be found on the Genexis Pulse EX400 repeater platform.
+
+Link: https://www.mouser.com/datasheet/2/761/SEMTS05226_1-2575172.pdf
+Link: https://www.spinics.net/lists/kernel/msg5669349.html
+
+Signed-off-by: David Bauer <mail at david-bauer.net>
+---
+ drivers/input/keyboard/Kconfig  |  11 +
+ drivers/input/keyboard/Makefile |   1 +
+ drivers/input/keyboard/sx951x.c | 490 ++++++++++++++++++++++++++++++++
+ 3 files changed, 502 insertions(+)
+ create mode 100644 drivers/input/keyboard/sx951x.c
+
+diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
+index 1d0c5f4c0f99..6dc397389c64 100644
+--- a/drivers/input/keyboard/Kconfig
++++ b/drivers/input/keyboard/Kconfig
+@@ -616,6 +616,17 @@ config KEYBOARD_SUNKBD
+ 	  To compile this driver as a module, choose M here: the
+ 	  module will be called sunkbd.
+ 
++config KEYBOARD_SX951X
++	tristate "Semtech SX951X capacitive touch controller"
++	depends on OF && I2C
++	select REGMAP_I2C
++	help
++	  Say Y here to enable the Semtech SX9512/SX9153 capacitive
++	  touch controller driver.
++
++	  To compile this driver as a module, choose M here: the
++	  module will be called sx951x.
++
+ config KEYBOARD_SH_KEYSC
+ 	tristate "SuperH KEYSC keypad support"
+ 	depends on ARCH_SHMOBILE || COMPILE_TEST
+diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
+index aecef00c5d09..e59ca83c30ec 100644
+--- a/drivers/input/keyboard/Makefile
++++ b/drivers/input/keyboard/Makefile
+@@ -66,6 +66,7 @@ obj-$(CONFIG_KEYBOARD_STOWAWAY)		+= stowaway.o
+ obj-$(CONFIG_KEYBOARD_ST_KEYSCAN)	+= st-keyscan.o
+ obj-$(CONFIG_KEYBOARD_SUN4I_LRADC)	+= sun4i-lradc-keys.o
+ obj-$(CONFIG_KEYBOARD_SUNKBD)		+= sunkbd.o
++obj-$(CONFIG_KEYBOARD_SX951X)		+= sx951x.o
+ obj-$(CONFIG_KEYBOARD_TC3589X)		+= tc3589x-keypad.o
+ obj-$(CONFIG_KEYBOARD_TEGRA)		+= tegra-kbc.o
+ obj-$(CONFIG_KEYBOARD_TM2_TOUCHKEY)	+= tm2-touchkey.o
+diff --git a/drivers/input/keyboard/sx951x.c b/drivers/input/keyboard/sx951x.c
+new file mode 100644
+index 000000000000..66355036aa95
+--- /dev/null
++++ b/drivers/input/keyboard/sx951x.c
+@@ -0,0 +1,490 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Input driver for Semtech SX9512/SX9513 capacitive touch sensors.
++ *
++ * The difference between SX9512 and SX9513 is the presence of proximity
++ * sensing capabilities on the SX9512.
++ *
++ * SX951xB is the identical chip but with a different I2C address.
++ *
++ * (c) 2025 David Bauer <mail at david-bauer.net>
++ */
++
++ #include <linux/kernel.h>
++ #include <linux/module.h>
++ #include <linux/input.h>
++ #include <linux/leds.h>
++ #include <linux/of.h>
++ #include <linux/regmap.h>
++ #include <linux/i2c.h>
++ #include <linux/gpio/consumer.h>
++ #include <linux/bitfield.h>
++
++ /* Generic properties */
++#define SX951X_I2C_ADDRESS		0x2b
++#define SX951XB_I2C_ADDRESS_		0x2d
++#define SX951X_NUM_CHANNELS		8
++#define SX951X_POLL_INTERVAL		100
++
++/* Registers*/
++#define SX951X_REG_IRQ_SRC		0x00
++#define SX951X_REG_TOUCH_STATUS		0x01
++#define SX951X_REG_PROXIMITY_STATUS	0x02
++#define SX951X_REG_COMPENSATION_STATUS	0x03
++#define SX951X_REG_IRQ_NVM_CTRL		0x04
++#define SX951X_REG_SPO2_MODE_CTRL	0x07
++#define SX951X_REG_PWR_KEY_CTRL		0x08
++#define SX951X_REG_IRQ_MASK		0x09
++
++/* LED registers */
++#define SX951X_REG_LED_MAP_ENG1		0x0c
++#define SX951X_REG_LED_MAP_ENG2		0x0d
++#define SX951X_REG_LED_PWM_FREQ		0x0e
++#define SX951X_REG_LED_MODE		0x0f
++#define SX951X_REG_LED_IDLE		0x10
++#define SX951X_REG_LED_OFF_DELAY	0x11
++#define SX951X_REG_LED_ON_ENG1		0x12
++#define SX951X_REG_LED_FADE_ENG1	0x13
++#define SX951X_REG_LED_ON_ENG2		0x14
++#define SX951X_REG_LED_FADE_ENG2	0x15
++#define SX951X_REG_LED_POWER_IDLE	0x16
++#define SX951X_REG_LED_POWER_ON		0x17
++#define SX951X_REG_LED_POWER_OFF	0x18
++#define SX951X_REG_LED_POWER_FADE	0x19
++#define SX951X_REG_LED_POWER_ON_PULSE	0x1a
++#define SX951X_REG_LED_POWER_MODE	0x1b
++
++/* Capacitive touch sensing registers*/
++#define SX951X_REG_CAP_SENSE_ENABLE			0x1e
++
++#define SX951X_REG_CAP_SENSE_RANGE(x)			(0x1f + (x))
++#define SX951X_REG_CAP_SENSE_RANGE_CIN_DELTA_MASK	GENMASK(1, 0)
++
++#define SX951X_REG_CAP_SENSE_THRESH(x)			(0x28 + (x))
++#define SX951X_REG_CAP_SENSE_THRESH_ALL			0x30
++
++#define SX951X_REG_CAP_SENSE_OP				0x31
++#define SX951X_REG_CAP_SENSE_MODE			0x32
++#define SX951X_REG_CAP_SENSE_DEBOUNCE			0x33
++
++/* Reset register*/
++#define SX951X_REG_SOFT_RESET			0xff
++
++/* Default properties (keys)*/
++#define SX951X_KEY_DEFAULT_CIN_DELTA		0x03
++#define SX951X_KEY_DEFAULT_SENSE_THRESHOLD	0x04
++
++struct sx951x_key_data {
++	u32 cin_delta;
++	u32 sense_threshold;
++};
++
++struct sx951x_led {
++#ifdef CONFIG_LEDS_CLASS
++	struct led_classdev cdev;
++	struct sx951x_priv *priv;
++
++	u32 reg;
++	bool registered;
++#endif
++};
++
++struct sx951x_priv {
++	struct regmap *regmap;
++	struct device *dev;
++	struct input_dev *idev;
++	const struct sx951x_hw_data *hw;
++
++	struct sx951x_led leds[SX951X_NUM_CHANNELS];
++
++	/* device-config */
++	u32 poll_interval;
++
++	/* key-config */
++	u32 keycodes[SX951X_NUM_CHANNELS];
++	struct sx951x_key_data key_data[SX951X_NUM_CHANNELS];
++};
++
++struct sx951x_hw_data {
++	bool has_proximity_sensing;
++};
++
++static const struct reg_default sx951x_reg_defaults[] = {
++	{ SX951X_REG_LED_MAP_ENG1,		0x00 },
++	{ SX951X_REG_LED_MAP_ENG2,		0x00 },
++	{ SX951X_REG_LED_PWM_FREQ,		0x10 },
++	{ SX951X_REG_LED_IDLE,			0xff },
++	{ SX951X_REG_LED_ON_ENG1,		0xff },
++	{ SX951X_REG_LED_ON_ENG2,		0xff },
++	{ SX951X_REG_LED_POWER_IDLE,		0xff },
++	{ SX951X_REG_LED_POWER_ON,		0xff },
++	{ SX951X_REG_CAP_SENSE_ENABLE,		0x00 },
++	{ SX951X_REG_CAP_SENSE_RANGE(0),	0x40 },
++	{ SX951X_REG_CAP_SENSE_RANGE(1),	0x40 },
++	{ SX951X_REG_CAP_SENSE_RANGE(2),	0x40 },
++	{ SX951X_REG_CAP_SENSE_RANGE(3),	0x40 },
++	{ SX951X_REG_CAP_SENSE_RANGE(4),	0x40 },
++	{ SX951X_REG_CAP_SENSE_RANGE(5),	0x40 },
++	{ SX951X_REG_CAP_SENSE_RANGE(6),	0x40 },
++	{ SX951X_REG_CAP_SENSE_RANGE(7),	0x40 },
++	{ SX951X_REG_CAP_SENSE_THRESH(0),	0x0f },
++	{ SX951X_REG_CAP_SENSE_THRESH(1),	0x0f },
++	{ SX951X_REG_CAP_SENSE_THRESH(2),	0x0f },
++	{ SX951X_REG_CAP_SENSE_THRESH(3),	0x0f },
++	{ SX951X_REG_CAP_SENSE_THRESH(4),	0x0f },
++	{ SX951X_REG_CAP_SENSE_THRESH(5),	0x0f },
++	{ SX951X_REG_CAP_SENSE_THRESH(6),	0x0f },
++	{ SX951X_REG_CAP_SENSE_THRESH(7),	0x0f },
++	{ SX951X_REG_CAP_SENSE_THRESH_ALL,	0x0f },
++	{ SX951X_REG_CAP_SENSE_OP,		0x14 },
++	{ SX951X_REG_CAP_SENSE_MODE,		0x70 },
++	{ SX951X_REG_CAP_SENSE_DEBOUNCE,	0xff },
++};
++
++static bool sx951x_volatile_reg(struct device *dev, unsigned int reg)
++{
++	switch (reg) {
++	case SX951X_REG_TOUCH_STATUS:
++		return true;
++	default:
++		return false;
++	}
++}
++
++static const struct regmap_config sx951x_regmap_config = {
++	.reg_bits = 8,
++	.val_bits = 8,
++
++	.max_register = SX951X_REG_SOFT_RESET,
++
++	.reg_defaults = sx951x_reg_defaults,
++	.num_reg_defaults = ARRAY_SIZE(sx951x_reg_defaults),
++
++	.cache_type = REGCACHE_MAPLE,
++	.volatile_reg = sx951x_volatile_reg,
++};
++
++#ifdef CONFIG_LEDS_CLASS
++static int sx951x_led_set(struct led_classdev *cdev, enum led_brightness value)
++{
++	struct sx951x_led *led = container_of(cdev, struct sx951x_led, cdev);
++	struct sx951x_priv *priv = led->priv;
++
++	return regmap_update_bits(priv->regmap,
++				  SX951X_REG_LED_MAP_ENG2,
++				  BIT(led->reg),
++				  value ? BIT(led->reg) : 0);
++}
++
++static int sx951x_led_init(struct sx951x_priv *priv,
++			   struct device_node *channel_node, u32 reg)
++{
++	struct device_node *led_node;
++	struct sx951x_led *led = &priv->leds[reg];
++	struct led_init_data init_data = {};
++	int error;
++
++	if (led->registered) {
++		dev_err(priv->dev,
++			"LED %d already registered\n", reg);
++		return -EINVAL;
++	}
++
++	led_node = of_get_child_by_name(channel_node, "led");
++	if (!led_node) {
++		/* No LED */
++		return 0;
++	}
++
++	led->cdev.flags = 0;
++	led->cdev.brightness_set_blocking = sx951x_led_set;
++	led->cdev.max_brightness = 1;
++	led->cdev.brightness = LED_OFF;
++
++	init_data.default_label = of_get_property(led_node, "label", NULL);
++	init_data.fwnode = of_fwnode_handle(led_node);
++
++	led->reg = reg;
++	led->priv = priv;
++
++	error = devm_led_classdev_register_ext(priv->dev, &led->cdev, &init_data);
++	if (error)
++		return error;
++
++	return 0;
++}
++#endif
++
++static void sx951x_poll(struct input_dev *input)
++{
++	struct sx951x_priv *priv = input_get_drvdata(input);
++	struct device *dev = priv->dev;
++	unsigned int val;
++	int error;
++	int i;
++
++	error = regmap_read(priv->regmap, SX951X_REG_TOUCH_STATUS, &val);
++	if (error) {
++		dev_err(dev, "Failed to read touch status: %d\n", error);
++		return;
++	}
++
++	for (i = 0; i < SX951X_NUM_CHANNELS; i++) {
++		if (priv->keycodes[i] == KEY_RESERVED)
++			continue;
++
++		input_report_key(input, priv->keycodes[i], !!(val & BIT(i)));
++		input_sync(input);
++	}
++}
++
++static int sx951x_channel_init(struct sx951x_priv *priv, struct device_node *of_node,
++			       u32 chan_idx)
++{
++	struct sx951x_key_data *key_data;
++	struct device *dev = priv->dev;
++	int error;
++
++	key_data = &priv->key_data[chan_idx];
++
++	/* Defaults */
++	key_data->cin_delta = SX951X_KEY_DEFAULT_CIN_DELTA;
++	key_data->sense_threshold = SX951X_KEY_DEFAULT_SENSE_THRESHOLD;
++
++	error = of_property_read_u32(of_node, "linux,keycodes",
++				     &priv->keycodes[chan_idx]);
++	if (error) {
++		/* Not configured */
++		return 0;
++	}
++
++	error = of_property_read_u32(of_node, "semtech,cin-delta",
++				     &key_data->cin_delta);
++	if (key_data->cin_delta > 0x03) {
++		dev_err(dev, "Failed to read cin-delta for channel %d: %d\n",
++			chan_idx, error);
++		return error;
++	}
++
++	error = of_property_read_u32(of_node, "semtech,sense-threshold",
++				     &key_data->sense_threshold);
++	if (key_data->sense_threshold > 0xff) {
++		dev_err(dev, "Failed to read sense-threshold for channel %d: %d\n",
++			chan_idx, error);
++		return error;
++	}
++
++	error = regmap_update_bits(priv->regmap,
++				   SX951X_REG_CAP_SENSE_RANGE(chan_idx),
++				   SX951X_REG_CAP_SENSE_RANGE_CIN_DELTA_MASK,
++				   key_data->cin_delta);
++
++	if (error) {
++		dev_err(dev, "Failed to set cin-delta for channel %d: %d\n",
++			chan_idx, error);
++		return error;
++	}
++
++	error = regmap_write(priv->regmap,
++			     SX951X_REG_CAP_SENSE_THRESH(chan_idx),
++			     key_data->sense_threshold);
++	if (error) {
++		dev_err(dev, "Failed to set sense-threshold for channel %d: %d\n",
++			chan_idx, error);
++		return error;
++	}
++
++	return 0;
++}
++
++static int sx951x_channels_init(struct sx951x_priv *priv)
++{
++	struct device *dev = priv->dev;
++	unsigned int channels = 0;
++	int error;
++	u32 reg;
++
++	for_each_child_of_node_scoped(dev->of_node, child) {
++		error = of_property_read_u32(child, "reg", &reg);
++		if (error != 0 || reg >= SX951X_NUM_CHANNELS) {
++			dev_err(dev, "Invalid channel %d\n", reg);
++			return -EINVAL;
++		}
++
++		priv->keycodes[reg] = KEY_RESERVED;
++
++		error = sx951x_channel_init(priv, child, reg);
++		if (error) {
++			dev_err(dev, "Failed to initialize channel %d: %d\n",
++				reg, error);
++			return error;
++		}
++
++		if (priv->keycodes[reg] != KEY_RESERVED)
++			channels |= BIT(reg);
++
++#ifdef CONFIG_LEDS_CLASS
++		error = sx951x_led_init(priv, child, reg);
++		if (error) {
++			dev_err(dev, "Failed to initialize LED %d: %d\n",
++				reg, error);
++			return error;
++		}
++#endif
++	}
++
++	/* Enable sensing on channels with keycode configured */
++	error = regmap_write(priv->regmap,
++			     SX951X_REG_CAP_SENSE_ENABLE,
++			     channels);
++
++	return 0;
++}
++
++static int sx951x_input_init(struct sx951x_priv *priv)
++{
++	struct device *dev = priv->dev;
++	int i, error;
++
++	priv->idev = devm_input_allocate_device(dev);
++	if (!priv->idev)
++		return -ENOMEM;
++
++	priv->idev->name = "SX9512/SX9513 capacitive touch sensor";
++	priv->idev->id.bustype = BUS_I2C;
++	__set_bit(EV_KEY, priv->idev->evbit);
++
++	for (i = 0; i < SX951X_NUM_CHANNELS; i++)
++		__set_bit(priv->keycodes[i], priv->idev->keybit);
++
++	__clear_bit(KEY_RESERVED, priv->idev->keybit);
++
++	priv->idev->keycode = priv->keycodes;
++	priv->idev->keycodesize = sizeof(priv->keycodes[0]);
++	priv->idev->keycodemax = SX951X_NUM_CHANNELS;
++
++	input_set_drvdata(priv->idev, priv);
++
++	error = input_setup_polling(priv->idev, sx951x_poll);
++	if (error) {
++		dev_err(dev, "Unable to set up polling: %d\n", error);
++		return error;
++	}
++
++	input_set_poll_interval(priv->idev, priv->poll_interval);
++
++	error = input_register_device(priv->idev);
++	if (error) {
++		dev_err(dev, "Unable to register polled device: %d\n",
++			error);
++		return error;
++	}
++
++	return 0;
++}
++
++static int sx951x_probe(struct i2c_client *i2c_client)
++{
++	const struct i2c_device_id *id;
++	const struct sx951x_hw_data *hw;
++	struct device *dev = &i2c_client->dev;
++	struct sx951x_priv *priv;
++	int error;
++
++	if (i2c_client->addr != SX951X_I2C_ADDRESS &&
++	    i2c_client->addr != SX951XB_I2C_ADDRESS_) {
++		dev_err(dev, "Invalid I2C address: 0x%02x\n",
++			i2c_client->addr);
++		return -ENODEV;
++	}
++
++	id = i2c_client_get_device_id(i2c_client);
++	hw = i2c_get_match_data(i2c_client);
++	if (!id || !hw) {
++		dev_err(dev, "Invalid device configuration\n");
++		return -EINVAL;
++	}
++
++	priv = devm_kzalloc(dev,
++			    sizeof(struct sx951x_priv),
++			    GFP_KERNEL);
++	if (!priv)
++		return -ENOMEM;
++
++	priv->dev = dev;
++	priv->hw = hw;
++
++	priv->regmap = devm_regmap_init_i2c(i2c_client, &sx951x_regmap_config);
++	if (IS_ERR(priv->regmap))
++		return PTR_ERR(priv->regmap);
++
++	/* Parse device configuration */
++	if (of_property_read_u32(dev->of_node, "poll-interval",
++				 &priv->poll_interval))
++		priv->poll_interval = SX951X_POLL_INTERVAL;
++
++	/* Register LED and input channels */
++	error = sx951x_channels_init(priv);
++	if (error) {
++		dev_err(dev, "Failed to initialize channels: %d\n", error);
++		return error;
++	}
++
++	/* Register input device */
++	error = sx951x_input_init(priv);
++	if (error) {
++		dev_err(dev, "Failed to register input device: %d\n", error);
++		return error;
++	}
++
++	return 0;
++}
++
++static void sx951x_remove(struct i2c_client *i2c_client)
++{
++	struct sx951x_priv *priv = i2c_get_clientdata(i2c_client);
++
++	/* Disable sensing */
++	regmap_write(priv->regmap, SX951X_REG_CAP_SENSE_ENABLE, 0x00);
++
++	/* Turn off all LEDs */
++	regmap_write(priv->regmap, SX951X_REG_LED_MAP_ENG2, 0x00);
++}
++
++static const struct sx951x_hw_data sx9512_hw_data = {
++	.has_proximity_sensing = true,
++};
++
++static const struct sx951x_hw_data sx9513_hw_data = {
++	.has_proximity_sensing = false,
++};
++
++static const struct of_device_id sx951x_dt_ids[] = {
++	{ .compatible = "semtech,sx9512", .data = &sx9512_hw_data },
++	{ .compatible = "semtech,sx9513", .data = &sx9513_hw_data },
++	{ }
++};
++MODULE_DEVICE_TABLE(of, sx951x_dt_ids);
++
++static const struct i2c_device_id sx951x_i2c_ids[] = {
++	{ "sx9512", (kernel_ulong_t)&sx9512_hw_data },
++	{ "sx9513", (kernel_ulong_t)&sx9513_hw_data },
++	{ }
++};
++MODULE_DEVICE_TABLE(i2c, sx951x_i2c_ids);
++
++static struct i2c_driver sx951x_i2c_driver = {
++	.driver = {
++		.name	= "sx951x",
++		.of_match_table = sx951x_dt_ids,
++	},
++	.id_table	= sx951x_i2c_ids,
++	.probe		= sx951x_probe,
++	.remove		= sx951x_remove,
++};
++
++module_i2c_driver(sx951x_i2c_driver);
++
++MODULE_DESCRIPTION("Semtech SX9512/SX9513 driver");
++MODULE_AUTHOR("David Bauer <mail at david-bauer.net>");
++MODULE_LICENSE("GPL");
+-- 
+2.47.2
+




More information about the lede-commits mailing list