Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,64 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Siemens X86 Platform Specific Drivers
#
config SIEMENS_SIMATIC_IPC
tristate "Siemens Simatic IPC Class driver"
help
This Simatic IPC class driver is the central of several drivers. It
is mainly used for system identification, after which drivers in other
classes will take care of driving specifics of those machines.
i.e. LEDs and watchdog.
To compile this driver as a module, choose M here: the module
will be called simatic-ipc.
config SIEMENS_SIMATIC_IPC_BATT
tristate "CMOS battery driver for Siemens Simatic IPCs"
default SIEMENS_SIMATIC_IPC
depends on HWMON
depends on SIEMENS_SIMATIC_IPC
help
This option enables support for monitoring the voltage of the CMOS
batteries of several Industrial PCs from Siemens.
To compile this driver as a module, choose M here: the module
will be called simatic-ipc-batt.
config SIEMENS_SIMATIC_IPC_BATT_APOLLOLAKE
tristate "CMOS Battery monitoring for Simatic IPCs based on Apollo Lake GPIO"
default SIEMENS_SIMATIC_IPC_BATT
depends on PINCTRL_BROXTON
depends on SIEMENS_SIMATIC_IPC_BATT
help
This option enables CMOS battery monitoring for Simatic Industrial PCs
from Siemens based on Apollo Lake GPIO.
To compile this driver as a module, choose M here: the module
will be called simatic-ipc-batt-apollolake.
config SIEMENS_SIMATIC_IPC_BATT_ELKHARTLAKE
tristate "CMOS Battery monitoring for Simatic IPCs based on Elkhart Lake GPIO"
default SIEMENS_SIMATIC_IPC_BATT
depends on PINCTRL_ELKHARTLAKE
depends on SIEMENS_SIMATIC_IPC_BATT
help
This option enables CMOS battery monitoring for Simatic Industrial PCs
from Siemens based on Elkhart Lake GPIO.
To compile this driver as a module, choose M here: the module
will be called simatic-ipc-batt-elkhartlake.
config SIEMENS_SIMATIC_IPC_BATT_F7188X
tristate "CMOS Battery monitoring for Simatic IPCs based on Nuvoton GPIO"
default SIEMENS_SIMATIC_IPC_BATT
depends on GPIO_F7188X
depends on PINCTRL_ALDERLAKE
depends on SIEMENS_SIMATIC_IPC_BATT
help
This option enables CMOS battery monitoring for Simatic Industrial PCs
from Siemens based on Nuvoton GPIO.
To compile this driver as a module, choose M here: the module
will be called simatic-ipc-batt-f7188x.
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for linux/drivers/platform/x86/siemens
# Siemens x86 Platform-Specific Drivers
#
obj-$(CONFIG_SIEMENS_SIMATIC_IPC) += simatic-ipc.o
obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT) += simatic-ipc-batt.o
obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_APOLLOLAKE) += simatic-ipc-batt-apollolake.o
obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_ELKHARTLAKE) += simatic-ipc-batt-elkhartlake.o
obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_F7188X) += simatic-ipc-batt-f7188x.o
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Siemens SIMATIC IPC driver for CMOS battery monitoring
*
* Copyright (c) Siemens AG, 2023
*
* Authors:
* Henning Schild <henning.schild@siemens.com>
*/
#include <linux/gpio/machine.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "simatic-ipc-batt.h"
static struct gpiod_lookup_table simatic_ipc_batt_gpio_table_127e = {
.table = {
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 55, NULL, 0, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 61, NULL, 1, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("apollolake-pinctrl.1", 41, NULL, 2, GPIO_ACTIVE_HIGH),
{} /* Terminating entry */
},
};
static void simatic_ipc_batt_apollolake_remove(struct platform_device *pdev)
{
simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_127e);
}
static int simatic_ipc_batt_apollolake_probe(struct platform_device *pdev)
{
return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_127e);
}
static struct platform_driver simatic_ipc_batt_driver = {
.probe = simatic_ipc_batt_apollolake_probe,
.remove = simatic_ipc_batt_apollolake_remove,
.driver = {
.name = KBUILD_MODNAME,
},
};
module_platform_driver(simatic_ipc_batt_driver);
MODULE_DESCRIPTION("CMOS Battery monitoring for Simatic IPCs based on Apollo Lake GPIO");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" KBUILD_MODNAME);
MODULE_SOFTDEP("pre: simatic-ipc-batt platform:apollolake-pinctrl");
MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Siemens SIMATIC IPC driver for CMOS battery monitoring
*
* Copyright (c) Siemens AG, 2023
*
* Authors:
* Henning Schild <henning.schild@siemens.com>
*/
#include <linux/gpio/machine.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "simatic-ipc-batt.h"
static struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_21a = {
.table = {
GPIO_LOOKUP_IDX("INTC1020:04", 18, NULL, 0, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("INTC1020:04", 19, NULL, 1, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("INTC1020:01", 66, NULL, 2, GPIO_ACTIVE_HIGH),
{} /* Terminating entry */
},
};
static void simatic_ipc_batt_elkhartlake_remove(struct platform_device *pdev)
{
simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_bx_21a);
}
static int simatic_ipc_batt_elkhartlake_probe(struct platform_device *pdev)
{
return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_bx_21a);
}
static struct platform_driver simatic_ipc_batt_driver = {
.probe = simatic_ipc_batt_elkhartlake_probe,
.remove = simatic_ipc_batt_elkhartlake_remove,
.driver = {
.name = KBUILD_MODNAME,
},
};
module_platform_driver(simatic_ipc_batt_driver);
MODULE_DESCRIPTION("CMOS Battery monitoring for Simatic IPCs based on Elkhart Lake GPIO");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" KBUILD_MODNAME);
MODULE_SOFTDEP("pre: simatic-ipc-batt platform:elkhartlake-pinctrl");
MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Siemens SIMATIC IPC driver for CMOS battery monitoring
*
* Copyright (c) Siemens AG, 2023
*
* Authors:
* Henning Schild <henning.schild@siemens.com>
*/
#include <linux/gpio/machine.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/platform_data/x86/simatic-ipc-base.h>
#include "simatic-ipc-batt.h"
static struct gpiod_lookup_table *batt_lookup_table;
static struct gpiod_lookup_table simatic_ipc_batt_gpio_table_227g = {
.table = {
GPIO_LOOKUP_IDX("gpio-f7188x-7", 6, NULL, 0, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("gpio-f7188x-7", 5, NULL, 1, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("INTC1020:01", 66, NULL, 2, GPIO_ACTIVE_HIGH),
{} /* Terminating entry */
},
};
static struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_39a = {
.table = {
GPIO_LOOKUP_IDX("gpio-f7188x-6", 4, NULL, 0, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("gpio-f7188x-6", 3, NULL, 1, GPIO_ACTIVE_HIGH),
{} /* Terminating entry */
},
};
static struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_59a = {
.table = {
GPIO_LOOKUP_IDX("gpio-f7188x-7", 6, NULL, 0, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("gpio-f7188x-7", 5, NULL, 1, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("INTC1056:00", 438, NULL, 2, GPIO_ACTIVE_HIGH),
{} /* Terminating entry */
}
};
static void simatic_ipc_batt_f7188x_remove(struct platform_device *pdev)
{
simatic_ipc_batt_remove(pdev, batt_lookup_table);
}
static int simatic_ipc_batt_f7188x_probe(struct platform_device *pdev)
{
const struct simatic_ipc_platform *plat = pdev->dev.platform_data;
switch (plat->devmode) {
case SIMATIC_IPC_DEVICE_227G:
batt_lookup_table = &simatic_ipc_batt_gpio_table_227g;
break;
case SIMATIC_IPC_DEVICE_BX_39A:
batt_lookup_table = &simatic_ipc_batt_gpio_table_bx_39a;
break;
case SIMATIC_IPC_DEVICE_BX_59A:
batt_lookup_table = &simatic_ipc_batt_gpio_table_bx_59a;
break;
default:
return -ENODEV;
}
return simatic_ipc_batt_probe(pdev, batt_lookup_table);
}
static struct platform_driver simatic_ipc_batt_driver = {
.probe = simatic_ipc_batt_f7188x_probe,
.remove = simatic_ipc_batt_f7188x_remove,
.driver = {
.name = KBUILD_MODNAME,
},
};
module_platform_driver(simatic_ipc_batt_driver);
MODULE_DESCRIPTION("CMOS Battery monitoring for Simatic IPCs based on Nuvoton GPIO");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" KBUILD_MODNAME);
MODULE_SOFTDEP("pre: simatic-ipc-batt gpio_f7188x platform:elkhartlake-pinctrl platform:alderlake-pinctrl");
MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
@@ -0,0 +1,253 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Siemens SIMATIC IPC driver for CMOS battery monitoring
*
* Copyright (c) Siemens AG, 2023
*
* Authors:
* Gerd Haeussler <gerd.haeussler.ext@siemens.com>
* Henning Schild <henning.schild@siemens.com>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/gpio/machine.h>
#include <linux/gpio/consumer.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/platform_data/x86/simatic-ipc-base.h>
#include <linux/sizes.h>
#include "simatic-ipc-batt.h"
#define BATT_DELAY_MS (1000 * 60 * 60 * 24) /* 24 h delay */
#define SIMATIC_IPC_BATT_LEVEL_FULL 3000
#define SIMATIC_IPC_BATT_LEVEL_CRIT 2750
#define SIMATIC_IPC_BATT_LEVEL_EMPTY 0
static struct simatic_ipc_batt {
u8 devmode;
long current_state;
struct gpio_desc *gpios[3];
unsigned long last_updated_jiffies;
} priv;
static long simatic_ipc_batt_read_gpio(void)
{
long r = SIMATIC_IPC_BATT_LEVEL_FULL;
if (priv.gpios[2]) {
gpiod_set_value(priv.gpios[2], 1);
msleep(150);
}
if (gpiod_get_value_cansleep(priv.gpios[0]))
r = SIMATIC_IPC_BATT_LEVEL_EMPTY;
else if (gpiod_get_value_cansleep(priv.gpios[1]))
r = SIMATIC_IPC_BATT_LEVEL_CRIT;
if (priv.gpios[2])
gpiod_set_value(priv.gpios[2], 0);
return r;
}
#define SIMATIC_IPC_BATT_PORT_BASE 0x404D
static struct resource simatic_ipc_batt_io_res =
DEFINE_RES_IO_NAMED(SIMATIC_IPC_BATT_PORT_BASE, SZ_1, KBUILD_MODNAME);
static long simatic_ipc_batt_read_io(struct device *dev)
{
long r = SIMATIC_IPC_BATT_LEVEL_FULL;
struct resource *res = &simatic_ipc_batt_io_res;
u8 val;
if (!request_muxed_region(res->start, resource_size(res), res->name)) {
dev_err(dev, "Unable to register IO resource at %pR\n", res);
return -EBUSY;
}
val = inb(SIMATIC_IPC_BATT_PORT_BASE);
release_region(simatic_ipc_batt_io_res.start, resource_size(&simatic_ipc_batt_io_res));
if (val & (1 << 7))
r = SIMATIC_IPC_BATT_LEVEL_EMPTY;
else if (val & (1 << 6))
r = SIMATIC_IPC_BATT_LEVEL_CRIT;
return r;
}
static long simatic_ipc_batt_read_value(struct device *dev)
{
unsigned long next_update;
next_update = priv.last_updated_jiffies + msecs_to_jiffies(BATT_DELAY_MS);
if (time_after(jiffies, next_update) || !priv.last_updated_jiffies) {
if (priv.devmode == SIMATIC_IPC_DEVICE_227E)
priv.current_state = simatic_ipc_batt_read_io(dev);
else
priv.current_state = simatic_ipc_batt_read_gpio();
priv.last_updated_jiffies = jiffies;
if (priv.current_state < SIMATIC_IPC_BATT_LEVEL_FULL)
dev_warn(dev, "CMOS battery needs to be replaced.\n");
}
return priv.current_state;
}
static int simatic_ipc_batt_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
switch (attr) {
case hwmon_in_input:
*val = simatic_ipc_batt_read_value(dev);
break;
case hwmon_in_lcrit:
*val = SIMATIC_IPC_BATT_LEVEL_CRIT;
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static umode_t simatic_ipc_batt_is_visible(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
if (attr == hwmon_in_input || attr == hwmon_in_lcrit)
return 0444;
return 0;
}
static const struct hwmon_ops simatic_ipc_batt_ops = {
.is_visible = simatic_ipc_batt_is_visible,
.read = simatic_ipc_batt_read,
};
static const struct hwmon_channel_info *simatic_ipc_batt_info[] = {
HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LCRIT),
NULL
};
static const struct hwmon_chip_info simatic_ipc_batt_chip_info = {
.ops = &simatic_ipc_batt_ops,
.info = simatic_ipc_batt_info,
};
void simatic_ipc_batt_remove(struct platform_device *pdev, struct gpiod_lookup_table *table)
{
gpiod_remove_lookup_table(table);
}
EXPORT_SYMBOL_GPL(simatic_ipc_batt_remove);
int simatic_ipc_batt_probe(struct platform_device *pdev, struct gpiod_lookup_table *table)
{
struct simatic_ipc_platform *plat;
struct device *dev = &pdev->dev;
struct device *hwmon_dev;
unsigned long flags;
int err;
plat = pdev->dev.platform_data;
priv.devmode = plat->devmode;
switch (priv.devmode) {
case SIMATIC_IPC_DEVICE_127E:
case SIMATIC_IPC_DEVICE_227G:
case SIMATIC_IPC_DEVICE_BX_39A:
case SIMATIC_IPC_DEVICE_BX_21A:
case SIMATIC_IPC_DEVICE_BX_59A:
table->dev_id = dev_name(dev);
gpiod_add_lookup_table(table);
break;
case SIMATIC_IPC_DEVICE_227E:
goto nogpio;
default:
return -ENODEV;
}
priv.gpios[0] = devm_gpiod_get_index(dev, "CMOSBattery empty", 0, GPIOD_IN);
if (IS_ERR(priv.gpios[0])) {
err = PTR_ERR(priv.gpios[0]);
priv.gpios[0] = NULL;
goto out;
}
priv.gpios[1] = devm_gpiod_get_index(dev, "CMOSBattery low", 1, GPIOD_IN);
if (IS_ERR(priv.gpios[1])) {
err = PTR_ERR(priv.gpios[1]);
priv.gpios[1] = NULL;
goto out;
}
if (table->table[2].key) {
flags = GPIOD_OUT_HIGH;
if (priv.devmode == SIMATIC_IPC_DEVICE_BX_21A ||
priv.devmode == SIMATIC_IPC_DEVICE_BX_59A)
flags = GPIOD_OUT_LOW;
priv.gpios[2] = devm_gpiod_get_index(dev, "CMOSBattery meter", 2, flags);
if (IS_ERR(priv.gpios[2])) {
err = PTR_ERR(priv.gpios[2]);
priv.gpios[2] = NULL;
goto out;
}
} else {
priv.gpios[2] = NULL;
}
nogpio:
hwmon_dev = devm_hwmon_device_register_with_info(dev, KBUILD_MODNAME,
&priv,
&simatic_ipc_batt_chip_info,
NULL);
if (IS_ERR(hwmon_dev)) {
err = PTR_ERR(hwmon_dev);
goto out;
}
/* warn about aging battery even if userspace never reads hwmon */
simatic_ipc_batt_read_value(dev);
return 0;
out:
simatic_ipc_batt_remove(pdev, table);
return err;
}
EXPORT_SYMBOL_GPL(simatic_ipc_batt_probe);
static void simatic_ipc_batt_io_remove(struct platform_device *pdev)
{
simatic_ipc_batt_remove(pdev, NULL);
}
static int simatic_ipc_batt_io_probe(struct platform_device *pdev)
{
return simatic_ipc_batt_probe(pdev, NULL);
}
static struct platform_driver simatic_ipc_batt_driver = {
.probe = simatic_ipc_batt_io_probe,
.remove = simatic_ipc_batt_io_remove,
.driver = {
.name = KBUILD_MODNAME,
},
};
module_platform_driver(simatic_ipc_batt_driver);
MODULE_DESCRIPTION("CMOS core battery driver for Siemens Simatic IPCs");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" KBUILD_MODNAME);
MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Siemens SIMATIC IPC driver for CMOS battery monitoring
*
* Copyright (c) Siemens AG, 2023
*
* Author:
* Henning Schild <henning.schild@siemens.com>
*/
#ifndef _SIMATIC_IPC_BATT_H
#define _SIMATIC_IPC_BATT_H
int simatic_ipc_batt_probe(struct platform_device *pdev,
struct gpiod_lookup_table *table);
void simatic_ipc_batt_remove(struct platform_device *pdev,
struct gpiod_lookup_table *table);
#endif /* _SIMATIC_IPC_BATT_H */
@@ -0,0 +1,237 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Siemens SIMATIC IPC platform driver
*
* Copyright (c) Siemens AG, 2018-2023
*
* Authors:
* Henning Schild <henning.schild@siemens.com>
* Jan Kiszka <jan.kiszka@siemens.com>
* Gerd Haeussler <gerd.haeussler.ext@siemens.com>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/dmi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_data/x86/simatic-ipc.h>
#include <linux/platform_device.h>
static struct platform_device *ipc_led_platform_device;
static struct platform_device *ipc_wdt_platform_device;
static struct platform_device *ipc_batt_platform_device;
static const struct dmi_system_id simatic_ipc_whitelist[] = {
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"),
},
},
{}
};
static struct simatic_ipc_platform platform_data;
#define SIMATIC_IPC_MAX_EXTRA_MODULES 2
static struct {
u32 station_id;
u8 led_mode;
u8 wdt_mode;
u8 batt_mode;
char *extra_modules[SIMATIC_IPC_MAX_EXTRA_MODULES];
} device_modes[] = {
{SIMATIC_IPC_IPC127E,
SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_127E,
{ "emc1403", NULL }},
{SIMATIC_IPC_IPC227D,
SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE,
{ "emc1403", NULL }},
{SIMATIC_IPC_IPC227E,
SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E,
{ "emc1403", NULL }},
{SIMATIC_IPC_IPC227G,
SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G,
{ "nct6775", "w83627hf_wdt" }},
{SIMATIC_IPC_IPC277G,
SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G,
{ "nct6775", "w83627hf_wdt" }},
{SIMATIC_IPC_IPC277E,
SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E,
{ "emc1403", NULL }},
{SIMATIC_IPC_IPC427D,
SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE,
{ "emc1403", NULL }},
{SIMATIC_IPC_IPC427E,
SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE,
{ "emc1403", NULL }},
{SIMATIC_IPC_IPC477E,
SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE,
{ "emc1403", NULL }},
{SIMATIC_IPC_IPCBX_39A,
SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_39A,
{ "nct6775", "w83627hf_wdt" }},
{SIMATIC_IPC_IPCPX_39A,
SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_39A,
{ "nct6775", "w83627hf_wdt" }},
{SIMATIC_IPC_IPCBX_21A,
SIMATIC_IPC_DEVICE_BX_21A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_21A,
{ "emc1403", NULL }},
{SIMATIC_IPC_IPCBX_56A,
SIMATIC_IPC_DEVICE_BX_59A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_59A,
{ "emc1403", "w83627hf_wdt" }},
{SIMATIC_IPC_IPCBX_59A,
SIMATIC_IPC_DEVICE_BX_59A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_59A,
{ "emc1403", "w83627hf_wdt" }},
};
static int register_platform_devices(u32 station_id)
{
u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
u8 battmode = SIMATIC_IPC_DEVICE_NONE;
char *pdevname;
int i;
for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
if (device_modes[i].station_id == station_id) {
ledmode = device_modes[i].led_mode;
wdtmode = device_modes[i].wdt_mode;
battmode = device_modes[i].batt_mode;
break;
}
}
if (battmode != SIMATIC_IPC_DEVICE_NONE) {
pdevname = KBUILD_MODNAME "_batt";
if (battmode == SIMATIC_IPC_DEVICE_127E)
pdevname = KBUILD_MODNAME "_batt_apollolake";
if (battmode == SIMATIC_IPC_DEVICE_BX_21A)
pdevname = KBUILD_MODNAME "_batt_elkhartlake";
if (battmode == SIMATIC_IPC_DEVICE_227G ||
battmode == SIMATIC_IPC_DEVICE_BX_39A ||
battmode == SIMATIC_IPC_DEVICE_BX_59A)
pdevname = KBUILD_MODNAME "_batt_f7188x";
platform_data.devmode = battmode;
ipc_batt_platform_device =
platform_device_register_data(NULL, pdevname,
PLATFORM_DEVID_NONE, &platform_data,
sizeof(struct simatic_ipc_platform));
if (IS_ERR(ipc_batt_platform_device))
return PTR_ERR(ipc_batt_platform_device);
pr_debug("device=%s created\n",
ipc_batt_platform_device->name);
}
if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
pdevname = KBUILD_MODNAME "_leds";
if (ledmode == SIMATIC_IPC_DEVICE_127E)
pdevname = KBUILD_MODNAME "_leds_gpio_apollolake";
if (ledmode == SIMATIC_IPC_DEVICE_227G || ledmode == SIMATIC_IPC_DEVICE_BX_59A)
pdevname = KBUILD_MODNAME "_leds_gpio_f7188x";
if (ledmode == SIMATIC_IPC_DEVICE_BX_21A)
pdevname = KBUILD_MODNAME "_leds_gpio_elkhartlake";
platform_data.devmode = ledmode;
ipc_led_platform_device =
platform_device_register_data(NULL,
pdevname, PLATFORM_DEVID_NONE,
&platform_data,
sizeof(struct simatic_ipc_platform));
if (IS_ERR(ipc_led_platform_device))
return PTR_ERR(ipc_led_platform_device);
pr_debug("device=%s created\n",
ipc_led_platform_device->name);
}
if (wdtmode != SIMATIC_IPC_DEVICE_NONE) {
platform_data.devmode = wdtmode;
ipc_wdt_platform_device =
platform_device_register_data(NULL,
KBUILD_MODNAME "_wdt", PLATFORM_DEVID_NONE,
&platform_data,
sizeof(struct simatic_ipc_platform));
if (IS_ERR(ipc_wdt_platform_device))
return PTR_ERR(ipc_wdt_platform_device);
pr_debug("device=%s created\n",
ipc_wdt_platform_device->name);
}
if (ledmode == SIMATIC_IPC_DEVICE_NONE &&
wdtmode == SIMATIC_IPC_DEVICE_NONE &&
battmode == SIMATIC_IPC_DEVICE_NONE) {
pr_warn("unsupported IPC detected, station id=%08x\n",
station_id);
return -EINVAL;
}
return 0;
}
static void request_additional_modules(u32 station_id)
{
char **extra_modules = NULL;
int i;
for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
if (device_modes[i].station_id == station_id) {
extra_modules = device_modes[i].extra_modules;
break;
}
}
if (!extra_modules)
return;
for (i = 0; i < SIMATIC_IPC_MAX_EXTRA_MODULES; i++) {
if (extra_modules[i])
request_module(extra_modules[i]);
else
break;
}
}
static int __init simatic_ipc_init_module(void)
{
const struct dmi_system_id *match;
u32 station_id;
int err;
match = dmi_first_match(simatic_ipc_whitelist);
if (!match)
return 0;
err = dmi_walk(simatic_ipc_find_dmi_entry_helper, &station_id);
if (err || station_id == SIMATIC_IPC_INVALID_STATION_ID) {
pr_warn("DMI entry %d not found\n", SIMATIC_IPC_DMI_ENTRY_OEM);
return 0;
}
request_additional_modules(station_id);
return register_platform_devices(station_id);
}
static void __exit simatic_ipc_exit_module(void)
{
platform_device_unregister(ipc_led_platform_device);
ipc_led_platform_device = NULL;
platform_device_unregister(ipc_wdt_platform_device);
ipc_wdt_platform_device = NULL;
platform_device_unregister(ipc_batt_platform_device);
ipc_batt_platform_device = NULL;
}
module_init(simatic_ipc_init_module);
module_exit(simatic_ipc_exit_module);
MODULE_DESCRIPTION("Siemens SIMATIC IPC platform driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Gerd Haeussler <gerd.haeussler.ext@siemens.com>");
MODULE_ALIAS("dmi:*:svnSIEMENSAG:*");