Linux Audio

Check our new training course

Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Siemens SIMATIC IPC driver for LEDs
  4 *
  5 * Copyright (c) Siemens AG, 2018-2021
  6 *
  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	0x404E
 24
 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(&reg_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	else
 64		outw(val & ~led->value, SIMATIC_IPC_LED_PORT_BASE);
 65
 66	spin_unlock_irqrestore(&reg_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>");
v6.2
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Siemens SIMATIC IPC driver for LEDs
  4 *
  5 * Copyright (c) Siemens AG, 2018-2021
  6 *
  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	0x404E
 24
 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(&reg_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	else
 64		outw(val & ~led->value, SIMATIC_IPC_LED_PORT_BASE);
 65
 66	spin_unlock_irqrestore(&reg_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};
129
130module_platform_driver(simatic_ipc_led_driver);
131
 
132MODULE_LICENSE("GPL v2");
133MODULE_ALIAS("platform:" KBUILD_MODNAME);
134MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");