135 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Siemens SIMATIC IPC driver for LEDs4 *5 * Copyright (c) Siemens AG, 2018-20216 *7 * Authors:8 * Henning Schild <henning.schild@siemens.com>9 * Jan Kiszka <jan.kiszka@siemens.com>10 * Gerd Haeussler <gerd.haeussler.ext@siemens.com>11 */12 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>22 23#define SIMATIC_IPC_LED_PORT_BASE 0x404E24 25struct simatic_ipc_led {26 unsigned int value; /* mask for io */27 char *name;28 struct led_classdev cdev;29};30 31static 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" },38 { }39};40 41static struct resource simatic_ipc_led_io_res =42 DEFINE_RES_IO_NAMED(SIMATIC_IPC_LED_PORT_BASE, SZ_2, KBUILD_MODNAME);43 44static DEFINE_SPINLOCK(reg_lock);45 46static inline struct simatic_ipc_led *cdev_to_led(struct led_classdev *led_cd)47{48 return container_of(led_cd, struct simatic_ipc_led, cdev);49}50 51static void simatic_ipc_led_set_io(struct led_classdev *led_cd,52 enum led_brightness brightness)53{54 struct simatic_ipc_led *led = cdev_to_led(led_cd);55 unsigned long flags;56 unsigned int val;57 58 spin_lock_irqsave(®_lock, flags);59 60 val = inw(SIMATIC_IPC_LED_PORT_BASE);61 if (brightness == LED_OFF)62 outw(val | led->value, SIMATIC_IPC_LED_PORT_BASE);63 else64 outw(val & ~led->value, SIMATIC_IPC_LED_PORT_BASE);65 66 spin_unlock_irqrestore(®_lock, flags);67}68 69static enum led_brightness simatic_ipc_led_get_io(struct led_classdev *led_cd)70{71 struct simatic_ipc_led *led = cdev_to_led(led_cd);72 73 return inw(SIMATIC_IPC_LED_PORT_BASE) & led->value ? LED_OFF : led_cd->max_brightness;74}75 76static int simatic_ipc_leds_probe(struct platform_device *pdev)77{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;82 struct resource *res;83 int err;84 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);94 ipcled++;95 }96 ipcled = simatic_ipc_leds_io;97 }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);100 return -EBUSY;101 }102 break;103 default:104 return -ENODEV;105 }106 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;113 114 err = devm_led_classdev_register(dev, cdev);115 if (err < 0)116 return err;117 ipcled++;118 }119 120 return 0;121}122 123static struct platform_driver simatic_ipc_led_driver = {124 .probe = simatic_ipc_leds_probe,125 .driver = {126 .name = KBUILD_MODNAME,127 }128};129module_platform_driver(simatic_ipc_led_driver);130 131MODULE_DESCRIPTION("LED driver for Siemens Simatic IPCs");132MODULE_LICENSE("GPL v2");133MODULE_ALIAS("platform:" KBUILD_MODNAME);134MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");135