> The Congatec CGEB is a BIOS interface found on some Congatec x86 > modules. It provides access to on board peripherals like I2C busses > and watchdogs. This driver contains the basic support for accessing > the CGEB interface and registers the child devices. > > Signed-off-by: Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> > --- > drivers/mfd/Kconfig | 10 + > drivers/mfd/Makefile | 1 + > drivers/mfd/congatec-cgeb.c | 590 +++++++++++++++++++++++++++++++++++++ > include/linux/mfd/congatec-cgeb.h | 105 +++++++ > 4 files changed, 706 insertions(+), 0 deletions(-) > create mode 100644 drivers/mfd/congatec-cgeb.c > create mode 100644 include/linux/mfd/congatec-cgeb.h > > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig > index f1391c2..873d04f 100644 > --- a/drivers/mfd/Kconfig > +++ b/drivers/mfd/Kconfig > @@ -772,6 +772,16 @@ config MFD_INTEL_MSIC > Passage) chip. This chip embeds audio, battery, GPIO, etc. > devices used in Intel Medfield platforms. > > +config MFD_CONGATEC_CGEB > + tristate "Support for the Congatec CGEB BIOS interface" > + depends on X86_32 > + help > + The Congatec CGEB BIOS interface provides access to onboard > + peripherals like I2C busses and watchdogs. additional drivers must be > + enabled in order to use the functionality of the device. > + Say y or m here if you are using a congatec module with CGEB interface, > + otherwise say n. > + > endmenu > endif > > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile > index b2292eb..cee77f7 100644 > --- a/drivers/mfd/Makefile > +++ b/drivers/mfd/Makefile > @@ -104,3 +104,4 @@ obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o > obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o > obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o > obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o > +obj-$(CONFIG_MFD_CONGATEC_CGEB) += congatec-cgeb.o > diff --git a/drivers/mfd/congatec-cgeb.c b/drivers/mfd/congatec-cgeb.c > new file mode 100644 > index 0000000..445a9c5 > --- /dev/null > +++ b/drivers/mfd/congatec-cgeb.c > @@ -0,0 +1,590 @@ > +/* > + * CGEB driver > + * > + * (c) 2011 Sascha Hauer, Pengutronix > + * > + * Based on code from Congatec AG. > + * > + * CGEB is a BIOS interface found on congatech modules. It consists of > + * code found in the BIOS memory map which is called in a ioctl like > + * fashion. This file contains the basic driver for this interface > + * which provides access to the GCEB interface and registers the child > + * devices like I2C busses and watchdogs. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; version 2 of the License. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > +#include <linux/kernel.h> > +#include <linux/string.h> > +#include <linux/version.h> > +#include <linux/module.h> > +#include <linux/sched.h> > +#include <linux/io.h> > +#include <linux/string.h> > +#include <linux/errno.h> > +#include <linux/kernel.h> > +#include <linux/slab.h> > +#include <linux/delay.h> > +#include <linux/platform_device.h> > +#include <linux/mfd/congatec-cgeb.h> > + > +#include <generated/autoconf.h> > +#include <stddef.h> > + > +#define CGOS_BOARD_MAX_SIZE_ID_STRING 16 > + > +#define CGEB_VERSION_MAJOR 1 > + > +#define CGEB_GET_VERSION_MAJOR(v) (((unsigned long)(v))>>24) > + > +/* CGEB Low Descriptor located in 0xc0000-0xfffff */ > +#define CGEB_LD_MAGIC "$CGEBLD$" > + > +struct cgeb_low_desc { > + char magic[8]; /* descriptor magic string */ > + u16 size; /* size of this descriptor */ > + u16 reserved; > + char bios_name[8]; /* BIOS name and revision "ppppRvvv" */ > + u32 hi_desc_phys_addr; /* phys addr of the high descriptor, can be 0 */ > +}; > + > +/* CGEB High Descriptor located in 0xfff00000-0xffffffff */ > +#define CGEB_HD_MAGIC "$CGEBHD$" > + > +struct cgeb_high_desc { > + char magic[8]; /* descriptor magic string */ > + u16 size; /* size of this descriptor */ > + u16 reserved; > + u32 data_size; /* CGEB data area size */ > + u32 code_size; /* CGEB code area size */ > + u32 entry_rel; /* CGEB entry point relative to start */ > +}; > + > +struct cgeb_far_ptr { > + u32 off; > + u16 seg; > + u16 pad; > +}; > + > +struct cgeb_fps { > + u32 size; /* size of the parameter structure */ > + u32 fct; /* function number */ > + struct cgeb_far_ptr data; /* CGEB data area */ > + u32 cont; /* private continuation pointer */ > + u32 subfps; /* private sub function parameter > + * structure pointer > + */ > + u32 subfct; /* sub function pointer */ > + u32 status; /* result codes of the function */ > + u32 unit; /* unit number or type */ > + u32 pars[4]; /* input parameters */ > + u32 rets[2]; /* return parameters */ > + void *iptr; /* input pointer */ > + void *optr; /* output pointer */ > +}; > + > +/* continuation status codes */ > +#define CGEB_SUCCESS 0 > +#define CGEB_NEXT 1 > +#define CGEB_DELAY 2 > +#define CGEB_NOIRQS 3 > + > +#define CGEB_DBG_STR 0x100 > +#define CGEB_DBG_HEX 0x101 > +#define CGEB_DBG_DEC 0x102 > + > +struct cgeb_map_mem { > + unsigned long phys; /* physical address */ > + unsigned long size; /* size in bytes */ > + struct cgeb_far_ptr virt; > +}; > + > +struct cgeb_map_mem_list { > + unsigned long count; /* number of memory map entries */ > + struct cgeb_map_mem entries[]; > +}; > + > +struct cgeb_boardinfo { > + unsigned long size; > + unsigned long flags; > + unsigned long classes; > + unsigned long primary_class; > + char board[CGOS_BOARD_MAX_SIZE_ID_STRING]; > + /* optional */ > + char vendor[CGOS_BOARD_MAX_SIZE_ID_STRING]; > +}; > + > +struct cgeb_i2c_info { > + unsigned long size; > + unsigned long type; > + unsigned long frequency; > + unsigned long maxFrequency; > +}; > + > +/* I2C Types */ > +#define CGEB_I2C_TYPE_UNKNOWN 0 > +#define CGEB_I2C_TYPE_PRIMARY 1 > +#define CGEB_I2C_TYPE_SMB 2 > +#define CGEB_I2C_TYPE_DDC 3 > +#define CGEB_I2C_TYPE_BC 4 > + > +struct cgeb_board_data { > + void *code; > + void *data; > + unsigned short ds; > + struct cgeb_map_mem_list *map_mem; > + struct platform_device **devices; > + int num_devices; > + > + /* > + * entry points to a bimodal C style function that expects a far pointer > + * to a fps. If cs is 0 then it does a near return, otherwise a far > + * return. If we ever need a far return then we must not pass cs at all. > + * parameters are removed by the caller. > + */ > + void __attribute__((regparm(0)))(*entry)(unsigned short, > + struct cgeb_fps *, unsigned short); > +}; > + > +static unsigned short get_data_segment(void) > +{ > + unsigned short ret; > + > + asm volatile("mov %%ds, %0\n" > + : "=r"(ret) > + : > + : "memory" > + ); > + > + return ret; > +} > + > +/* > + * cgeb_invoke - invoke CGEB BIOS call. > + * > + * @board: board context data > + * @p: CGEB parameters for this call > + * @fct: CGEB function code > + * @return: 0 on success or negative error code on failure. > + * > + * Call the CGEB BIOS code with the given parameters. > + */ > +unsigned int cgeb_call(struct cgeb_board_data *board, > + struct cgeb_function_parameters *p, cgeb_function_t fct) > +{ > + struct cgeb_fps fps; > + int i; > + > + memset(&fps, 0, sizeof(fps)); > + > + fps.size = sizeof(fps); > + fps.fct = fct; > + fps.data.off = (unsigned long) board->data; > + fps.data.seg = board->ds; > + fps.data.pad = 0; > + fps.status = 0; > + fps.cont = fps.subfct = fps.subfps = 0; > + fps.unit = p->unit; > + for (i = 0; i < 4; i++) > + fps.pars[i] = p->pars[i]; > + fps.iptr = p->iptr; > + fps.optr = p->optr; > + > + while (1) { > + pr_debug("CGEB: CGEB: -> size %02x, fct %02x, data %04x:%08x, status %08x\n", > + fps.size, fps.fct, fps.data.seg, fps.data.off, > + fps.status); > + > + board->entry(0, &fps, fps.data.seg); > + > + switch (fps.status) { > + case CGEB_SUCCESS: > + goto out; > + case CGEB_NEXT: > + break; /* simply call again */ > + case CGEB_NOIRQS: > + current->state = TASK_INTERRUPTIBLE; > + schedule_timeout(0); > + break; > + case CGEB_DELAY: > + usleep_range(fps.rets[0], fps.rets[0] + 1000); > + break; > + case CGEB_DBG_STR: > + if (fps.optr) > + pr_debug("CGEB: %s\n", (char *)fps.optr); > + break; > + case CGEB_DBG_HEX: > + pr_debug("CGEB: 0x%08x\n", fps.rets[0]); > + break; > + case CGEB_DBG_DEC: > + pr_debug("CGEB: %d\n", fps.rets[0]); > + break; > + > + default: > + /* unknown continuation code */ > + return -EINVAL; > + } > + } > +out: > + for (i = 0; i < 2; i++) > + p->rets[i] = fps.rets[i]; > + p->optr = fps.optr; > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(cgeb_call); > + > +/* > + * cgeb_call_simple - convenience wrapper for cgeb_call > + * > + * @board: board context data > + * @p: CGEB parameters for this call > + * @fct: CGEB function code > + * @return: 0 on success or negative error code on failure. > + * > + * Call the CGEB BIOS code with the given parameters. > + */ description of cgeb_call_simple() is wrong regarding function arguments. > +int cgeb_call_simple(struct cgeb_board_data *board, > + cgeb_function_t fct, unsigned long unit, > + unsigned long *optr, unsigned long *result) > +{ > + struct cgeb_function_parameters p; > + unsigned int ret; > + > + memset(&p, 0, sizeof(p)); > + p.unit = unit; > + p.optr = optr; > + > + ret = cgeb_call(board, &p, fct); > + if (optr) > + *optr = (unsigned long)p.optr; > + if (result) > + *result = p.rets[0]; > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(cgeb_call_simple); > + > +static void *cgeb_find_magic(void *_mem, size_t len, char *magic) > +{ > + unsigned long magic0 = ((unsigned long *) magic)[0]; > + unsigned long magic1 = ((unsigned long *) magic)[1]; > + int i = 0; > + > + while (i < len) { > + u32 *mem = _mem + i; > + if (mem[0] == magic0 && mem[1] == magic1) > + return mem; > + i += 16; > + } > + > + return NULL; > +} > + > +static void cgeb_unmap_memory(struct cgeb_board_data *board) > +{ > + struct cgeb_map_mem_list *pmm; > + struct cgeb_map_mem *pmme; > + unsigned long i; > + > + if (!board->map_mem) > + return; > + > + pmm = board->map_mem; > + pmme = pmm->entries; > + for (i = 0; i < pmm->count; i++, pmme++) { > + if (pmme->virt.off) > + iounmap((void *)pmme->virt.off); > + pmme->virt.off = 0; > + pmme->virt.seg = 0; > + } > +} > + > +static int cgeb_map_memory(struct cgeb_board_data *board) > +{ > + struct cgeb_map_mem_list *pmm; > + struct cgeb_map_mem *pmme; > + int i; > + int ret; > + > + ret = cgeb_call_simple(board, CgebMapGetMem, 0, (void *)&board->map_mem, > + NULL); > + if (ret) > + return ret; > + if (!board->map_mem) > + return 0; > + > + pmm = board->map_mem; > + pmme = pmm->entries; > + > + pr_debug("CGEB: Memory Map with %lu entries\n", pmm->count); > + > + for (i = 0; i < pmm->count; i++, pmme++) { > + if (pmme->phys && pmme->size) { > + pmme->virt.off = > + (unsigned long) ioremap_cache(pmme->phys, > + pmme->size); > + if (!pmme->virt.off) > + return -ENOMEM; > + } else { > + pmme->virt.off = 0; > + } > + > + pmme->virt.seg = (pmme->virt.off) ? board->ds : 0; > + > + pr_debug("CGEB: Map phys %08lx, size %08lx, virt %04x:%08x\n", > + pmme->phys, pmme->size, pmme->virt.seg, > + pmme->virt.off); > + } > + > + return cgeb_call_simple(board, CgebMapChanged, 0, NULL, NULL); > +} > + > +static struct cgeb_board_data *cgeb_open(unsigned long base, unsigned long len) > +{ > + unsigned long dw; > + struct cgeb_boardinfo *pbi; > + struct cgeb_low_desc *low_desc; > + struct cgeb_high_desc *high_desc = NULL; > + unsigned long high_desc_phys; > + unsigned long high_desc_len; > + void __iomem *pcur, *high_desc_virt; > + int ret; > + > + struct cgeb_board_data *board; > + > + board = kzalloc(sizeof(*board), GFP_KERNEL); > + if (!board) > + return NULL; > + > + pcur = ioremap_cache(base, len); > + if (!pcur) > + goto err_kfree; > + > + /* look for the CGEB descriptor */ > + low_desc = cgeb_find_magic(pcur, len, CGEB_LD_MAGIC); > + if (!low_desc) > + goto err_kfree; > + > + pr_debug("CGEB: Found CGEB_LD_MAGIC\n"); > + > + if (low_desc->size < sizeof(struct cgeb_low_desc) - sizeof(long)) > + goto err_kfree; > + > + if (low_desc->size >= sizeof(struct cgeb_low_desc) > + && low_desc->hi_desc_phys_addr) > + high_desc_phys = low_desc->hi_desc_phys_addr; > + else > + high_desc_phys = 0xfff00000; > + > + high_desc_len = (unsigned long) -(long) high_desc_phys; > + > + pr_debug("CGEB: Looking for CGEB hi desc between phys 0x%lx and 0x%x\n", > + high_desc_phys, -1); > + > + high_desc_virt = ioremap_cache(high_desc_phys, high_desc_len); > + if (!high_desc_virt) > + goto err_kfree; > + > + pr_debug("CGEB: Looking for CGEB hi desc between virt 0x%p and 0x%p\n", > + high_desc_virt, high_desc_virt + high_desc_len - 1); > + > + high_desc = cgeb_find_magic(high_desc_virt, high_desc_len, > + CGEB_HD_MAGIC); > + if (!high_desc) > + goto err_iounmap; > + > + pr_debug("CGEB: Found CGEB_HD_MAGIC\n"); > + > + if (high_desc->size < sizeof(struct cgeb_high_desc)) > + goto err_iounmap; > + > + pr_debug("CGEB: data_size %u, code_size %u, entry_rel %u\n", > + high_desc->data_size, high_desc->code_size, high_desc->entry_rel); > + > + board->code = __vmalloc(high_desc->code_size, GFP_KERNEL, > + PAGE_KERNEL_EXEC); > + if (!board->code) > + goto err_iounmap; > + > + memcpy(board->code, high_desc, high_desc->code_size); > + > + high_desc = board->code; > + > + board->entry = board->code + high_desc->entry_rel; > + > + board->ds = get_data_segment(); > + > + ret = cgeb_call_simple(board, CgebGetCgebVersion, 0, NULL, &dw); > + if (ret) > + goto err_vfree; > + > + if (CGEB_GET_VERSION_MAJOR(dw) != CGEB_VERSION_MAJOR) > + goto err_vfree; > + > + pr_debug("CGEB: BIOS interface revision: %ld.%ld\n", > + dw >> 16, dw & 0xffff); > + > + if (high_desc->data_size) { > + board->data = vmalloc(high_desc->data_size); > + if (!board->data) > + goto err_vfree; > + } else { > + ret = cgeb_call_simple(board, CgebGetDataSize, 0, NULL, &dw); > + if (!ret && dw) { > + board->data = vmalloc(dw); > + if (!board->data) > + goto err_vfree; > + } > + } > + > + ret = cgeb_call_simple(board, CgebOpen, 0, NULL, NULL); > + if (ret) > + goto err_vfree_data; > + > + ret = cgeb_map_memory(board); > + if (ret) > + goto err_free_map; > + > + ret = cgeb_call_simple(board, CgebBoardGetInfo, 0, &dw, NULL); > + if (ret) > + goto err_free_map; > + > + pbi = (struct cgeb_boardinfo *) dw; > + > + pr_info("CGEB: Board name: %c%c%c%c\n", > + pbi->board[0], pbi->board[1], > + pbi->board[2], pbi->board[3]); > + > + iounmap(high_desc_virt); > + > + return board; > + > +err_free_map: > + cgeb_unmap_memory(board); > +err_vfree_data: > + vfree(board->data); > +err_vfree: > + vfree(board->code); > +err_iounmap: > + iounmap(high_desc_virt); > +err_kfree: > + kfree(board); > + return NULL; > +} > + > +static void cgeb_close(struct cgeb_board_data *board) > +{ > + cgeb_call_simple(board, CgebClose, 0, NULL, NULL); > + > + cgeb_unmap_memory(board); > + > + vfree(board->data); > + vfree(board->code); > +} > + > +static unsigned long cgeb_i2c_get_type(struct cgeb_board_data *brd, int unit) > +{ > + struct cgeb_i2c_info *info; > + int ret; > + > + ret = cgeb_call_simple(brd, CgebI2CGetInfo, unit, (void *) &info, NULL); > + if (ret) > + return ret; > + if (!info) > + return -EINVAL; > + return info->type; > +} > + > +static struct cgeb_board_data *cgeb_board; > + > +static int __init cgeb_init(void) > +{ > + struct cgeb_board_data *board; > + unsigned long base; > + int i, ret; > + struct cgeb_pdata pdata; > + unsigned long i2c_count, watchdog_count; > + int num_devices = 0; > + > + for (base = 0xf0000; base >= 0xc0000; base -= 0x10000) { > + board = cgeb_open(base, 0x10000); > + if (board) > + break; > + } > + > + if (!board) > + return -ENODEV; > + > + cgeb_board = board; > + > + pdata.board = board; > + > + cgeb_call_simple(board, CgebI2CCount, 0, NULL, &i2c_count); > + cgeb_call_simple(board, CgebWDogCount, 0, NULL, &watchdog_count); > + > + board->num_devices = i2c_count + watchdog_count; > + board->devices = kzalloc(sizeof(void *) * (board->num_devices), > + GFP_KERNEL); > + if (!board->devices) { > + ret = -ENOMEM; > + goto err_out; > + } > + > + for (i = 0; i < i2c_count; i++) { > + ret = cgeb_i2c_get_type(board, i); > + if (ret != CGEB_I2C_TYPE_PRIMARY) > + continue; > + > + pdata.unit = i; > + > + board->devices[num_devices] = > + platform_device_register_data(NULL, "cgeb-i2c", i, > + &pdata, sizeof(pdata)); > + num_devices++; > + } > + > + for (i = 0; i < watchdog_count; i++) { > + board->devices[num_devices] = > + platform_device_register_data(NULL, "cgeb-watchdog", i, > + &pdata, sizeof(pdata)); > + pdata.unit = i; > + > + num_devices++; > + } > + > + return 0; > +err_out: > + cgeb_close(board); > + kfree(board); > + > + return ret; > +} > + > +static void cgeb_exit(void) > +{ > + struct cgeb_board_data *board = cgeb_board; > + int i; > + > + for (i = 0; i < board->num_devices; i++) > + if (board->devices[i]) > + platform_device_unregister(board->devices[i]); > + > + cgeb_close(board); > + > + kfree(board->devices); > + kfree(board); > +} > + > +module_init(cgeb_init); > +module_exit(cgeb_exit); > + > +MODULE_AUTHOR("Sascha Hauer <s.hauer@xxxxxxxxxxxxxx>"); > +MODULE_DESCRIPTION("CGEB driver"); > +MODULE_LICENSE("GPL"); > diff --git a/include/linux/mfd/congatec-cgeb.h b/include/linux/mfd/congatec-cgeb.h > new file mode 100644 > index 0000000..74069fc > --- /dev/null > +++ b/include/linux/mfd/congatec-cgeb.h > @@ -0,0 +1,105 @@ > +#ifndef __CONGATEC_CGEB_H > +#define __CONGATEC_CGEB_H > + > +/* CGEB interface functions */ > +typedef enum { > + CgebGetCgebVersion = 0, > + CgebGetSysBiosVersion = 1, > + CgebGetVgaBiosVersion = 2, > + CgebGetDataSize = 3, > + CgebOpen = 4, > + CgebClose = 5, > + CgebMapGetMem = 6, > + CgebMapChanged = 7, > + CgebMapGetPorts = 8, > + CgebDelayUs = 9, > + CgebCgbcReadWrite = 10, > + CgebCgbcSetControl = 11, > + CgebCgbcGetInfo = 12, > + CgebCgbcHandleCommand = 13, > + CgebBoardGetInfo = 14, > + CgebBoardGetBootCounter = 15, > + CgebBoardGetRunningTimeMeter = 16, > + CgebBoardGetBootErrorLog = 17, > + CgebVgaCount = 18, > + CgebVgaGetInfo = 19, > + CgebVgaGetContrast = 20, > + CgebVgaSetContrast = 21, > + CgebVgaGetContrastEnable = 22, > + CgebVgaSetContrastEnable = 23, > + CgebVgaGetBacklight = 24, > + CgebVgaSetBacklight = 25, > + CgebVgaGetBacklightEnable = 26, > + CgebVgaSetBacklightEnable = 27, > + CgebVgaEndDarkBoot = 28, > + CgebStorageAreaCount = 29, > + CgebStorageAreaGetInfo = 30, > + CgebStorageAreaRead = 31, > + CgebStorageAreaWrite = 32, > + CgebStorageAreaErase = 33, > + CgebStorageAreaEraseStatus = 34, > + CgebI2CCount = 35, > + CgebI2CGetInfo = 36, > + CgebI2CGetAddrList = 37, > + CgebI2CTransfer = 38, > + CgebI2CGetFrequency = 39, > + CgebI2CSetFrequency = 40, > + CgebIOCount = 41, > + CgebIOGetInfo = 42, > + CgebIORead = 43, > + CgebIOWrite = 44, > + CgebIOGetDirection = 45, > + CgebIOSetDirection = 46, > + CgebWDogCount = 47, > + CgebWDogGetInfo = 48, > + CgebWDogTrigger = 49, > + CgebWDogGetConfig = 50, > + CgebWDogSetConfig = 51, > + CgebPerformanceGetCurrent = 52, > + CgebPerformanceSetCurrent = 53, > + CgebPerformanceGetPolicyCaps = 54, > + CgebPerformanceGetPolicy = 55, > + CgebPerformanceSetPolicy = 56, > + CgebTemperatureCount = 57, > + CgebTemperatureGetInfo = 58, > + CgebTemperatureGetCurrent = 59, > + CgebTemperatureSetLimits = 60, > + CgebFanCount = 61, > + CgebFanGetInfo = 62, > + CgebFanGetCurrent = 63, > + CgebFanSetLimits = 64, > + CgebVoltageCount = 65, > + CgebVoltageGetInfo = 66, > + CgebVoltageGetCurrent = 67, > + CgebVoltageSetLimits = 68, > + CgebStorageAreaLock = 69, > + CgebStorageAreaUnlock = 70, > + CgebStorageAreaIsLocked = 71, > +} cgeb_function_t; > + > +struct cgeb_function_parameters { > + u32 unit; /* unit number or type */ > + u32 pars[4]; /* input parameters */ > + u32 rets[2]; /* return parameters */ > + void *iptr; /* input pointer */ > + void *optr; /* output pointer */ > +}; > + > +struct cgeb_board_data; > + > +unsigned int cgeb_call(struct cgeb_board_data *, > + struct cgeb_function_parameters *, cgeb_function_t); > + > +int cgeb_call_simple(struct cgeb_board_data *, > + cgeb_function_t, unsigned long, > + unsigned long *, unsigned long *); > + > +/* > + * Platform data for child devices > + */ > +struct cgeb_pdata { > + struct cgeb_board_data *board; > + int unit; > +}; > + > +#endif /* __CONGATEC_CGEB_H */ > -- > 1.7.2.5 I have found only one small issue - see below - and did test this driver with an Atom and E6xx based Congatec CPU module. Tested-by: Christian Gmeiner <christian.gmeiner@xxxxxxxxx> ��.n��������+%������w��{.n�����{��-��)��jg��������ݢj����G�������j:+v���w�m������w�������h�����٥