Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Kontron PLD MFD core driver
  4 *
  5 * Copyright (c) 2010-2013 Kontron Europe GmbH
  6 * Author: Michael Brunner <michael.brunner@kontron.com>
  7 */
  8
  9#include <linux/err.h>
 10#include <linux/platform_device.h>
 11#include <linux/mfd/core.h>
 12#include <linux/mfd/kempld.h>
 13#include <linux/mod_devicetable.h>
 14#include <linux/module.h>
 15#include <linux/property.h>
 16#include <linux/dmi.h>
 17#include <linux/io.h>
 18#include <linux/delay.h>
 19#include <linux/sysfs.h>
 20
 21#define MAX_ID_LEN 4
 22static char force_device_id[MAX_ID_LEN + 1] = "";
 23module_param_string(force_device_id, force_device_id,
 24		    sizeof(force_device_id), 0);
 25MODULE_PARM_DESC(force_device_id, "Override detected product");
 26
 27/*
 28 * Get hardware mutex to block firmware from accessing the pld.
 29 * It is possible for the firmware may hold the mutex for an extended length of
 30 * time. This function will block until access has been granted.
 31 */
 32static void kempld_get_hardware_mutex(struct kempld_device_data *pld)
 33{
 34	/* The mutex bit will read 1 until access has been granted */
 35	while (ioread8(pld->io_index) & KEMPLD_MUTEX_KEY)
 36		usleep_range(1000, 3000);
 37}
 38
 39static void kempld_release_hardware_mutex(struct kempld_device_data *pld)
 40{
 41	/* The harware mutex is released when 1 is written to the mutex bit. */
 42	iowrite8(KEMPLD_MUTEX_KEY, pld->io_index);
 43}
 44
 45static int kempld_get_info_generic(struct kempld_device_data *pld)
 46{
 47	u16 version;
 48	u8 spec;
 49
 50	kempld_get_mutex(pld);
 51
 52	version = kempld_read16(pld, KEMPLD_VERSION);
 53	spec = kempld_read8(pld, KEMPLD_SPEC);
 54	pld->info.buildnr = kempld_read16(pld, KEMPLD_BUILDNR);
 55
 56	pld->info.minor = KEMPLD_VERSION_GET_MINOR(version);
 57	pld->info.major = KEMPLD_VERSION_GET_MAJOR(version);
 58	pld->info.number = KEMPLD_VERSION_GET_NUMBER(version);
 59	pld->info.type = KEMPLD_VERSION_GET_TYPE(version);
 60
 61	if (spec == 0xff) {
 62		pld->info.spec_minor = 0;
 63		pld->info.spec_major = 1;
 64	} else {
 65		pld->info.spec_minor = KEMPLD_SPEC_GET_MINOR(spec);
 66		pld->info.spec_major = KEMPLD_SPEC_GET_MAJOR(spec);
 67	}
 68
 69	if (pld->info.spec_major > 0)
 70		pld->feature_mask = kempld_read16(pld, KEMPLD_FEATURE);
 71	else
 72		pld->feature_mask = 0;
 73
 74	kempld_release_mutex(pld);
 75
 76	return 0;
 77}
 78
 79enum kempld_cells {
 80	KEMPLD_I2C = 0,
 81	KEMPLD_WDT,
 82	KEMPLD_GPIO,
 83	KEMPLD_UART,
 84};
 85
 86static const char *kempld_dev_names[] = {
 87	[KEMPLD_I2C] = "kempld-i2c",
 88	[KEMPLD_WDT] = "kempld-wdt",
 89	[KEMPLD_GPIO] = "kempld-gpio",
 90	[KEMPLD_UART] = "kempld-uart",
 91};
 92
 93#define KEMPLD_MAX_DEVS	ARRAY_SIZE(kempld_dev_names)
 94
 95static int kempld_register_cells_generic(struct kempld_device_data *pld)
 96{
 97	struct mfd_cell devs[KEMPLD_MAX_DEVS] = {};
 98	int i = 0;
 99
100	if (pld->feature_mask & KEMPLD_FEATURE_BIT_I2C)
101		devs[i++].name = kempld_dev_names[KEMPLD_I2C];
102
103	if (pld->feature_mask & KEMPLD_FEATURE_BIT_WATCHDOG)
104		devs[i++].name = kempld_dev_names[KEMPLD_WDT];
105
106	if (pld->feature_mask & KEMPLD_FEATURE_BIT_GPIO)
107		devs[i++].name = kempld_dev_names[KEMPLD_GPIO];
108
109	if (pld->feature_mask & KEMPLD_FEATURE_MASK_UART)
110		devs[i++].name = kempld_dev_names[KEMPLD_UART];
111
112	return mfd_add_devices(pld->dev, PLATFORM_DEVID_NONE, devs, i, NULL, 0, NULL);
113}
114
115static struct resource kempld_ioresource = {
116	.start	= KEMPLD_IOINDEX,
117	.end	= KEMPLD_IODATA,
118	.flags	= IORESOURCE_IO,
119};
120
121static const struct kempld_platform_data kempld_platform_data_generic = {
122	.pld_clock		= KEMPLD_CLK,
123	.ioresource		= &kempld_ioresource,
124	.get_hardware_mutex	= kempld_get_hardware_mutex,
125	.release_hardware_mutex	= kempld_release_hardware_mutex,
126	.get_info		= kempld_get_info_generic,
127	.register_cells		= kempld_register_cells_generic,
128};
129
130static struct platform_device *kempld_pdev;
131
132static int kempld_create_platform_device(const struct kempld_platform_data *pdata)
133{
134	const struct platform_device_info pdevinfo = {
135		.name = "kempld",
136		.id = PLATFORM_DEVID_NONE,
137		.res = pdata->ioresource,
138		.num_res = 1,
139		.data = pdata,
140		.size_data = sizeof(*pdata),
141	};
142
143	kempld_pdev = platform_device_register_full(&pdevinfo);
144	if (IS_ERR(kempld_pdev))
145		return PTR_ERR(kempld_pdev);
146
147	return 0;
148}
149
150/**
151 * kempld_read8 - read 8 bit register
152 * @pld: kempld_device_data structure describing the PLD
153 * @index: register index on the chip
154 *
155 * kempld_get_mutex must be called prior to calling this function.
156 */
157u8 kempld_read8(struct kempld_device_data *pld, u8 index)
158{
159	iowrite8(index, pld->io_index);
160	return ioread8(pld->io_data);
161}
162EXPORT_SYMBOL_GPL(kempld_read8);
163
164/**
165 * kempld_write8 - write 8 bit register
166 * @pld: kempld_device_data structure describing the PLD
167 * @index: register index on the chip
168 * @data: new register value
169 *
170 * kempld_get_mutex must be called prior to calling this function.
171 */
172void kempld_write8(struct kempld_device_data *pld, u8 index, u8 data)
173{
174	iowrite8(index, pld->io_index);
175	iowrite8(data, pld->io_data);
176}
177EXPORT_SYMBOL_GPL(kempld_write8);
178
179/**
180 * kempld_read16 - read 16 bit register
181 * @pld: kempld_device_data structure describing the PLD
182 * @index: register index on the chip
183 *
184 * kempld_get_mutex must be called prior to calling this function.
185 */
186u16 kempld_read16(struct kempld_device_data *pld, u8 index)
187{
188	return kempld_read8(pld, index) | kempld_read8(pld, index + 1) << 8;
189}
190EXPORT_SYMBOL_GPL(kempld_read16);
191
192/**
193 * kempld_write16 - write 16 bit register
194 * @pld: kempld_device_data structure describing the PLD
195 * @index: register index on the chip
196 * @data: new register value
197 *
198 * kempld_get_mutex must be called prior to calling this function.
199 */
200void kempld_write16(struct kempld_device_data *pld, u8 index, u16 data)
201{
202	kempld_write8(pld, index, (u8)data);
203	kempld_write8(pld, index + 1, (u8)(data >> 8));
204}
205EXPORT_SYMBOL_GPL(kempld_write16);
206
207/**
208 * kempld_read32 - read 32 bit register
209 * @pld: kempld_device_data structure describing the PLD
210 * @index: register index on the chip
211 *
212 * kempld_get_mutex must be called prior to calling this function.
213 */
214u32 kempld_read32(struct kempld_device_data *pld, u8 index)
215{
216	return kempld_read16(pld, index) | kempld_read16(pld, index + 2) << 16;
217}
218EXPORT_SYMBOL_GPL(kempld_read32);
219
220/**
221 * kempld_write32 - write 32 bit register
222 * @pld: kempld_device_data structure describing the PLD
223 * @index: register index on the chip
224 * @data: new register value
225 *
226 * kempld_get_mutex must be called prior to calling this function.
227 */
228void kempld_write32(struct kempld_device_data *pld, u8 index, u32 data)
229{
230	kempld_write16(pld, index, (u16)data);
231	kempld_write16(pld, index + 2, (u16)(data >> 16));
232}
233EXPORT_SYMBOL_GPL(kempld_write32);
234
235/**
236 * kempld_get_mutex - acquire PLD mutex
237 * @pld: kempld_device_data structure describing the PLD
238 */
239void kempld_get_mutex(struct kempld_device_data *pld)
240{
241	const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
242
243	mutex_lock(&pld->lock);
244	pdata->get_hardware_mutex(pld);
245}
246EXPORT_SYMBOL_GPL(kempld_get_mutex);
247
248/**
249 * kempld_release_mutex - release PLD mutex
250 * @pld: kempld_device_data structure describing the PLD
251 */
252void kempld_release_mutex(struct kempld_device_data *pld)
253{
254	const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
255
256	pdata->release_hardware_mutex(pld);
257	mutex_unlock(&pld->lock);
258}
259EXPORT_SYMBOL_GPL(kempld_release_mutex);
260
261/**
262 * kempld_get_info - update device specific information
263 * @pld: kempld_device_data structure describing the PLD
264 *
265 * This function calls the configured board specific kempld_get_info_XXXX
266 * function which is responsible for gathering information about the specific
267 * hardware. The information is then stored within the pld structure.
268 */
269static int kempld_get_info(struct kempld_device_data *pld)
270{
271	int ret;
272	const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
273	char major, minor;
274
275	ret = pdata->get_info(pld);
276	if (ret)
277		return ret;
278
279	/* The Kontron PLD firmware version string has the following format:
280	 * Pwxy.zzzz
281	 *   P:    Fixed
282	 *   w:    PLD number    - 1 hex digit
283	 *   x:    Major version - 1 alphanumerical digit (0-9A-V)
284	 *   y:    Minor version - 1 alphanumerical digit (0-9A-V)
285	 *   zzzz: Build number  - 4 zero padded hex digits */
286
287	if (pld->info.major < 10)
288		major = pld->info.major + '0';
289	else
290		major = (pld->info.major - 10) + 'A';
291	if (pld->info.minor < 10)
292		minor = pld->info.minor + '0';
293	else
294		minor = (pld->info.minor - 10) + 'A';
295
296	scnprintf(pld->info.version, sizeof(pld->info.version), "P%X%c%c.%04X",
297		  pld->info.number, major, minor, pld->info.buildnr);
298
299	return 0;
300}
301
302/*
303 * kempld_register_cells - register cell drivers
304 *
305 * This function registers cell drivers for the detected hardware by calling
306 * the configured kempld_register_cells_XXXX function which is responsible
307 * to detect and register the needed cell drivers.
308 */
309static int kempld_register_cells(struct kempld_device_data *pld)
310{
311	const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
312
313	return pdata->register_cells(pld);
314}
315
316static const char *kempld_get_type_string(struct kempld_device_data *pld)
317{
318	const char *version_type;
319
320	switch (pld->info.type) {
321	case 0:
322		version_type = "release";
323		break;
324	case 1:
325		version_type = "debug";
326		break;
327	case 2:
328		version_type = "custom";
329		break;
330	default:
331		version_type = "unspecified";
332		break;
333	}
334
335	return version_type;
336}
337
338static ssize_t pld_version_show(struct device *dev,
339				struct device_attribute *attr, char *buf)
340{
341	struct kempld_device_data *pld = dev_get_drvdata(dev);
342
343	return sysfs_emit(buf, "%s\n", pld->info.version);
344}
345
346static ssize_t pld_specification_show(struct device *dev,
347				      struct device_attribute *attr, char *buf)
348{
349	struct kempld_device_data *pld = dev_get_drvdata(dev);
350
351	return sysfs_emit(buf, "%d.%d\n", pld->info.spec_major, pld->info.spec_minor);
352}
353
354static ssize_t pld_type_show(struct device *dev,
355			     struct device_attribute *attr, char *buf)
356{
357	struct kempld_device_data *pld = dev_get_drvdata(dev);
358
359	return sysfs_emit(buf, "%s\n", kempld_get_type_string(pld));
360}
361
362static DEVICE_ATTR_RO(pld_version);
363static DEVICE_ATTR_RO(pld_specification);
364static DEVICE_ATTR_RO(pld_type);
365
366static struct attribute *pld_attrs[] = {
367	&dev_attr_pld_version.attr,
368	&dev_attr_pld_specification.attr,
369	&dev_attr_pld_type.attr,
370	NULL
371};
372ATTRIBUTE_GROUPS(pld);
373
374static int kempld_detect_device(struct kempld_device_data *pld)
375{
376	u8 index_reg;
377	int ret;
378
379	mutex_lock(&pld->lock);
380
381	/* Check for empty IO space */
382	index_reg = ioread8(pld->io_index);
383	if (index_reg == 0xff && ioread8(pld->io_data) == 0xff) {
384		mutex_unlock(&pld->lock);
385		return -ENODEV;
386	}
387
388	/* Release hardware mutex if acquired */
389	if (!(index_reg & KEMPLD_MUTEX_KEY)) {
390		iowrite8(KEMPLD_MUTEX_KEY, pld->io_index);
391		/* PXT and COMe-cPC2 boards may require a second release */
392		iowrite8(KEMPLD_MUTEX_KEY, pld->io_index);
393	}
394
395	mutex_unlock(&pld->lock);
396
397	ret = kempld_get_info(pld);
398	if (ret)
399		return ret;
400
401	dev_info(pld->dev, "Found Kontron PLD - %s (%s), spec %d.%d\n",
402		 pld->info.version, kempld_get_type_string(pld),
403		 pld->info.spec_major, pld->info.spec_minor);
404
405	return kempld_register_cells(pld);
406}
407
408static int kempld_probe(struct platform_device *pdev)
409{
410	const struct kempld_platform_data *pdata;
411	struct device *dev = &pdev->dev;
412	struct kempld_device_data *pld;
413	struct resource *ioport;
414	int ret;
415
416	if (IS_ERR_OR_NULL(kempld_pdev)) {
417		/*
418		 * No kempld_pdev device has been registered in kempld_init,
419		 * so we seem to be probing an ACPI platform device.
420		 */
421		pdata = device_get_match_data(dev);
422		if (!pdata)
423			return -ENODEV;
424
425		ret = platform_device_add_data(pdev, pdata, sizeof(*pdata));
426		if (ret)
427			return ret;
428	} else if (kempld_pdev == pdev) {
429		pdata = dev_get_platdata(dev);
430	} else {
431		/*
432		 * The platform device we are probing is not the one we
433		 * registered in kempld_init using the DMI table, so this one
434		 * comes from ACPI.
435		 * As we can only probe one - abort here and use the DMI
436		 * based one instead.
437		 */
438		dev_notice(dev, "platform device exists - not using ACPI\n");
439		return -ENODEV;
440	}
441
442	pld = devm_kzalloc(dev, sizeof(*pld), GFP_KERNEL);
443	if (!pld)
444		return -ENOMEM;
445
446	ioport = platform_get_resource(pdev, IORESOURCE_IO, 0);
447	if (!ioport)
448		return -EINVAL;
449
450	pld->io_base = devm_ioport_map(dev, ioport->start,
451					resource_size(ioport));
452	if (!pld->io_base)
453		return -ENOMEM;
454
455	pld->io_index = pld->io_base;
456	pld->io_data = pld->io_base + 1;
457	pld->pld_clock = pdata->pld_clock;
458	pld->dev = dev;
459
460	mutex_init(&pld->lock);
461	platform_set_drvdata(pdev, pld);
462
463	return kempld_detect_device(pld);
464}
465
466static void kempld_remove(struct platform_device *pdev)
467{
468	struct kempld_device_data *pld = platform_get_drvdata(pdev);
469	const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
470
471	mfd_remove_devices(&pdev->dev);
472	pdata->release_hardware_mutex(pld);
473}
474
475static const struct acpi_device_id kempld_acpi_table[] = {
476	{ "KEM0000", (kernel_ulong_t)&kempld_platform_data_generic },
477	{ "KEM0001", (kernel_ulong_t)&kempld_platform_data_generic },
478	{}
479};
480MODULE_DEVICE_TABLE(acpi, kempld_acpi_table);
481
482static struct platform_driver kempld_driver = {
483	.driver		= {
484		.name	= "kempld",
485		.acpi_match_table = kempld_acpi_table,
486		.dev_groups	  = pld_groups,
487	},
488	.probe		= kempld_probe,
489	.remove		= kempld_remove,
490};
491
492static const struct dmi_system_id kempld_dmi_table[] __initconst = {
493	{
494		.ident = "BBD6",
495		.matches = {
496			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
497			DMI_MATCH(DMI_BOARD_NAME, "COMe-bBD"),
498		},
499	}, {
500		.ident = "BBL6",
501		.matches = {
502			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
503			DMI_MATCH(DMI_BOARD_NAME, "COMe-bBL6"),
504		},
505	}, {
506		.ident = "BDV7",
507		.matches = {
508			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
509			DMI_MATCH(DMI_BOARD_NAME, "COMe-bDV7"),
510		},
511	}, {
512		.ident = "BHL6",
513		.matches = {
514			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
515			DMI_MATCH(DMI_BOARD_NAME, "COMe-bHL6"),
516		},
517	}, {
518		.ident = "BKL6",
519		.matches = {
520			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
521			DMI_MATCH(DMI_BOARD_NAME, "COMe-bKL6"),
522		},
523	}, {
524		.ident = "BSL6",
525		.matches = {
526			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
527			DMI_MATCH(DMI_BOARD_NAME, "COMe-bSL6"),
528		},
529	}, {
530		.ident = "CAL6",
531		.matches = {
532			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
533			DMI_MATCH(DMI_BOARD_NAME, "COMe-cAL"),
534		},
535	}, {
536		.ident = "CBL6",
537		.matches = {
538			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
539			DMI_MATCH(DMI_BOARD_NAME, "COMe-cBL6"),
540		},
541	}, {
542		.ident = "CBW6",
543		.matches = {
544			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
545			DMI_MATCH(DMI_BOARD_NAME, "COMe-cBW6"),
546		},
547	}, {
548		.ident = "CCR2",
549		.matches = {
550			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
551			DMI_MATCH(DMI_BOARD_NAME, "COMe-bIP2"),
552		},
553	}, {
554		.ident = "CCR6",
555		.matches = {
556			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
557			DMI_MATCH(DMI_BOARD_NAME, "COMe-bIP6"),
558		},
559	}, {
560		.ident = "CDV7",
561		.matches = {
562			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
563			DMI_MATCH(DMI_BOARD_NAME, "COMe-cDV7"),
564		},
565	}, {
566		.ident = "CHL6",
567		.matches = {
568			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
569			DMI_MATCH(DMI_BOARD_NAME, "COMe-cHL6"),
570		},
571	}, {
572		.ident = "CHR2",
573		.matches = {
574			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
575			DMI_MATCH(DMI_BOARD_NAME, "ETXexpress-SC T2"),
576		},
577	}, {
578		.ident = "CHR2",
579		.matches = {
580			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
581			DMI_MATCH(DMI_BOARD_NAME, "ETXe-SC T2"),
582		},
583	}, {
584		.ident = "CHR2",
585		.matches = {
586			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
587			DMI_MATCH(DMI_BOARD_NAME, "COMe-bSC2"),
588		},
589	}, {
590		.ident = "CHR6",
591		.matches = {
592			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
593			DMI_MATCH(DMI_BOARD_NAME, "ETXexpress-SC T6"),
594		},
595	}, {
596		.ident = "CHR6",
597		.matches = {
598			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
599			DMI_MATCH(DMI_BOARD_NAME, "ETXe-SC T6"),
600		},
601	}, {
602		.ident = "CHR6",
603		.matches = {
604			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
605			DMI_MATCH(DMI_BOARD_NAME, "COMe-bSC6"),
606		},
607	}, {
608		.ident = "CKL6",
609		.matches = {
610			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
611			DMI_MATCH(DMI_BOARD_NAME, "COMe-cKL6"),
612		},
613	}, {
614		.ident = "CNTG",
615		.matches = {
616			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
617			DMI_MATCH(DMI_BOARD_NAME, "ETXexpress-PC"),
618		},
619	}, {
620		.ident = "CNTG",
621		.matches = {
622			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
623			DMI_MATCH(DMI_BOARD_NAME, "COMe-bPC2"),
624		},
625	}, {
626		.ident = "CNTX",
627		.matches = {
628			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
629			DMI_MATCH(DMI_BOARD_NAME, "PXT"),
630		},
631	}, {
632		.ident = "CSL6",
633		.matches = {
634			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
635			DMI_MATCH(DMI_BOARD_NAME, "COMe-cSL6"),
636		},
637	}, {
638		.ident = "CVV6",
639		.matches = {
640			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
641			DMI_MATCH(DMI_BOARD_NAME, "COMe-cBT"),
642		},
643	}, {
644		.ident = "FRI2",
645		.matches = {
646			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
647			DMI_MATCH(DMI_BIOS_VERSION, "FRI2"),
648		},
649	}, {
650		.ident = "FRI2",
651		.matches = {
652			DMI_MATCH(DMI_PRODUCT_NAME, "Fish River Island II"),
653		},
654	}, {
655		.ident = "A203",
656		.matches = {
657			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
658			DMI_MATCH(DMI_BOARD_NAME, "KBox A-203"),
659		},
660	}, {
661		.ident = "M4A1",
662		.matches = {
663			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
664			DMI_MATCH(DMI_BOARD_NAME, "COMe-m4AL"),
665		},
666	}, {
667		.ident = "MAL1",
668		.matches = {
669			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
670			DMI_MATCH(DMI_BOARD_NAME, "COMe-mAL10"),
671		},
672	}, {
673		.ident = "MAPL",
674		.matches = {
675			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
676			DMI_MATCH(DMI_BOARD_NAME, "mITX-APL"),
677		},
678	}, {
679		.ident = "MBR1",
680		.matches = {
681			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
682			DMI_MATCH(DMI_BOARD_NAME, "ETX-OH"),
683		},
684	}, {
685		.ident = "MVV1",
686		.matches = {
687			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
688			DMI_MATCH(DMI_BOARD_NAME, "COMe-mBT"),
689		},
690	}, {
691		.ident = "NTC1",
692		.matches = {
693			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
694			DMI_MATCH(DMI_BOARD_NAME, "nanoETXexpress-TT"),
695		},
696	}, {
697		.ident = "NTC1",
698		.matches = {
699			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
700			DMI_MATCH(DMI_BOARD_NAME, "nETXe-TT"),
701		},
702	}, {
703		.ident = "NTC1",
704		.matches = {
705			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
706			DMI_MATCH(DMI_BOARD_NAME, "COMe-mTT"),
707		},
708	}, {
709		.ident = "NUP1",
710		.matches = {
711			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
712			DMI_MATCH(DMI_BOARD_NAME, "COMe-mCT"),
713		},
714	}, {
715		.ident = "PAPL",
716		.matches = {
717			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
718			DMI_MATCH(DMI_BOARD_NAME, "pITX-APL"),
719		},
720	}, {
721		.ident = "SXAL",
722		.matches = {
723			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
724			DMI_MATCH(DMI_BOARD_NAME, "SMARC-sXAL"),
725		},
726	}, {
727		.ident = "SXAL4",
728		.matches = {
729			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
730			DMI_MATCH(DMI_BOARD_NAME, "SMARC-sXA4"),
731		},
732	}, {
733		.ident = "UNP1",
734		.matches = {
735			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
736			DMI_MATCH(DMI_BOARD_NAME, "microETXexpress-DC"),
737		},
738	}, {
739		.ident = "UNP1",
740		.matches = {
741			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
742			DMI_MATCH(DMI_BOARD_NAME, "COMe-cDC2"),
743		},
744	}, {
745		.ident = "UNTG",
746		.matches = {
747			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
748			DMI_MATCH(DMI_BOARD_NAME, "microETXexpress-PC"),
749		},
750	}, {
751		.ident = "UNTG",
752		.matches = {
753			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
754			DMI_MATCH(DMI_BOARD_NAME, "COMe-cPC2"),
755		},
756	}, {
757		.ident = "UUP6",
758		.matches = {
759			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
760			DMI_MATCH(DMI_BOARD_NAME, "COMe-cCT6"),
761		},
762	}, {
763		.ident = "UTH6",
764		.matches = {
765			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
766			DMI_MATCH(DMI_BOARD_NAME, "COMe-cTH6"),
767		},
768	}, {
769		.ident = "Q7AL",
770		.matches = {
771			DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
772			DMI_MATCH(DMI_BOARD_NAME, "Qseven-Q7AL"),
773		},
774	},
775	{}
776};
777MODULE_DEVICE_TABLE(dmi, kempld_dmi_table);
778
779static int __init kempld_init(void)
780{
781	const struct dmi_system_id *id;
782	int ret = -ENODEV;
783
784	for (id = dmi_first_match(kempld_dmi_table); id; id = dmi_first_match(id + 1)) {
785		/* Check, if user asked for the exact device ID match */
786		if (force_device_id[0] && !strstr(id->ident, force_device_id))
787			continue;
788
789		ret = kempld_create_platform_device(&kempld_platform_data_generic);
790		if (ret)
791			continue;
792
793		break;
794	}
795	if (ret)
796		return ret;
797
798	return platform_driver_register(&kempld_driver);
799}
800
801static void __exit kempld_exit(void)
802{
803	platform_device_unregister(kempld_pdev);
804	platform_driver_unregister(&kempld_driver);
805}
806
807module_init(kempld_init);
808module_exit(kempld_exit);
809
810MODULE_DESCRIPTION("KEM PLD Core Driver");
811MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>");
812MODULE_LICENSE("GPL");
813MODULE_ALIAS("platform:kempld-core");