linux/drivers/mfd/kempld-core.c
Michael Brunner 87bfb48f34 mfd: kempld-core: Don't replace resources provided by ACPI
The current implementation to retrieve ACPI resources is faulty
and may cause issues that even can lead to non-booting systems.

When adding data from an ACPI device, the resources are already
assigned to the platform device. Therefore there is no need to
retrieve the resource list from ACPI and manually assign it to
the platform device. Also there shouldn't be any BIOS in the wild
anymore, that does not have resources added to the KEMPLD ACPI
data.

In particular this fixes an issue where the retrieval of the
resource list using /proc/ioports is disturbed and does not list
the assigned resource for the kempld device or even no resources
at all.
On some distributions this also leads to problems during system
initialization (e.g. with udev) and causes the system to not
boot at all.

I have reproduced the issue with the following kernel versions:
    5.10.209
    5.15.148
    6.1.25
    6.6.17
    6.7.5
    6.8-rc5

The patch applies to all of those versions and seems to resolve
the issue.

Signed-off-by: Michael Brunner <michael.brunner@kontron.com>
Link: https://lore.kernel.org/r/af8756be81c9062f9543d2e5d9373cf5e7877b1e.camel@kontron.com
Signed-off-by: Lee Jones <lee@kernel.org>
2024-02-23 17:14:14 +00:00

945 lines
25 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Kontron PLD MFD core driver
*
* Copyright (c) 2010-2013 Kontron Europe GmbH
* Author: Michael Brunner <michael.brunner@kontron.com>
*/
#include <linux/platform_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/kempld.h>
#include <linux/module.h>
#include <linux/dmi.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/acpi.h>
#define MAX_ID_LEN 4
static char force_device_id[MAX_ID_LEN + 1] = "";
module_param_string(force_device_id, force_device_id,
sizeof(force_device_id), 0);
MODULE_PARM_DESC(force_device_id, "Override detected product");
/*
* Get hardware mutex to block firmware from accessing the pld.
* It is possible for the firmware may hold the mutex for an extended length of
* time. This function will block until access has been granted.
*/
static void kempld_get_hardware_mutex(struct kempld_device_data *pld)
{
/* The mutex bit will read 1 until access has been granted */
while (ioread8(pld->io_index) & KEMPLD_MUTEX_KEY)
usleep_range(1000, 3000);
}
static void kempld_release_hardware_mutex(struct kempld_device_data *pld)
{
/* The harware mutex is released when 1 is written to the mutex bit. */
iowrite8(KEMPLD_MUTEX_KEY, pld->io_index);
}
static int kempld_get_info_generic(struct kempld_device_data *pld)
{
u16 version;
u8 spec;
kempld_get_mutex(pld);
version = kempld_read16(pld, KEMPLD_VERSION);
spec = kempld_read8(pld, KEMPLD_SPEC);
pld->info.buildnr = kempld_read16(pld, KEMPLD_BUILDNR);
pld->info.minor = KEMPLD_VERSION_GET_MINOR(version);
pld->info.major = KEMPLD_VERSION_GET_MAJOR(version);
pld->info.number = KEMPLD_VERSION_GET_NUMBER(version);
pld->info.type = KEMPLD_VERSION_GET_TYPE(version);
if (spec == 0xff) {
pld->info.spec_minor = 0;
pld->info.spec_major = 1;
} else {
pld->info.spec_minor = KEMPLD_SPEC_GET_MINOR(spec);
pld->info.spec_major = KEMPLD_SPEC_GET_MAJOR(spec);
}
if (pld->info.spec_major > 0)
pld->feature_mask = kempld_read16(pld, KEMPLD_FEATURE);
else
pld->feature_mask = 0;
kempld_release_mutex(pld);
return 0;
}
enum kempld_cells {
KEMPLD_I2C = 0,
KEMPLD_WDT,
KEMPLD_GPIO,
KEMPLD_UART,
};
static const char *kempld_dev_names[] = {
[KEMPLD_I2C] = "kempld-i2c",
[KEMPLD_WDT] = "kempld-wdt",
[KEMPLD_GPIO] = "kempld-gpio",
[KEMPLD_UART] = "kempld-uart",
};
#define KEMPLD_MAX_DEVS ARRAY_SIZE(kempld_dev_names)
static int kempld_register_cells_generic(struct kempld_device_data *pld)
{
struct mfd_cell devs[KEMPLD_MAX_DEVS] = {};
int i = 0;
if (pld->feature_mask & KEMPLD_FEATURE_BIT_I2C)
devs[i++].name = kempld_dev_names[KEMPLD_I2C];
if (pld->feature_mask & KEMPLD_FEATURE_BIT_WATCHDOG)
devs[i++].name = kempld_dev_names[KEMPLD_WDT];
if (pld->feature_mask & KEMPLD_FEATURE_BIT_GPIO)
devs[i++].name = kempld_dev_names[KEMPLD_GPIO];
if (pld->feature_mask & KEMPLD_FEATURE_MASK_UART)
devs[i++].name = kempld_dev_names[KEMPLD_UART];
return mfd_add_devices(pld->dev, -1, devs, i, NULL, 0, NULL);
}
static struct resource kempld_ioresource = {
.start = KEMPLD_IOINDEX,
.end = KEMPLD_IODATA,
.flags = IORESOURCE_IO,
};
static const struct kempld_platform_data kempld_platform_data_generic = {
.pld_clock = KEMPLD_CLK,
.ioresource = &kempld_ioresource,
.get_hardware_mutex = kempld_get_hardware_mutex,
.release_hardware_mutex = kempld_release_hardware_mutex,
.get_info = kempld_get_info_generic,
.register_cells = kempld_register_cells_generic,
};
static struct platform_device *kempld_pdev;
static int kempld_create_platform_device(const struct dmi_system_id *id)
{
const struct kempld_platform_data *pdata = id->driver_data;
int ret;
kempld_pdev = platform_device_alloc("kempld", -1);
if (!kempld_pdev)
return -ENOMEM;
ret = platform_device_add_data(kempld_pdev, pdata, sizeof(*pdata));
if (ret)
goto err;
ret = platform_device_add_resources(kempld_pdev, pdata->ioresource, 1);
if (ret)
goto err;
ret = platform_device_add(kempld_pdev);
if (ret)
goto err;
return 0;
err:
platform_device_put(kempld_pdev);
return ret;
}
/**
* kempld_read8 - read 8 bit register
* @pld: kempld_device_data structure describing the PLD
* @index: register index on the chip
*
* kempld_get_mutex must be called prior to calling this function.
*/
u8 kempld_read8(struct kempld_device_data *pld, u8 index)
{
iowrite8(index, pld->io_index);
return ioread8(pld->io_data);
}
EXPORT_SYMBOL_GPL(kempld_read8);
/**
* kempld_write8 - write 8 bit register
* @pld: kempld_device_data structure describing the PLD
* @index: register index on the chip
* @data: new register value
*
* kempld_get_mutex must be called prior to calling this function.
*/
void kempld_write8(struct kempld_device_data *pld, u8 index, u8 data)
{
iowrite8(index, pld->io_index);
iowrite8(data, pld->io_data);
}
EXPORT_SYMBOL_GPL(kempld_write8);
/**
* kempld_read16 - read 16 bit register
* @pld: kempld_device_data structure describing the PLD
* @index: register index on the chip
*
* kempld_get_mutex must be called prior to calling this function.
*/
u16 kempld_read16(struct kempld_device_data *pld, u8 index)
{
return kempld_read8(pld, index) | kempld_read8(pld, index + 1) << 8;
}
EXPORT_SYMBOL_GPL(kempld_read16);
/**
* kempld_write16 - write 16 bit register
* @pld: kempld_device_data structure describing the PLD
* @index: register index on the chip
* @data: new register value
*
* kempld_get_mutex must be called prior to calling this function.
*/
void kempld_write16(struct kempld_device_data *pld, u8 index, u16 data)
{
kempld_write8(pld, index, (u8)data);
kempld_write8(pld, index + 1, (u8)(data >> 8));
}
EXPORT_SYMBOL_GPL(kempld_write16);
/**
* kempld_read32 - read 32 bit register
* @pld: kempld_device_data structure describing the PLD
* @index: register index on the chip
*
* kempld_get_mutex must be called prior to calling this function.
*/
u32 kempld_read32(struct kempld_device_data *pld, u8 index)
{
return kempld_read16(pld, index) | kempld_read16(pld, index + 2) << 16;
}
EXPORT_SYMBOL_GPL(kempld_read32);
/**
* kempld_write32 - write 32 bit register
* @pld: kempld_device_data structure describing the PLD
* @index: register index on the chip
* @data: new register value
*
* kempld_get_mutex must be called prior to calling this function.
*/
void kempld_write32(struct kempld_device_data *pld, u8 index, u32 data)
{
kempld_write16(pld, index, (u16)data);
kempld_write16(pld, index + 2, (u16)(data >> 16));
}
EXPORT_SYMBOL_GPL(kempld_write32);
/**
* kempld_get_mutex - acquire PLD mutex
* @pld: kempld_device_data structure describing the PLD
*/
void kempld_get_mutex(struct kempld_device_data *pld)
{
const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
mutex_lock(&pld->lock);
pdata->get_hardware_mutex(pld);
}
EXPORT_SYMBOL_GPL(kempld_get_mutex);
/**
* kempld_release_mutex - release PLD mutex
* @pld: kempld_device_data structure describing the PLD
*/
void kempld_release_mutex(struct kempld_device_data *pld)
{
const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
pdata->release_hardware_mutex(pld);
mutex_unlock(&pld->lock);
}
EXPORT_SYMBOL_GPL(kempld_release_mutex);
/**
* kempld_get_info - update device specific information
* @pld: kempld_device_data structure describing the PLD
*
* This function calls the configured board specific kempld_get_info_XXXX
* function which is responsible for gathering information about the specific
* hardware. The information is then stored within the pld structure.
*/
static int kempld_get_info(struct kempld_device_data *pld)
{
int ret;
const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
char major, minor;
ret = pdata->get_info(pld);
if (ret)
return ret;
/* The Kontron PLD firmware version string has the following format:
* Pwxy.zzzz
* P: Fixed
* w: PLD number - 1 hex digit
* x: Major version - 1 alphanumerical digit (0-9A-V)
* y: Minor version - 1 alphanumerical digit (0-9A-V)
* zzzz: Build number - 4 zero padded hex digits */
if (pld->info.major < 10)
major = pld->info.major + '0';
else
major = (pld->info.major - 10) + 'A';
if (pld->info.minor < 10)
minor = pld->info.minor + '0';
else
minor = (pld->info.minor - 10) + 'A';
ret = scnprintf(pld->info.version, sizeof(pld->info.version),
"P%X%c%c.%04X", pld->info.number, major, minor,
pld->info.buildnr);
if (ret < 0)
return ret;
return 0;
}
/*
* kempld_register_cells - register cell drivers
*
* This function registers cell drivers for the detected hardware by calling
* the configured kempld_register_cells_XXXX function which is responsible
* to detect and register the needed cell drivers.
*/
static int kempld_register_cells(struct kempld_device_data *pld)
{
const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
return pdata->register_cells(pld);
}
static const char *kempld_get_type_string(struct kempld_device_data *pld)
{
const char *version_type;
switch (pld->info.type) {
case 0:
version_type = "release";
break;
case 1:
version_type = "debug";
break;
case 2:
version_type = "custom";
break;
default:
version_type = "unspecified";
break;
}
return version_type;
}
static ssize_t pld_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct kempld_device_data *pld = dev_get_drvdata(dev);
return sysfs_emit(buf, "%s\n", pld->info.version);
}
static ssize_t pld_specification_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct kempld_device_data *pld = dev_get_drvdata(dev);
return sysfs_emit(buf, "%d.%d\n", pld->info.spec_major, pld->info.spec_minor);
}
static ssize_t pld_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct kempld_device_data *pld = dev_get_drvdata(dev);
return sysfs_emit(buf, "%s\n", kempld_get_type_string(pld));
}
static DEVICE_ATTR_RO(pld_version);
static DEVICE_ATTR_RO(pld_specification);
static DEVICE_ATTR_RO(pld_type);
static struct attribute *pld_attributes[] = {
&dev_attr_pld_version.attr,
&dev_attr_pld_specification.attr,
&dev_attr_pld_type.attr,
NULL
};
static const struct attribute_group pld_attr_group = {
.attrs = pld_attributes,
};
static int kempld_detect_device(struct kempld_device_data *pld)
{
u8 index_reg;
int ret;
mutex_lock(&pld->lock);
/* Check for empty IO space */
index_reg = ioread8(pld->io_index);
if (index_reg == 0xff && ioread8(pld->io_data) == 0xff) {
mutex_unlock(&pld->lock);
return -ENODEV;
}
/* Release hardware mutex if acquired */
if (!(index_reg & KEMPLD_MUTEX_KEY)) {
iowrite8(KEMPLD_MUTEX_KEY, pld->io_index);
/* PXT and COMe-cPC2 boards may require a second release */
iowrite8(KEMPLD_MUTEX_KEY, pld->io_index);
}
mutex_unlock(&pld->lock);
ret = kempld_get_info(pld);
if (ret)
return ret;
dev_info(pld->dev, "Found Kontron PLD - %s (%s), spec %d.%d\n",
pld->info.version, kempld_get_type_string(pld),
pld->info.spec_major, pld->info.spec_minor);
ret = sysfs_create_group(&pld->dev->kobj, &pld_attr_group);
if (ret)
return ret;
ret = kempld_register_cells(pld);
if (ret)
sysfs_remove_group(&pld->dev->kobj, &pld_attr_group);
return ret;
}
#ifdef CONFIG_ACPI
static int kempld_get_acpi_data(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct kempld_platform_data *pdata;
int ret;
pdata = acpi_device_get_match_data(dev);
ret = platform_device_add_data(pdev, pdata,
sizeof(struct kempld_platform_data));
return ret;
}
#else
static int kempld_get_acpi_data(struct platform_device *pdev)
{
return -ENODEV;
}
#endif /* CONFIG_ACPI */
static int kempld_probe(struct platform_device *pdev)
{
const struct kempld_platform_data *pdata;
struct device *dev = &pdev->dev;
struct kempld_device_data *pld;
struct resource *ioport;
int ret;
if (kempld_pdev == NULL) {
/*
* No kempld_pdev device has been registered in kempld_init,
* so we seem to be probing an ACPI platform device.
*/
ret = kempld_get_acpi_data(pdev);
if (ret)
return ret;
} else if (kempld_pdev != pdev) {
/*
* The platform device we are probing is not the one we
* registered in kempld_init using the DMI table, so this one
* comes from ACPI.
* As we can only probe one - abort here and use the DMI
* based one instead.
*/
dev_notice(dev, "platform device exists - not using ACPI\n");
return -ENODEV;
}
pdata = dev_get_platdata(dev);
pld = devm_kzalloc(dev, sizeof(*pld), GFP_KERNEL);
if (!pld)
return -ENOMEM;
ioport = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!ioport)
return -EINVAL;
pld->io_base = devm_ioport_map(dev, ioport->start,
resource_size(ioport));
if (!pld->io_base)
return -ENOMEM;
pld->io_index = pld->io_base;
pld->io_data = pld->io_base + 1;
pld->pld_clock = pdata->pld_clock;
pld->dev = dev;
mutex_init(&pld->lock);
platform_set_drvdata(pdev, pld);
return kempld_detect_device(pld);
}
static void kempld_remove(struct platform_device *pdev)
{
struct kempld_device_data *pld = platform_get_drvdata(pdev);
const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
sysfs_remove_group(&pld->dev->kobj, &pld_attr_group);
mfd_remove_devices(&pdev->dev);
pdata->release_hardware_mutex(pld);
}
#ifdef CONFIG_ACPI
static const struct acpi_device_id kempld_acpi_table[] = {
{ "KEM0000", (kernel_ulong_t)&kempld_platform_data_generic },
{ "KEM0001", (kernel_ulong_t)&kempld_platform_data_generic },
{}
};
MODULE_DEVICE_TABLE(acpi, kempld_acpi_table);
#endif
static struct platform_driver kempld_driver = {
.driver = {
.name = "kempld",
.acpi_match_table = ACPI_PTR(kempld_acpi_table),
},
.probe = kempld_probe,
.remove_new = kempld_remove,
};
static const struct dmi_system_id kempld_dmi_table[] __initconst = {
{
.ident = "BBD6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bBD"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "BBL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bBL6"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "BDV7",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bDV7"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "BHL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bHL6"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "BKL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bKL6"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "BSL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bSL6"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CAL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cAL"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CBL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cBL6"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CBW6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cBW6"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CCR2",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bIP2"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CCR6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bIP6"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CDV7",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cDV7"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CHL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cHL6"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CHR2",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "ETXexpress-SC T2"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CHR2",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "ETXe-SC T2"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CHR2",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bSC2"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CHR6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "ETXexpress-SC T6"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CHR6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "ETXe-SC T6"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CHR6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bSC6"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CKL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cKL6"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CNTG",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "ETXexpress-PC"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CNTG",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bPC2"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CNTX",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "PXT"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CSL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cSL6"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "CVV6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cBT"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "FRI2",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BIOS_VERSION, "FRI2"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "FRI2",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Fish River Island II"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "A203",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "KBox A-203"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "M4A1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-m4AL"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "MAL1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-mAL10"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "MAPL",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "mITX-APL"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "MBR1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "ETX-OH"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "MVV1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-mBT"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "NTC1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "nanoETXexpress-TT"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "NTC1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "nETXe-TT"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "NTC1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-mTT"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "NUP1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-mCT"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "PAPL",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "pITX-APL"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "SXAL",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "SMARC-sXAL"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "SXAL4",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "SMARC-sXA4"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "UNP1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "microETXexpress-DC"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "UNP1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cDC2"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "UNTG",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "microETXexpress-PC"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "UNTG",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cPC2"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "UUP6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cCT6"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "UTH6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cTH6"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
.ident = "Q7AL",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "Qseven-Q7AL"),
},
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
},
{}
};
MODULE_DEVICE_TABLE(dmi, kempld_dmi_table);
static int __init kempld_init(void)
{
const struct dmi_system_id *id;
if (force_device_id[0]) {
for (id = kempld_dmi_table;
id->matches[0].slot != DMI_NONE; id++)
if (strstr(id->ident, force_device_id))
if (id->callback && !id->callback(id))
break;
if (id->matches[0].slot == DMI_NONE)
return -ENODEV;
} else {
dmi_check_system(kempld_dmi_table);
}
return platform_driver_register(&kempld_driver);
}
static void __exit kempld_exit(void)
{
if (kempld_pdev)
platform_device_unregister(kempld_pdev);
platform_driver_unregister(&kempld_driver);
}
module_init(kempld_init);
module_exit(kempld_exit);
MODULE_DESCRIPTION("KEM PLD Core Driver");
MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:kempld-core");