diff options
| author | Niklas Cassel <cassel@kernel.org> | 2026-09-16 15:00:32 +0200 |
|---|---|---|
| committer | Niklas Cassel <cassel@kernel.org> | 2026-09-16 16:18:00 +0200 |
| commit | 8d836581f9b1f57ceaa2b47654754ef1260b410b (patch) | |
| tree | 1dbd4d0dfb99c2e238356cbe947e5b0be3e3a571 /drivers/leds/simatic | |
| download | linux-stable-8d836581f9b1f57ceaa2b47654754ef1260b410b.tar.gz linux-stable-8d836581f9b1f57ceaa2b47654754ef1260b410b.zip | |
ata: libata-scsi: fix ata_dsm_trim_pages() kernel-docgrafted
make htmldocs fails with:
Documentation/driver-api/libata:604: ./drivers/ata/libata-scsi.c:2301:
ERROR: Unexpected indentation. [docutils]
The Return: section of ata_dsm_trim_pages() ends its first sentence with
a colon and continues with an indented bullet list. reStructuredText
requires a blank line before an indented block, so docutils chokes on
the list.
Simply adding the missing blank line does not work either: Return: is a
kernel-doc "special section", which is terminated by the first blank
line, so the bullet list would end up in the Description section,
detached from the sentence introducing it.
Spell the two bounds out as prose instead, so that the Return: section
stays self-contained. Documentation-only change.
Fixes: e64e6b5dc867 ("ata: libata-scsi: scale DSM TRIM payload by MAX PAGES PER DSM COMMAND")
Reported-by: Thomas Huth <thuth@redhat.com>
Closes: https://lore.kernel.org/linux-ide/b0a0b8e8-cc4f-4c7b-8bc5-fee0712d405a@redhat.com/
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Link: https://lore.kernel.org/r/20260916130031.29990-2-cassel@kernel.org
Signed-off-by: Niklas Cassel <cassel@kernel.org>
Diffstat (limited to 'drivers/leds/simatic')
| -rw-r--r-- | drivers/leds/simatic/Kconfig | 51 | ||||
| -rw-r--r-- | drivers/leds/simatic/Makefile | 5 | ||||
| -rw-r--r-- | drivers/leds/simatic/simatic-ipc-leds-gpio-apollolake.c | 67 | ||||
| -rw-r--r-- | drivers/leds/simatic/simatic-ipc-leds-gpio-core.c | 108 | ||||
| -rw-r--r-- | drivers/leds/simatic/simatic-ipc-leds-gpio-elkhartlake.c | 57 | ||||
| -rw-r--r-- | drivers/leds/simatic/simatic-ipc-leds-gpio-f7188x.c | 107 | ||||
| -rw-r--r-- | drivers/leds/simatic/simatic-ipc-leds-gpio.h | 22 | ||||
| -rw-r--r-- | drivers/leds/simatic/simatic-ipc-leds.c | 134 |
8 files changed, 551 insertions, 0 deletions
diff --git a/drivers/leds/simatic/Kconfig b/drivers/leds/simatic/Kconfig new file mode 100644 index 000000000..e616cc6d6 --- /dev/null +++ b/drivers/leds/simatic/Kconfig @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: GPL-2.0-only +config LEDS_SIEMENS_SIMATIC_IPC + tristate "LED driver for Siemens Simatic IPCs" + depends on LEDS_CLASS + depends on SIEMENS_SIMATIC_IPC + default y + help + This option enables support for the LEDs of several Industrial PCs + from Siemens. + + To compile this driver as a module, choose M here: the module + will be called simatic-ipc-leds. + +config LEDS_SIEMENS_SIMATIC_IPC_APOLLOLAKE + tristate "LED driver for Siemens Simatic IPCs based on Intel Apollo Lake GPIO" + depends on LEDS_GPIO + depends on PINCTRL_BROXTON + depends on SIEMENS_SIMATIC_IPC + default LEDS_SIEMENS_SIMATIC_IPC + help + This option enables support for the LEDs of several Industrial PCs + from Siemens based on Apollo Lake GPIO i.e. IPC127E. + + To compile this driver as a module, choose M here: the module + will be called simatic-ipc-leds-gpio-apollolake. + +config LEDS_SIEMENS_SIMATIC_IPC_F7188X + tristate "LED driver for Siemens Simatic IPCs based on Nuvoton GPIO" + depends on LEDS_GPIO + depends on GPIO_F7188X + depends on SIEMENS_SIMATIC_IPC + default LEDS_SIEMENS_SIMATIC_IPC + help + This option enables support for the LEDs of several Industrial PCs + from Siemens based on Nuvoton GPIO i.e. IPC227G. + + To compile this driver as a module, choose M here: the module + will be called simatic-ipc-leds-gpio-f7188x. + +config LEDS_SIEMENS_SIMATIC_IPC_ELKHARTLAKE + tristate "LED driver for Siemens Simatic IPCs based on Intel Elkhart Lake GPIO" + depends on LEDS_GPIO + depends on PINCTRL_ELKHARTLAKE + depends on SIEMENS_SIMATIC_IPC + default LEDS_SIEMENS_SIMATIC_IPC + help + This option enables support for the LEDs of several Industrial PCs + from Siemens based on Elkhart Lake GPIO i.e. BX-21A. + + To compile this driver as a module, choose M here: the module + will be called simatic-ipc-leds-gpio-elkhartlake. diff --git a/drivers/leds/simatic/Makefile b/drivers/leds/simatic/Makefile new file mode 100644 index 000000000..783578f11 --- /dev/null +++ b/drivers/leds/simatic/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds.o +obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC_APOLLOLAKE) += simatic-ipc-leds-gpio-core.o simatic-ipc-leds-gpio-apollolake.o +obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC_F7188X) += simatic-ipc-leds-gpio-core.o simatic-ipc-leds-gpio-f7188x.o +obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC_ELKHARTLAKE) += simatic-ipc-leds-gpio-core.o simatic-ipc-leds-gpio-elkhartlake.o diff --git a/drivers/leds/simatic/simatic-ipc-leds-gpio-apollolake.c b/drivers/leds/simatic/simatic-ipc-leds-gpio-apollolake.c new file mode 100644 index 000000000..c98c37068 --- /dev/null +++ b/drivers/leds/simatic/simatic-ipc-leds-gpio-apollolake.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Siemens SIMATIC IPC driver for GPIO based LEDs + * + * Copyright (c) Siemens AG, 2023 + * + * Author: + * Henning Schild <henning.schild@siemens.com> + */ + +#include <linux/gpio/machine.h> +#include <linux/gpio/consumer.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/platform_data/x86/simatic-ipc-base.h> + +#include "simatic-ipc-leds-gpio.h" + +static struct gpiod_lookup_table simatic_ipc_led_gpio_table = { + .dev_id = "leds-gpio", + .table = { + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 52, NULL, 0, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 53, NULL, 1, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 57, NULL, 2, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 58, NULL, 3, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 60, NULL, 4, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 51, NULL, 5, GPIO_ACTIVE_LOW), + {} /* Terminating entry */ + }, +}; + +static struct gpiod_lookup_table simatic_ipc_led_gpio_table_extra = { + .dev_id = NULL, /* Filled during initialization */ + .table = { + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 56, NULL, 6, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 59, NULL, 7, GPIO_ACTIVE_HIGH), + {} /* Terminating entry */ + }, +}; + +static int simatic_ipc_leds_gpio_apollolake_probe(struct platform_device *pdev) +{ + return simatic_ipc_leds_gpio_probe(pdev, &simatic_ipc_led_gpio_table, + &simatic_ipc_led_gpio_table_extra); +} + +static void simatic_ipc_leds_gpio_apollolake_remove(struct platform_device *pdev) +{ + simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table, + &simatic_ipc_led_gpio_table_extra); +} + +static struct platform_driver simatic_ipc_led_gpio_apollolake_driver = { + .probe = simatic_ipc_leds_gpio_apollolake_probe, + .remove = simatic_ipc_leds_gpio_apollolake_remove, + .driver = { + .name = KBUILD_MODNAME, + }, +}; +module_platform_driver(simatic_ipc_led_gpio_apollolake_driver); + +MODULE_DESCRIPTION("LED driver for Siemens Simatic IPCs based on Intel Apollo Lake GPIO"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" KBUILD_MODNAME); +MODULE_SOFTDEP("pre: simatic-ipc-leds-gpio-core platform:apollolake-pinctrl"); +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>"); diff --git a/drivers/leds/simatic/simatic-ipc-leds-gpio-core.c b/drivers/leds/simatic/simatic-ipc-leds-gpio-core.c new file mode 100644 index 000000000..9bc5f361a --- /dev/null +++ b/drivers/leds/simatic/simatic-ipc-leds-gpio-core.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Siemens SIMATIC IPC driver for GPIO based LEDs + * + * Copyright (c) Siemens AG, 2023 + * + * Author: + * Henning Schild <henning.schild@siemens.com> + */ + +#include <linux/gpio/machine.h> +#include <linux/gpio/consumer.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/platform_data/x86/simatic-ipc-base.h> + +#include "simatic-ipc-leds-gpio.h" + +static struct platform_device *simatic_leds_pdev; + +static const struct gpio_led simatic_ipc_gpio_leds[] = { + { .name = "red:" LED_FUNCTION_STATUS "-1" }, + { .name = "green:" LED_FUNCTION_STATUS "-1" }, + { .name = "red:" LED_FUNCTION_STATUS "-2" }, + { .name = "green:" LED_FUNCTION_STATUS "-2" }, + { .name = "red:" LED_FUNCTION_STATUS "-3" }, + { .name = "green:" LED_FUNCTION_STATUS "-3" }, +}; + +static const struct gpio_led_platform_data simatic_ipc_gpio_leds_pdata = { + .num_leds = ARRAY_SIZE(simatic_ipc_gpio_leds), + .leds = simatic_ipc_gpio_leds, +}; + +void simatic_ipc_leds_gpio_remove(struct platform_device *pdev, + struct gpiod_lookup_table *table, + struct gpiod_lookup_table *table_extra) +{ + gpiod_remove_lookup_table(table); + gpiod_remove_lookup_table(table_extra); + platform_device_unregister(simatic_leds_pdev); +} +EXPORT_SYMBOL_GPL(simatic_ipc_leds_gpio_remove); + +int simatic_ipc_leds_gpio_probe(struct platform_device *pdev, + struct gpiod_lookup_table *table, + struct gpiod_lookup_table *table_extra) +{ + const struct simatic_ipc_platform *plat = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct gpio_desc *gpiod; + int err; + + switch (plat->devmode) { + case SIMATIC_IPC_DEVICE_127E: + case SIMATIC_IPC_DEVICE_227G: + case SIMATIC_IPC_DEVICE_BX_21A: + case SIMATIC_IPC_DEVICE_BX_59A: + break; + default: + return -ENODEV; + } + + gpiod_add_lookup_table(table); + simatic_leds_pdev = platform_device_register_resndata(NULL, + "leds-gpio", PLATFORM_DEVID_NONE, NULL, 0, + &simatic_ipc_gpio_leds_pdata, + sizeof(simatic_ipc_gpio_leds_pdata)); + if (IS_ERR(simatic_leds_pdev)) { + err = PTR_ERR(simatic_leds_pdev); + goto out; + } + + if (!table_extra) + return 0; + + table_extra->dev_id = dev_name(dev); + gpiod_add_lookup_table(table_extra); + + /* PM_BIOS_BOOT_N */ + gpiod = gpiod_get_index(dev, NULL, 6, GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) { + err = PTR_ERR(gpiod); + goto out; + } + gpiod_put(gpiod); + + /* PM_WDT_OUT */ + gpiod = gpiod_get_index(dev, NULL, 7, GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) { + err = PTR_ERR(gpiod); + goto out; + } + gpiod_put(gpiod); + + return 0; +out: + simatic_ipc_leds_gpio_remove(pdev, table, table_extra); + + return err; +} +EXPORT_SYMBOL_GPL(simatic_ipc_leds_gpio_probe); + +MODULE_DESCRIPTION("Siemens SIMATIC IPC core driver for GPIO based LEDs"); +MODULE_LICENSE("GPL v2"); +MODULE_SOFTDEP("pre: platform:leds-gpio"); +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>"); diff --git a/drivers/leds/simatic/simatic-ipc-leds-gpio-elkhartlake.c b/drivers/leds/simatic/simatic-ipc-leds-gpio-elkhartlake.c new file mode 100644 index 000000000..7f7cff275 --- /dev/null +++ b/drivers/leds/simatic/simatic-ipc-leds-gpio-elkhartlake.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Siemens SIMATIC IPC driver for GPIO based LEDs + * + * Copyright (c) Siemens AG, 2023 + * + * Author: + * Henning Schild <henning.schild@siemens.com> + */ + +#include <linux/gpio/machine.h> +#include <linux/gpio/consumer.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/platform_data/x86/simatic-ipc-base.h> + +#include "simatic-ipc-leds-gpio.h" + +static struct gpiod_lookup_table simatic_ipc_led_gpio_table = { + .dev_id = "leds-gpio", + .table = { + GPIO_LOOKUP_IDX("INTC1020:04", 72, NULL, 0, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("INTC1020:04", 77, NULL, 1, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("INTC1020:04", 78, NULL, 2, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("INTC1020:04", 58, NULL, 3, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("INTC1020:04", 60, NULL, 4, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("INTC1020:04", 62, NULL, 5, GPIO_ACTIVE_HIGH), + {} /* Terminating entry */ + }, +}; + +static int simatic_ipc_leds_gpio_elkhartlake_probe(struct platform_device *pdev) +{ + return simatic_ipc_leds_gpio_probe(pdev, &simatic_ipc_led_gpio_table, + NULL); +} + +static void simatic_ipc_leds_gpio_elkhartlake_remove(struct platform_device *pdev) +{ + simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table, NULL); +} + +static struct platform_driver simatic_ipc_led_gpio_elkhartlake_driver = { + .probe = simatic_ipc_leds_gpio_elkhartlake_probe, + .remove = simatic_ipc_leds_gpio_elkhartlake_remove, + .driver = { + .name = KBUILD_MODNAME, + }, +}; +module_platform_driver(simatic_ipc_led_gpio_elkhartlake_driver); + +MODULE_DESCRIPTION("LED driver for Siemens Simatic IPCs based on Intel Elkhart Lake GPIO"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" KBUILD_MODNAME); +MODULE_SOFTDEP("pre: simatic-ipc-leds-gpio-core platform:elkhartlake-pinctrl"); +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>"); diff --git a/drivers/leds/simatic/simatic-ipc-leds-gpio-f7188x.c b/drivers/leds/simatic/simatic-ipc-leds-gpio-f7188x.c new file mode 100644 index 000000000..bc23d701b --- /dev/null +++ b/drivers/leds/simatic/simatic-ipc-leds-gpio-f7188x.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Siemens SIMATIC IPC driver for GPIO based LEDs + * + * Copyright (c) Siemens AG, 2023 + * + * Author: + * Henning Schild <henning.schild@siemens.com> + */ + +#include <linux/gpio/machine.h> +#include <linux/gpio/consumer.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/platform_data/x86/simatic-ipc-base.h> + +#include "simatic-ipc-leds-gpio.h" + +struct simatic_ipc_led_tables { + struct gpiod_lookup_table *led_lookup_table; + struct gpiod_lookup_table *led_lookup_table_extra; +}; + +static struct gpiod_lookup_table simatic_ipc_led_gpio_table_227g = { + .dev_id = "leds-gpio", + .table = { + GPIO_LOOKUP_IDX("gpio-f7188x-2", 0, NULL, 0, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("gpio-f7188x-2", 1, NULL, 1, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("gpio-f7188x-2", 2, NULL, 2, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("gpio-f7188x-2", 3, NULL, 3, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("gpio-f7188x-2", 4, NULL, 4, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("gpio-f7188x-2", 5, NULL, 5, GPIO_ACTIVE_LOW), + {} /* Terminating entry */ + }, +}; + +static struct gpiod_lookup_table simatic_ipc_led_gpio_table_extra_227g = { + .dev_id = NULL, /* Filled during initialization */ + .table = { + GPIO_LOOKUP_IDX("gpio-f7188x-3", 6, NULL, 6, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("gpio-f7188x-3", 7, NULL, 7, GPIO_ACTIVE_HIGH), + {} /* Terminating entry */ + }, +}; + +static struct gpiod_lookup_table simatic_ipc_led_gpio_table_bx_59a = { + .dev_id = "leds-gpio", + .table = { + GPIO_LOOKUP_IDX("gpio-f7188x-2", 0, NULL, 0, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("gpio-f7188x-2", 3, NULL, 1, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("gpio-f7188x-5", 3, NULL, 2, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("gpio-f7188x-5", 2, NULL, 3, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("gpio-f7188x-7", 7, NULL, 4, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("gpio-f7188x-7", 4, NULL, 5, GPIO_ACTIVE_LOW), + {} /* Terminating entry */ + } +}; + +static int simatic_ipc_leds_gpio_f7188x_probe(struct platform_device *pdev) +{ + const struct simatic_ipc_platform *plat = dev_get_platdata(&pdev->dev); + struct simatic_ipc_led_tables *led_tables; + + led_tables = devm_kzalloc(&pdev->dev, sizeof(*led_tables), GFP_KERNEL); + if (!led_tables) + return -ENOMEM; + + switch (plat->devmode) { + case SIMATIC_IPC_DEVICE_227G: + led_tables->led_lookup_table = &simatic_ipc_led_gpio_table_227g; + led_tables->led_lookup_table_extra = &simatic_ipc_led_gpio_table_extra_227g; + break; + case SIMATIC_IPC_DEVICE_BX_59A: + led_tables->led_lookup_table = &simatic_ipc_led_gpio_table_bx_59a; + break; + default: + return -ENODEV; + } + + platform_set_drvdata(pdev, led_tables); + return simatic_ipc_leds_gpio_probe(pdev, led_tables->led_lookup_table, + led_tables->led_lookup_table_extra); +} + +static void simatic_ipc_leds_gpio_f7188x_remove(struct platform_device *pdev) +{ + struct simatic_ipc_led_tables *led_tables = platform_get_drvdata(pdev); + + simatic_ipc_leds_gpio_remove(pdev, led_tables->led_lookup_table, + led_tables->led_lookup_table_extra); +} + +static struct platform_driver simatic_ipc_led_gpio_driver = { + .probe = simatic_ipc_leds_gpio_f7188x_probe, + .remove = simatic_ipc_leds_gpio_f7188x_remove, + .driver = { + .name = KBUILD_MODNAME, + }, +}; +module_platform_driver(simatic_ipc_led_gpio_driver); + +MODULE_DESCRIPTION("LED driver for Siemens Simatic IPCs based on Nuvoton GPIO"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" KBUILD_MODNAME); +MODULE_SOFTDEP("pre: simatic-ipc-leds-gpio-core gpio_f7188x"); +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>"); diff --git a/drivers/leds/simatic/simatic-ipc-leds-gpio.h b/drivers/leds/simatic/simatic-ipc-leds-gpio.h new file mode 100644 index 000000000..6b2519809 --- /dev/null +++ b/drivers/leds/simatic/simatic-ipc-leds-gpio.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Siemens SIMATIC IPC driver for GPIO based LEDs + * + * Copyright (c) Siemens AG, 2023 + * + * Author: + * Henning Schild <henning.schild@siemens.com> + */ + +#ifndef _SIMATIC_IPC_LEDS_GPIO_H +#define _SIMATIC_IPC_LEDS_GPIO_H + +int simatic_ipc_leds_gpio_probe(struct platform_device *pdev, + struct gpiod_lookup_table *table, + struct gpiod_lookup_table *table_extra); + +void simatic_ipc_leds_gpio_remove(struct platform_device *pdev, + struct gpiod_lookup_table *table, + struct gpiod_lookup_table *table_extra); + +#endif /* _SIMATIC_IPC_LEDS_GPIO_H */ diff --git a/drivers/leds/simatic/simatic-ipc-leds.c b/drivers/leds/simatic/simatic-ipc-leds.c new file mode 100644 index 000000000..348679f0d --- /dev/null +++ b/drivers/leds/simatic/simatic-ipc-leds.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Siemens SIMATIC IPC driver for LEDs + * + * Copyright (c) Siemens AG, 2018-2021 + * + * Authors: + * Henning Schild <henning.schild@siemens.com> + * Jan Kiszka <jan.kiszka@siemens.com> + * Gerd Haeussler <gerd.haeussler.ext@siemens.com> + */ + +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_data/x86/simatic-ipc-base.h> +#include <linux/platform_device.h> +#include <linux/sizes.h> +#include <linux/spinlock.h> + +#define SIMATIC_IPC_LED_PORT_BASE 0x404E + +struct simatic_ipc_led { + unsigned int value; /* mask for io */ + char *name; + struct led_classdev cdev; +}; + +static struct simatic_ipc_led simatic_ipc_leds_io[] = { + {1 << 15, "green:" LED_FUNCTION_STATUS "-1" }, + {1 << 7, "yellow:" LED_FUNCTION_STATUS "-1" }, + {1 << 14, "red:" LED_FUNCTION_STATUS "-2" }, + {1 << 6, "yellow:" LED_FUNCTION_STATUS "-2" }, + {1 << 13, "red:" LED_FUNCTION_STATUS "-3" }, + {1 << 5, "yellow:" LED_FUNCTION_STATUS "-3" }, + { } +}; + +static struct resource simatic_ipc_led_io_res = + DEFINE_RES_IO_NAMED(SIMATIC_IPC_LED_PORT_BASE, SZ_2, KBUILD_MODNAME); + +static DEFINE_SPINLOCK(reg_lock); + +static inline struct simatic_ipc_led *cdev_to_led(struct led_classdev *led_cd) +{ + return container_of(led_cd, struct simatic_ipc_led, cdev); +} + +static void simatic_ipc_led_set_io(struct led_classdev *led_cd, + enum led_brightness brightness) +{ + struct simatic_ipc_led *led = cdev_to_led(led_cd); + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(®_lock, flags); + + val = inw(SIMATIC_IPC_LED_PORT_BASE); + if (brightness == LED_OFF) + outw(val | led->value, SIMATIC_IPC_LED_PORT_BASE); + else + outw(val & ~led->value, SIMATIC_IPC_LED_PORT_BASE); + + spin_unlock_irqrestore(®_lock, flags); +} + +static enum led_brightness simatic_ipc_led_get_io(struct led_classdev *led_cd) +{ + struct simatic_ipc_led *led = cdev_to_led(led_cd); + + return inw(SIMATIC_IPC_LED_PORT_BASE) & led->value ? LED_OFF : led_cd->max_brightness; +} + +static int simatic_ipc_leds_probe(struct platform_device *pdev) +{ + const struct simatic_ipc_platform *plat = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct simatic_ipc_led *ipcled; + struct led_classdev *cdev; + struct resource *res; + int err; + + switch (plat->devmode) { + case SIMATIC_IPC_DEVICE_227D: + case SIMATIC_IPC_DEVICE_427E: + res = &simatic_ipc_led_io_res; + ipcled = simatic_ipc_leds_io; + /* on 227D the two bytes work the other way araound */ + if (plat->devmode == SIMATIC_IPC_DEVICE_227D) { + while (ipcled->value) { + ipcled->value = swab16(ipcled->value); + ipcled++; + } + ipcled = simatic_ipc_leds_io; + } + if (!devm_request_region(dev, res->start, resource_size(res), KBUILD_MODNAME)) { + dev_err(dev, "Unable to register IO resource at %pR\n", res); + return -EBUSY; + } + break; + default: + return -ENODEV; + } + + while (ipcled->value) { + cdev = &ipcled->cdev; + cdev->brightness_set = simatic_ipc_led_set_io; + cdev->brightness_get = simatic_ipc_led_get_io; + cdev->max_brightness = LED_ON; + cdev->name = ipcled->name; + + err = devm_led_classdev_register(dev, cdev); + if (err < 0) + return err; + ipcled++; + } + + return 0; +} + +static struct platform_driver simatic_ipc_led_driver = { + .probe = simatic_ipc_leds_probe, + .driver = { + .name = KBUILD_MODNAME, + } +}; +module_platform_driver(simatic_ipc_led_driver); + +MODULE_DESCRIPTION("LED driver for Siemens Simatic IPCs"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" KBUILD_MODNAME); +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>"); |
