Commit a6c80bec authored by Henning Schild's avatar Henning Schild Committed by Lee Jones

leds: simatic-ipc-leds-gpio: Add GPIO version of Siemens driver

On Apollo Lake the pinctrl drivers will now come up without ACPI. Use
that instead of open coding it.
Create a new driver for that which can later be filled with more GPIO
based models, and which has different dependencies.
Signed-off-by: default avatarHenning Schild <henning.schild@siemens.com>
Signed-off-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarLee Jones <lee@kernel.org>
parent 446f0cf9
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
config LEDS_SIEMENS_SIMATIC_IPC config LEDS_SIEMENS_SIMATIC_IPC
tristate "LED driver for Siemens Simatic IPCs" tristate "LED driver for Siemens Simatic IPCs"
depends on LEDS_CLASS depends on LEDS_GPIO
depends on SIEMENS_SIMATIC_IPC depends on SIEMENS_SIMATIC_IPC
select P2SB
help help
This option enables support for the LEDs of several Industrial PCs This option enables support for the LEDs of several Industrial PCs
from Siemens. from Siemens.
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the modules
will be called simatic-ipc-leds. will be called simatic-ipc-leds and simatic-ipc-leds-gpio.
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds.o obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds.o
obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds-gpio.o
// SPDX-License-Identifier: GPL-2.0
/*
* Siemens SIMATIC IPC driver for GPIO based LEDs
*
* Copyright (c) Siemens AG, 2022
*
* Authors:
* 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>
static struct gpiod_lookup_table simatic_ipc_led_gpio_table = {
.dev_id = "leds-gpio",
.table = {
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 51, NULL, 0, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 52, NULL, 1, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 53, NULL, 2, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 57, NULL, 3, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 58, NULL, 4, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 60, NULL, 5, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 56, NULL, 6, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 59, NULL, 7, GPIO_ACTIVE_HIGH),
},
};
static const struct gpio_led simatic_ipc_gpio_leds[] = {
{ .name = "green:" LED_FUNCTION_STATUS "-3" },
{ .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" },
};
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,
};
static struct platform_device *simatic_leds_pdev;
static int simatic_ipc_leds_gpio_remove(struct platform_device *pdev)
{
gpiod_remove_lookup_table(&simatic_ipc_led_gpio_table);
platform_device_unregister(simatic_leds_pdev);
return 0;
}
static int simatic_ipc_leds_gpio_probe(struct platform_device *pdev)
{
struct gpio_desc *gpiod;
int err;
gpiod_add_lookup_table(&simatic_ipc_led_gpio_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;
}
/* PM_BIOS_BOOT_N */
gpiod = gpiod_get_index(&simatic_leds_pdev->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(&simatic_leds_pdev->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);
return err;
}
static struct platform_driver simatic_ipc_led_gpio_driver = {
.probe = simatic_ipc_leds_gpio_probe,
.remove = simatic_ipc_leds_gpio_remove,
.driver = {
.name = KBUILD_MODNAME,
}
};
module_platform_driver(simatic_ipc_led_gpio_driver);
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" KBUILD_MODNAME);
MODULE_SOFTDEP("pre: platform:leds-gpio");
MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/platform_data/x86/p2sb.h>
#include <linux/platform_data/x86/simatic-ipc-base.h> #include <linux/platform_data/x86/simatic-ipc-base.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/sizes.h> #include <linux/sizes.h>
...@@ -24,7 +23,7 @@ ...@@ -24,7 +23,7 @@
#define SIMATIC_IPC_LED_PORT_BASE 0x404E #define SIMATIC_IPC_LED_PORT_BASE 0x404E
struct simatic_ipc_led { struct simatic_ipc_led {
unsigned int value; /* mask for io and offset for mem */ unsigned int value; /* mask for io */
char *name; char *name;
struct led_classdev cdev; struct led_classdev cdev;
}; };
...@@ -39,21 +38,6 @@ static struct simatic_ipc_led simatic_ipc_leds_io[] = { ...@@ -39,21 +38,6 @@ static struct simatic_ipc_led simatic_ipc_leds_io[] = {
{ } { }
}; };
/* the actual start will be discovered with p2sb, 0 is a placeholder */
static struct resource simatic_ipc_led_mem_res = DEFINE_RES_MEM_NAMED(0, 0, KBUILD_MODNAME);
static void __iomem *simatic_ipc_led_memory;
static struct simatic_ipc_led simatic_ipc_leds_mem[] = {
{0x500 + 0x1A0, "red:" LED_FUNCTION_STATUS "-1"},
{0x500 + 0x1A8, "green:" LED_FUNCTION_STATUS "-1"},
{0x500 + 0x1C8, "red:" LED_FUNCTION_STATUS "-2"},
{0x500 + 0x1D0, "green:" LED_FUNCTION_STATUS "-2"},
{0x500 + 0x1E0, "red:" LED_FUNCTION_STATUS "-3"},
{0x500 + 0x198, "green:" LED_FUNCTION_STATUS "-3"},
{ }
};
static struct resource simatic_ipc_led_io_res = static struct resource simatic_ipc_led_io_res =
DEFINE_RES_IO_NAMED(SIMATIC_IPC_LED_PORT_BASE, SZ_2, KBUILD_MODNAME); DEFINE_RES_IO_NAMED(SIMATIC_IPC_LED_PORT_BASE, SZ_2, KBUILD_MODNAME);
...@@ -89,28 +73,6 @@ static enum led_brightness simatic_ipc_led_get_io(struct led_classdev *led_cd) ...@@ -89,28 +73,6 @@ static enum led_brightness simatic_ipc_led_get_io(struct led_classdev *led_cd)
return inw(SIMATIC_IPC_LED_PORT_BASE) & led->value ? LED_OFF : led_cd->max_brightness; return inw(SIMATIC_IPC_LED_PORT_BASE) & led->value ? LED_OFF : led_cd->max_brightness;
} }
static void simatic_ipc_led_set_mem(struct led_classdev *led_cd,
enum led_brightness brightness)
{
struct simatic_ipc_led *led = cdev_to_led(led_cd);
void __iomem *reg = simatic_ipc_led_memory + led->value;
u32 val;
val = readl(reg);
val = (val & ~1) | (brightness == LED_OFF);
writel(val, reg);
}
static enum led_brightness simatic_ipc_led_get_mem(struct led_classdev *led_cd)
{
struct simatic_ipc_led *led = cdev_to_led(led_cd);
void __iomem *reg = simatic_ipc_led_memory + led->value;
u32 val;
val = readl(reg);
return (val & 1) ? LED_OFF : led_cd->max_brightness;
}
static int simatic_ipc_leds_probe(struct platform_device *pdev) static int simatic_ipc_leds_probe(struct platform_device *pdev)
{ {
const struct simatic_ipc_platform *plat = pdev->dev.platform_data; const struct simatic_ipc_platform *plat = pdev->dev.platform_data;
...@@ -118,9 +80,7 @@ static int simatic_ipc_leds_probe(struct platform_device *pdev) ...@@ -118,9 +80,7 @@ static int simatic_ipc_leds_probe(struct platform_device *pdev)
struct simatic_ipc_led *ipcled; struct simatic_ipc_led *ipcled;
struct led_classdev *cdev; struct led_classdev *cdev;
struct resource *res; struct resource *res;
void __iomem *reg; int err;
int err, type;
u32 val;
switch (plat->devmode) { switch (plat->devmode) {
case SIMATIC_IPC_DEVICE_227D: case SIMATIC_IPC_DEVICE_227D:
...@@ -135,51 +95,19 @@ static int simatic_ipc_leds_probe(struct platform_device *pdev) ...@@ -135,51 +95,19 @@ static int simatic_ipc_leds_probe(struct platform_device *pdev)
} }
ipcled = simatic_ipc_leds_io; ipcled = simatic_ipc_leds_io;
} }
type = IORESOURCE_IO;
if (!devm_request_region(dev, res->start, resource_size(res), KBUILD_MODNAME)) { if (!devm_request_region(dev, res->start, resource_size(res), KBUILD_MODNAME)) {
dev_err(dev, "Unable to register IO resource at %pR\n", res); dev_err(dev, "Unable to register IO resource at %pR\n", res);
return -EBUSY; return -EBUSY;
} }
break; break;
case SIMATIC_IPC_DEVICE_127E:
res = &simatic_ipc_led_mem_res;
ipcled = simatic_ipc_leds_mem;
type = IORESOURCE_MEM;
err = p2sb_bar(NULL, 0, res);
if (err)
return err;
/* do the final address calculation */
res->start = res->start + (0xC5 << 16);
res->end = res->start + SZ_4K - 1;
simatic_ipc_led_memory = devm_ioremap_resource(dev, res);
if (IS_ERR(simatic_ipc_led_memory))
return PTR_ERR(simatic_ipc_led_memory);
/* initialize power/watchdog LED */
reg = simatic_ipc_led_memory + 0x500 + 0x1D8; /* PM_WDT_OUT */
val = readl(reg);
writel(val & ~1, reg);
reg = simatic_ipc_led_memory + 0x500 + 0x1C0; /* PM_BIOS_BOOT_N */
val = readl(reg);
writel(val | 1, reg);
break;
default: default:
return -ENODEV; return -ENODEV;
} }
while (ipcled->value) { while (ipcled->value) {
cdev = &ipcled->cdev; cdev = &ipcled->cdev;
if (type == IORESOURCE_MEM) { cdev->brightness_set = simatic_ipc_led_set_io;
cdev->brightness_set = simatic_ipc_led_set_mem; cdev->brightness_get = simatic_ipc_led_get_io;
cdev->brightness_get = simatic_ipc_led_get_mem;
} else {
cdev->brightness_set = simatic_ipc_led_set_io;
cdev->brightness_get = simatic_ipc_led_get_io;
}
cdev->max_brightness = LED_ON; cdev->max_brightness = LED_ON;
cdev->name = ipcled->name; cdev->name = ipcled->name;
......
...@@ -51,6 +51,7 @@ static int register_platform_devices(u32 station_id) ...@@ -51,6 +51,7 @@ static int register_platform_devices(u32 station_id)
{ {
u8 ledmode = SIMATIC_IPC_DEVICE_NONE; u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
u8 wdtmode = SIMATIC_IPC_DEVICE_NONE; u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
char *pdevname = KBUILD_MODNAME "_leds";
int i; int i;
platform_data.devmode = SIMATIC_IPC_DEVICE_NONE; platform_data.devmode = SIMATIC_IPC_DEVICE_NONE;
...@@ -64,10 +65,12 @@ static int register_platform_devices(u32 station_id) ...@@ -64,10 +65,12 @@ static int register_platform_devices(u32 station_id)
} }
if (ledmode != SIMATIC_IPC_DEVICE_NONE) { if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
if (ledmode == SIMATIC_IPC_DEVICE_127E)
pdevname = KBUILD_MODNAME "_leds_gpio";
platform_data.devmode = ledmode; platform_data.devmode = ledmode;
ipc_led_platform_device = ipc_led_platform_device =
platform_device_register_data(NULL, platform_device_register_data(NULL,
KBUILD_MODNAME "_leds", PLATFORM_DEVID_NONE, pdevname, PLATFORM_DEVID_NONE,
&platform_data, &platform_data,
sizeof(struct simatic_ipc_platform)); sizeof(struct simatic_ipc_platform));
if (IS_ERR(ipc_led_platform_device)) if (IS_ERR(ipc_led_platform_device))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment