1 // SPDX-License-Identifier: GPL-2.0
3 * Siemens SIMATIC IPC driver for LEDs
5 * Copyright (c) Siemens AG, 2018-2021
8 * Henning Schild <henning.schild@siemens.com>
9 * Jan Kiszka <jan.kiszka@siemens.com>
10 * Gerd Haeussler <gerd.haeussler.ext@siemens.com>
13 #include <linux/ioport.h>
14 #include <linux/kernel.h>
15 #include <linux/leds.h>
16 #include <linux/module.h>
17 #include <linux/pci.h>
18 #include <linux/platform_data/x86/simatic-ipc-base.h>
19 #include <linux/platform_device.h>
20 #include <linux/sizes.h>
21 #include <linux/spinlock.h>
23 #define SIMATIC_IPC_LED_PORT_BASE 0x404E
25 struct simatic_ipc_led
{
26 unsigned int value
; /* mask for io */
28 struct led_classdev cdev
;
31 static struct simatic_ipc_led simatic_ipc_leds_io
[] = {
32 {1 << 15, "green:" LED_FUNCTION_STATUS
"-1" },
33 {1 << 7, "yellow:" LED_FUNCTION_STATUS
"-1" },
34 {1 << 14, "red:" LED_FUNCTION_STATUS
"-2" },
35 {1 << 6, "yellow:" LED_FUNCTION_STATUS
"-2" },
36 {1 << 13, "red:" LED_FUNCTION_STATUS
"-3" },
37 {1 << 5, "yellow:" LED_FUNCTION_STATUS
"-3" },
41 static struct resource simatic_ipc_led_io_res
=
42 DEFINE_RES_IO_NAMED(SIMATIC_IPC_LED_PORT_BASE
, SZ_2
, KBUILD_MODNAME
);
44 static DEFINE_SPINLOCK(reg_lock
);
46 static inline struct simatic_ipc_led
*cdev_to_led(struct led_classdev
*led_cd
)
48 return container_of(led_cd
, struct simatic_ipc_led
, cdev
);
51 static void simatic_ipc_led_set_io(struct led_classdev
*led_cd
,
52 enum led_brightness brightness
)
54 struct simatic_ipc_led
*led
= cdev_to_led(led_cd
);
58 spin_lock_irqsave(®_lock
, flags
);
60 val
= inw(SIMATIC_IPC_LED_PORT_BASE
);
61 if (brightness
== LED_OFF
)
62 outw(val
| led
->value
, SIMATIC_IPC_LED_PORT_BASE
);
64 outw(val
& ~led
->value
, SIMATIC_IPC_LED_PORT_BASE
);
66 spin_unlock_irqrestore(®_lock
, flags
);
69 static enum led_brightness
simatic_ipc_led_get_io(struct led_classdev
*led_cd
)
71 struct simatic_ipc_led
*led
= cdev_to_led(led_cd
);
73 return inw(SIMATIC_IPC_LED_PORT_BASE
) & led
->value
? LED_OFF
: led_cd
->max_brightness
;
76 static int simatic_ipc_leds_probe(struct platform_device
*pdev
)
78 const struct simatic_ipc_platform
*plat
= pdev
->dev
.platform_data
;
79 struct device
*dev
= &pdev
->dev
;
80 struct simatic_ipc_led
*ipcled
;
81 struct led_classdev
*cdev
;
85 switch (plat
->devmode
) {
86 case SIMATIC_IPC_DEVICE_227D
:
87 case SIMATIC_IPC_DEVICE_427E
:
88 res
= &simatic_ipc_led_io_res
;
89 ipcled
= simatic_ipc_leds_io
;
90 /* on 227D the two bytes work the other way araound */
91 if (plat
->devmode
== SIMATIC_IPC_DEVICE_227D
) {
92 while (ipcled
->value
) {
93 ipcled
->value
= swab16(ipcled
->value
);
96 ipcled
= simatic_ipc_leds_io
;
98 if (!devm_request_region(dev
, res
->start
, resource_size(res
), KBUILD_MODNAME
)) {
99 dev_err(dev
, "Unable to register IO resource at %pR\n", res
);
107 while (ipcled
->value
) {
108 cdev
= &ipcled
->cdev
;
109 cdev
->brightness_set
= simatic_ipc_led_set_io
;
110 cdev
->brightness_get
= simatic_ipc_led_get_io
;
111 cdev
->max_brightness
= LED_ON
;
112 cdev
->name
= ipcled
->name
;
114 err
= devm_led_classdev_register(dev
, cdev
);
123 static struct platform_driver simatic_ipc_led_driver
= {
124 .probe
= simatic_ipc_leds_probe
,
126 .name
= KBUILD_MODNAME
,
129 module_platform_driver(simatic_ipc_led_driver
);
131 MODULE_DESCRIPTION("LED driver for Siemens Simatic IPCs");
132 MODULE_LICENSE("GPL v2");
133 MODULE_ALIAS("platform:" KBUILD_MODNAME
);
134 MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");