Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755787Ab2BFTHj (ORCPT ); Mon, 6 Feb 2012 14:07:39 -0500 Received: from mail-iy0-f174.google.com ([209.85.210.174]:49695 "EHLO mail-iy0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753284Ab2BFTHh (ORCPT ); Mon, 6 Feb 2012 14:07:37 -0500 MIME-Version: 1.0 In-Reply-To: <1328102793-4313-2-git-send-email-s.hauer@pengutronix.de> References: <1328102793-4313-1-git-send-email-s.hauer@pengutronix.de> <1328102793-4313-2-git-send-email-s.hauer@pengutronix.de> From: Christian Gmeiner Date: Mon, 6 Feb 2012 20:07:16 +0100 Message-ID: Subject: Re: [PATCH 1/3] mfd: Add basic support for the Congatec CGEB BIOS interface To: Sascha Hauer Cc: linux-kernel@vger.kernel.org, Samuel Ortiz , Jean Delvare , linux-i2c@vger.kernel.org, linux-watchdog@vger.kernel.org, Ben Dooks , kernel@pengutronix.de Content-Type: text/plain; charset=UTF-8 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: 8bit X-MIME-Autoconverted: from base64 to 8bit by nfs id q16J7kjc022242 Content-Length: 29076 Lines: 764 > 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 > --- >  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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +#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 "); > +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 ????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?