brintos

brintos / linux-shallow public Read only

0
0
Text · 3.5 KiB · 348679f Raw
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(&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	else64		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>");135