* [PATCH v2 0/2] Add support for Congatec CGEB BIOS interface
@ 2024-08-01 16:06 Mary Strodl
2024-08-01 16:06 ` [PATCH v2 1/2] x86: Add basic support for the " Mary Strodl
` (2 more replies)
0 siblings, 3 replies; 10+ messages in thread
From: Mary Strodl @ 2024-08-01 16:06 UTC (permalink / raw)
To: linux-kernel
Cc: akpm, urezki, hch, linux-mm, lee, andi.shyti, linux-i2c, s.hauer,
christian.gmeiner, Mary Strodl
The following series adds support for the Congatec CGEB interface
found on some Congatec x86 boards. The CGEB interface is a BIOS
interface which provides access to onboard peripherals like I2C
busses and watchdogs. It works by mapping BIOS code and searching
for magic values which specify the entry points to the CGEB call.
The CGEB call is an API provided by the BIOS which provides access
to the functions in an ioctl like fashion.
At the request of some folks last time this series went out, CGEB
now has a userspace component which runs the x86 blob (rather than
running it directly in the kernel), which sends requests back and
forth using the cn_netlink API.
You can find a reference implementation of the userspace helper here:
https://github.com/Mstrodl/cgeb-helper
I didn't get an answer when I asked where the userspace component
should live, so I didn't put a ton of work into getting the helper
up to snuff since similar userspace helpers (like v86d) are not
in-tree. If folks would like the helper in-tree, that's fine too.
This series is based on the excellent work of Sascha Hauer and
Christian Gmeiner. You can find their original work here:
http://patchwork.ozlabs.org/patch/219756/
http://patchwork.ozlabs.org/patch/219755/
http://patchwork.ozlabs.org/patch/219757/
http://patchwork.ozlabs.org/patch/483262/
http://patchwork.ozlabs.org/patch/483264/
http://patchwork.ozlabs.org/patch/483261/
http://patchwork.ozlabs.org/patch/483263/
Mary Strodl (1):
x86: Add basic support for the Congatec CGEB BIOS interface
Sascha Hauer (1):
i2c: Add Congatec CGEB I2C driver
drivers/i2c/busses/Kconfig | 7 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-congatec-cgeb.c | 189 ++++
drivers/mfd/Kconfig | 10 +
drivers/mfd/Makefile | 1 +
drivers/mfd/congatec-cgeb.c | 1139 ++++++++++++++++++++++++
include/linux/mfd/congatec-cgeb.h | 111 +++
include/uapi/linux/connector.h | 4 +-
8 files changed, 1461 insertions(+), 1 deletion(-)
create mode 100644 drivers/i2c/busses/i2c-congatec-cgeb.c
create mode 100644 drivers/mfd/congatec-cgeb.c
create mode 100644 include/linux/mfd/congatec-cgeb.h
--
2.45.2
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v2 1/2] x86: Add basic support for the Congatec CGEB BIOS interface 2024-08-01 16:06 [PATCH v2 0/2] Add support for Congatec CGEB BIOS interface Mary Strodl @ 2024-08-01 16:06 ` Mary Strodl 2024-08-03 21:46 ` kernel test robot ` (3 more replies) 2024-08-01 16:06 ` [PATCH v2 2/2] i2c: Add Congatec CGEB I2C driver Mary Strodl 2024-08-07 23:14 ` [PATCH v2 0/2] Add support for Congatec CGEB BIOS interface Andi Shyti 2 siblings, 4 replies; 10+ messages in thread From: Mary Strodl @ 2024-08-01 16:06 UTC (permalink / raw) To: linux-kernel Cc: akpm, urezki, hch, linux-mm, lee, andi.shyti, linux-i2c, s.hauer, christian.gmeiner, Mary Strodl 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@pengutronix.de> Signed-off-by: Christian Gmeiner <christian.gmeiner@gmail.com> Signed-off-by: Mary Strodl <mstrodl@csh.rit.edu> --- drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 1 + drivers/mfd/congatec-cgeb.c | 1139 +++++++++++++++++++++++++++++ include/linux/mfd/congatec-cgeb.h | 111 +++ include/uapi/linux/connector.h | 4 +- 5 files changed, 1264 insertions(+), 1 deletion(-) 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 266b4f54af60..fa06a9dc34f9 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1206,6 +1206,16 @@ config MFD_RT5120 is targeted at providing the CPU voltage, memory, I/O and peripheral power rails in home entertainment devices. +config MFD_CONGATEC_CGEB + tristate "Support for the Congatec CGEB BIOS interface" + depends on X86 + 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. + config MFD_RC5T583 bool "Ricoh RC5T583 Power Management system device" depends on I2C=y diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c66f07edcd0e..38f31841ac88 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -217,6 +217,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS) += intel-lpss.o obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o obj-$(CONFIG_MFD_INTEL_PMC_BXT) += intel_pmc_bxt.o +obj-$(CONFIG_MFD_CONGATEC_CGEB) += congatec-cgeb.o obj-$(CONFIG_MFD_PALMAS) += palmas.o obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o obj-$(CONFIG_MFD_NTXEC) += ntxec.o diff --git a/drivers/mfd/congatec-cgeb.c b/drivers/mfd/congatec-cgeb.c new file mode 100644 index 000000000000..6cdfb3c36e35 --- /dev/null +++ b/drivers/mfd/congatec-cgeb.c @@ -0,0 +1,1139 @@ +/* + * CGEB driver + * + * Copyright (c) 2024 Mary Strodl + * + * Based on code from Congatec AG and Sascha Hauer + * + * 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/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/vmalloc.h> +#include <linux/mm.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/connector.h> +#include <linux/mfd/congatec-cgeb.h> +#include <linux/completion.h> + +#include <generated/autoconf.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$" + +#pragma pack(push,4) + +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 */ +#ifdef CONFIG_X86_64 +#define CGEB_HD_MAGIC "$CGEBQD$" +#else +#define CGEB_HD_MAGIC "$CGEBHD$" +#endif + +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 { + void __user *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 */ + void __user *cont; /* private continuation pointer */ + void __user *subfps; /* private sub function parameter + * structure pointer + */ + void __user *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 __user *iptr; /* input pointer */ + void __user *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 { + void *phys; /* physical address */ + u32 size; /* size in bytes */ + struct cgeb_far_ptr virt; +}; + +struct cgeb_map_mem_list { + u32 count; /* number of memory map entries */ + struct cgeb_map_mem entries[]; +}; + +struct cgeb_boardinfo { + u32 size; + u32 flags; + u32 classes; + u32 primary_class; + char board[CGOS_BOARD_MAX_SIZE_ID_STRING]; + /* optional */ + char vendor[CGOS_BOARD_MAX_SIZE_ID_STRING]; +}; + +struct cgeb_i2c_info { + u32 size; + u32 type; + u32 frequency; + u32 maxFrequency; +}; + +#pragma pack(pop) + +/* 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 __user *data; + void __user *code; + size_t code_size; + u16 ds; + struct cgeb_map_mem_list *map_mem; + void __user *map_mem_user; + struct platform_device **devices; + int num_devices; + + #ifdef CONFIG_X86_64 + void (*entry)(void*, struct cgeb_fps *, struct cgeb_fps *, void*); + #else + /* + * 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); + #endif +}; + +struct cgeb_call_user { + void *optr; + size_t size; + void *callback_data; + int (*callback)(void __user *, void *, void *); +}; + +enum cgeb_msg_type { + CGEB_MSG_ACK = 0, + CGEB_MSG_ERROR, + CGEB_MSG_FPS, + CGEB_MSG_MAPPED, + CGEB_MSG_MAP, + CGEB_MSG_CODE, + CGEB_MSG_ALLOC, + CGEB_MSG_ALLOC_CODE, + CGEB_MSG_FREE, + CGEB_MSG_MUNMAP, + CGEB_MSG_CALL, + CGEB_MSG_PING, +}; + +struct cgeb_msg { + enum cgeb_msg_type type; + union { + struct cgeb_msg_mapped { + void *virt; + } mapped; + struct cgeb_msg_fps { + size_t optr_size; + void *optr; + struct cgeb_fps fps; + } fps; + struct cgeb_msg_code { + size_t length; + uint32_t entry_rel; + void *data; + } code; + struct cgeb_msg_map { + uint32_t phys; + size_t size; + } map; + }; +}; + +static char cgeb_helper_path[PATH_MAX] = "/sbin/cgeb-helper"; + +static struct cb_id cgeb_cn_id = { + .idx = CN_IDX_CGEB, + .val = CN_VAL_CGEB +}; + +enum cgeb_request_state { + CGEB_REQ_IDLE = 0, + CGEB_REQ_ACTIVE, + CGEB_REQ_DONE, +}; + +static DEFINE_MUTEX(cgeb_lock); +struct cgeb_request { + struct completion done; + struct cgeb_msg *out; + enum cgeb_request_state busy; + int ack; + int (*callback)(struct cgeb_msg*, void*); + void *user; +}; + +#define CGEB_REQUEST_MAX 16 +static struct cgeb_request cgeb_requests[CGEB_REQUEST_MAX]; + +struct cgeb_after_alloc_data { + void *kernel; + void __user **user; + size_t length; +}; + +struct cgeb_map_data { + void __user *user_list; + struct cgeb_board_data *board; +}; + +static int cgeb_helper_start(void) +{ + char *envp[] = { + NULL, + }; + + char *argv[] = { + cgeb_helper_path, + NULL, + }; + + return call_usermodehelper(cgeb_helper_path, argv, envp, UMH_WAIT_EXEC); +} + +static int cgeb_request(struct cgeb_msg msg, struct cgeb_msg *out, + int (*callback)(struct cgeb_msg*, void*), void *user) +{ + static int seq; + struct cn_msg *wrapper; + struct cgeb_request *req; + int err, retries = 0; + + wrapper = (struct cn_msg*) kzalloc(sizeof(*wrapper) + sizeof(msg), + GFP_KERNEL); + if (!wrapper) + return -ENOMEM; + + memset(wrapper, 0, sizeof(*wrapper)); + memcpy(&wrapper->id, &cgeb_cn_id, sizeof(cgeb_cn_id)); + + wrapper->len = sizeof(msg); + wrapper->ack = get_random_u32(); + memcpy(wrapper + 1, &msg, sizeof(msg)); + + mutex_lock(&cgeb_lock); + + req = &cgeb_requests[seq]; + + if (req->busy) { + mutex_unlock(&cgeb_lock); + err = -EBUSY; + goto out; + } + wrapper->seq = seq; + req->busy = CGEB_REQ_ACTIVE; + req->ack = wrapper->ack; + req->out = out; + req->callback = callback; + req->user = user; + + err = cn_netlink_send(wrapper, 0, 0, GFP_KERNEL); + if (err == -ESRCH) { + err = cgeb_helper_start(); + if (err) { + pr_err("failed to execute %s\n", cgeb_helper_path); + pr_err("make sure that the cgeb helper is installed and" + " executable\n"); + } else { + do { + err = cn_netlink_send(wrapper, 0, 0, + GFP_KERNEL); + if (err == -ENOBUFS) + err = 0; + if (err == -ESRCH) + msleep(30); + } while (err == -ESRCH && ++retries < 5); + } + } else if (err == -ENOBUFS) + err = 0; + + kfree(wrapper); + + if (++seq >= CGEB_REQUEST_MAX) + seq = 0; + + mutex_unlock(&cgeb_lock); + + if (err) + goto out; + + /* Wait for a response to the request */ + err = wait_for_completion_interruptible_timeout( + &req->done, msecs_to_jiffies(20000)); + if (err == 0) { + pr_err("CGEB: Timed out running request of type %d!\n", + msg.type); + err = -ETIMEDOUT; + } else if (err > 0) + err = 0; + + if (err) + goto out; + + mutex_lock(&cgeb_lock); + + if (req->busy != CGEB_REQ_DONE) { + pr_err("CGEB: BUG: Request is in a bad state?\n"); + err = -EINVAL; + } + + req->busy = CGEB_REQ_IDLE; + mutex_unlock(&cgeb_lock); +out: + return err; +} + +static void cgeb_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) +{ + struct cgeb_request *req; + if (!capable(CAP_SYS_ADMIN)) + return; + + if (msg->seq >= CGEB_REQUEST_MAX) { + pr_err("CGEB: Impossible sequence number: %d! Ignoring\n", + msg->seq); + return; + } + + mutex_lock(&cgeb_lock); + req = &cgeb_requests[msg->seq]; + + if (!req->busy || req->ack != msg->ack) { + pr_err("CGEB: Bad response to request %d! Ignoring\n", + msg->seq); + mutex_unlock(&cgeb_lock); + return; + } + + if (msg->len != sizeof(*req->out)) { + pr_err("CGEB: Bad response size for request %d!\n", msg->seq); + mutex_unlock(&cgeb_lock); + return; + } + + pr_debug("Got a response to request %d!\n", msg->seq); + + memcpy(req->out, &msg->data, sizeof(*req->out)); + + req->busy = CGEB_REQ_DONE; + mutex_unlock(&cgeb_lock); + + if (req->callback) + req->callback(req->out, req->user); + + complete(&req->done); + + return; +} + +static int cgeb_copy_to_user(struct cgeb_msg *resp, void *user) +{ + struct cgeb_high_desc *high_desc; + unsigned long uncopied; + high_desc = user; + uncopied = copy_to_user(resp->code.data, high_desc, + high_desc->code_size); + if (uncopied) { + pr_err("CGEB: Couldn't copy code into userspace! %ld\n", + uncopied); + return -ENOMEM; + } + return 0; +} + +static int cgeb_upload_code(struct cgeb_high_desc *high_desc, + struct cgeb_board_data *board) +{ + struct cgeb_msg req = {0}, resp; + size_t len = high_desc->code_size; + int ret = 0; + + req.type = CGEB_MSG_ALLOC_CODE; + req.code.length = len; + pr_debug("CGEB: Allocating memory for code\n"); + ret = cgeb_request(req, &resp, cgeb_copy_to_user, high_desc); + if (ret) + goto out; + if (resp.type != CGEB_MSG_CODE) { + pr_err("CGEB: Bad response type for alloc: %d\n", resp.type); + ret = -EINVAL; + goto out; + } + + board->code = resp.code.data; + board->code_size = len; + + req.type = CGEB_MSG_CODE; + req.code.data = resp.code.data; + req.code.entry_rel = high_desc->entry_rel; + req.code.length = len; + + pr_debug("CGEB: Uploading code\n"); + ret = cgeb_request(req, &resp, NULL, NULL); + + if (ret) + goto out; + + /* Do stuff with response */ + if (resp.type != CGEB_MSG_ACK) { + pr_err("CGEB: Failed to upload code! Got non-ack response!\n"); + ret = -EINVAL; + } + +out: + return ret; +} + +static unsigned short get_data_segment(void) +{ + unsigned short ret; + +#ifdef CONFIG_X86_64 + ret = 0; +#else + asm volatile("mov %%ds, %0\n" + : "=r"(ret) + : + : "memory" + ); +#endif + + return ret; +} + +static int cgeb_after_call(struct cgeb_msg *resp, void *user) +{ + int ret = 0; + int alloc_size; + struct cgeb_call_user *data = user; + if (!resp->fps.fps.optr) + return ret; + + switch(resp->fps.fps.status) { + case CGEB_NEXT: + case CGEB_NOIRQS: + case CGEB_DELAY: + case CGEB_DBG_HEX: + case CGEB_DBG_DEC: + /* These lead to continuations, we don't need their memory */ + return ret; + + /* Everything else we could need */ + case CGEB_DBG_STR: + data->size = alloc_size = strnlen_user(resp->fps.fps.optr, 1023); + if (alloc_size > 1023) { + data->size = 1023; + alloc_size = data->size + 1; + } + /* Special case, because these come from program memory */ + data->optr = kzalloc(alloc_size, GFP_KERNEL); + if (!data->optr) + return -ENOMEM; + } + + ret = copy_from_user(data->optr, resp->fps.fps.optr, data->size); + + if (ret) { + pr_err("CGEB: Couldn't copy optr out of userspace! %d\n", ret); + ret = -ENOMEM; + } + + if (resp->fps.fps.status == CGEB_SUCCESS && data->callback) { + data->callback(resp->fps.fps.optr, data->optr, + data->callback_data); + } + + return ret; +} + +static int cgeb_after_alloc(struct cgeb_msg *resp, void *user) +{ + int ret; + struct cgeb_after_alloc_data *data = user; + + ret = copy_to_user(resp->code.data, data->kernel, data->length); + + if (ret) { + pr_err("CGEB: Couldn't copy iptr into userspace! %d\n", ret); + + ret = -ENOMEM; + } + + *data->user = resp->code.data; + + return ret; +} + +static int cgeb_get_user_ptr(void *kernel, void __user **user, size_t length) +{ + struct cgeb_msg req = {0}, resp; + struct cgeb_after_alloc_data data; + + data.kernel = kernel; + data.user = user; + data.length = length; + + req.type = CGEB_MSG_ALLOC; + req.code.length = length; + return cgeb_request(req, &resp, cgeb_after_alloc, &data); +} + +static void __user *cgeb_user_alloc(size_t length) +{ + int ret; + struct cgeb_msg req = {0}, resp; + req.type = CGEB_MSG_ALLOC; + req.code.length = length; + ret = cgeb_request(req, &resp, NULL, NULL); + if (ret) + return NULL; + + return resp.code.data; +} + +static int cgeb_user_free(void __user *memory) +{ + struct cgeb_msg req = {0}, resp; + + req.type = CGEB_MSG_FREE; + req.code.data = memory; + return cgeb_request(req, &resp, NULL, NULL); +} + +/* + * cgeb_call - 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. + */ +int cgeb_call(struct cgeb_board_data *board, + struct cgeb_function_parameters *p, cgeb_function_t fct) +{ + struct cgeb_fps *fps; + struct cgeb_msg req = {0}, resp; + struct cgeb_call_user user_data, user_data_clone; + int i; + int err; + void __user *iptr_user = NULL; + void __user *optr_user = NULL; + + if ((p->optr && !p->optr_size) || (p->iptr && !p->iptr_size) || + (!p->optr && p->optr_size) || (!p->iptr && p->iptr_size)) { + pr_warn("CGEB: called with impossible iptr/optr\n"); + return -EINVAL; + } + + memset(&req, 0, sizeof(req)); + fps = &req.fps.fps; + + fps->size = sizeof(fps); + fps->fct = fct; + fps->data.off = 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]; + if (p->iptr) { + if (!p->iptr_size) + return -EINVAL; + err = cgeb_get_user_ptr(p->iptr, &iptr_user, p->iptr_size); + fps->iptr = iptr_user; + if (err) + return err; + } + req.fps.optr_size = p->optr_size; + user_data.optr = p->optr; + user_data.size = p->optr_size; + + user_data.callback = p->callback; + user_data.callback_data = p->callback_data; + + while (1) { + pr_debug("CGEB: CGEB: -> size %02x, fct %02x, data %04x:%p\n", + fps->size, fps->fct, fps->data.seg, fps->data.off); + + user_data_clone = user_data; + req.type = CGEB_MSG_CALL; + req.fps.fps = *fps; + err = cgeb_request(req, &resp, cgeb_after_call, + &user_data_clone); + if (err) + goto out; + if (resp.type != CGEB_MSG_FPS) { + err = -EINVAL; + goto out; + } + fps = &resp.fps.fps; + /* Don't allocate another optr */ + req.fps.optr_size = 0; + + if (resp.fps.optr) { + optr_user = resp.fps.optr; + } + + /* DBG_STR causes an allocation, don't overwrite our original */ + if (fps->status != CGEB_DBG_STR) + user_data = user_data_clone; + + pr_debug("CGEB: CGEB: <- size %02x, fct %02x, data %04x:%p\n", + fps->size, fps->fct, fps->data.seg, fps->data.off); + + switch (fps->status) { + case CGEB_SUCCESS: + goto out; + case CGEB_NEXT: + break; /* simply call again */ + case CGEB_NOIRQS: + __set_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 (dbg): %s\n", (char *)user_data_clone.optr); + kfree(user_data_clone.optr); + } + break; + case CGEB_DBG_HEX: + pr_debug("CGEB (dbg): 0x%08x\n", fps->rets[0]); + break; + case CGEB_DBG_DEC: + pr_debug("CGEB (dbg): %d\n", fps->rets[0]); + break; + + default: + /* unknown continuation code */ + err = -EINVAL; + goto out; + } + } +out: + if (iptr_user) + cgeb_user_free(iptr_user); + if (optr_user) + cgeb_user_free(optr_user); + for (i = 0; i < 2; i++) + p->rets[i] = fps->rets[i]; + p->optr = user_data.optr; + + return err; +} +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. + */ +int cgeb_call_simple(struct cgeb_board_data *board, + cgeb_function_t fct, u32 unit, + void *optr, size_t optr_size, u32 *result) +{ + struct cgeb_function_parameters p; + unsigned int ret; + + memset(&p, 0, sizeof(p)); + p.optr_size = optr_size; + p.unit = unit; + p.optr = optr; + + ret = cgeb_call(board, &p, fct); + + 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) +{ + u32 magic0 = ((u32 *)magic)[0]; + u32 magic1 = ((u32 *)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 int cgeb_overwrite_map(struct cgeb_msg *req, void *user) +{ + struct cgeb_board_data *board = (struct cgeb_board_data*) user; + struct cgeb_map_mem_list *pmm = board->map_mem; + int err; + err = copy_to_user(board->map_mem_user, board->map_mem, + sizeof(pmm->entries[0]) * pmm->count + sizeof(*pmm)); + if (err) { + pr_err("CGEB: Couldn't copy map_mem into userspace!\n"); + return -ENOMEM; + } + return 0; +} + +static void cgeb_unmap_memory(struct cgeb_board_data *board) +{ + struct cgeb_map_mem_list *pmm; + struct cgeb_map_mem *pmme; + struct cgeb_msg req = {0}, res; + unsigned long i; + + if (!board->map_mem) + return; + + req.type = CGEB_MSG_MUNMAP; + + pmm = board->map_mem; + pmme = pmm->entries; + pr_debug("CGEB: Unmapping %d pages\n", pmm->count); + for (i = 0; i < pmm->count; i++, pmme++) { + pmme->virt.seg = 0; + if (pmme->virt.off) { + req.code.data = pmme->virt.off; + req.code.length = pmme->size; + pmme->virt.off = 0; + cgeb_request(req, &res, cgeb_overwrite_map, board); + } + } + board->map_mem_user = NULL; + kfree(board->map_mem); +} + +static int cgeb_deref_map(void __user *userspace, void *optr, void *user) +{ + struct cgeb_board_data *board = (struct cgeb_board_data*) user; + struct cgeb_map_mem_list *list = optr; + size_t size; + int uncopied; + size = sizeof(*list) + sizeof(list->entries[0]) * list->count; + + board->map_mem = (struct cgeb_map_mem_list*) kzalloc(size, GFP_KERNEL); + if (!board->map_mem) { + pr_err("Couldn't allocate map_mem\n"); + return -ENOMEM; + } + + board->map_mem_user = userspace; + uncopied = copy_from_user(board->map_mem, board->map_mem_user, size); + if (uncopied) { + pr_err("Couldn't copy map_mem from map_mem_user!\n"); + kfree(board->map_mem); + board->map_mem = NULL; + return -ENOMEM; + } + return 0; +} + + +static int cgeb_map_memory(struct cgeb_board_data *board) +{ + struct cgeb_map_mem_list *pmm; + struct cgeb_map_mem *pmme; + struct cgeb_msg req = {0}, res; + struct cgeb_function_parameters fps = {0}; + int i; + int ret; + + fps.optr = &board->map_mem; + fps.optr_size = sizeof(*board->map_mem); + fps.callback = cgeb_deref_map; + fps.callback_data = board; + ret = cgeb_call(board, &fps, CgebMapGetMem); + if (ret) + return ret; + + if (!board->map_mem) + return 0; + + pmm = board->map_mem; + pmme = pmm->entries; + + req.type = CGEB_MSG_MAP; + + pr_debug("CGEB: Memory Map with %d entries\n", pmm->count); + + for (i = 0; i < pmm->count; i++, pmme++) { + pr_debug("CGEB: Memory map entry phys=%p, size=%08x\n", + pmme->phys, pmme->size); + if (pmme->phys && pmme->size) { + /* We only want to look at the lower 32 bits */ + req.map.phys = (size_t) pmme->phys & 0xffffffff; + req.map.size = pmme->size; + ret = cgeb_request(req, &res, NULL, NULL); + if (ret) + return ret; + if (res.type != CGEB_MSG_MAPPED) { + pr_err("CGEB: Invalid map response!\n"); + return -EINVAL; + } + pmme->virt.off = res.mapped.virt; + } else { + pmme->virt.off = 0; + } + + pmme->virt.seg = (pmme->virt.off) ? board->ds : 0; + + pr_debug("CGEB: Map phys %p, size %08x, virt %04x:%p\n", + pmme->phys, pmme->size, pmme->virt.seg, + pmme->virt.off); + } + + cgeb_request(req, &res, cgeb_overwrite_map, board); + return cgeb_call_simple(board, CgebMapChanged, 0, NULL, 0, NULL); +} + +static void cgeb_munmap(void __user *ptr, size_t size) +{ + struct cgeb_msg req = {0}, res; + + req.type = CGEB_MSG_MUNMAP; + req.code.data = ptr; + req.code.length = size; + cgeb_request(req, &res, NULL, NULL); +} + +static struct cgeb_board_data *cgeb_open(resource_size_t base, u32 len) +{ + u32 dw; + struct cgeb_boardinfo pbi; + struct cgeb_low_desc *low_desc; + struct cgeb_high_desc *high_desc = NULL; + u32 high_desc_phys; + u32 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 int) -(int)high_desc_phys; + + pr_debug("CGEB: Looking for CGEB hi desc between phys 0x%x 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); + + ret = cgeb_upload_code(high_desc, board); + if (ret) { + pr_err("CGEB: Couldn't upload code to helper: %d\n", ret); + goto err_munmap; + } + + board->ds = get_data_segment(); + + ret = cgeb_call_simple(board, CgebGetCgebVersion, 0, NULL, 0, &dw); + if (ret) + goto err_munmap; + + if (CGEB_GET_VERSION_MAJOR(dw) != CGEB_VERSION_MAJOR) + goto err_munmap; + + pr_debug("CGEB: BIOS interface revision: %d.%d\n", + dw >> 16, dw & 0xffff); + + if (high_desc->data_size) + dw = high_desc->data_size; + else + ret = cgeb_call_simple(board, CgebGetDataSize, 0, NULL, 0, &dw); + + if (!ret && dw) { + board->data = cgeb_user_alloc(high_desc->data_size); + if (!board->data) + goto err_munmap; + } + + ret = cgeb_call_simple(board, CgebOpen, 0, NULL, 0, NULL); + if (ret) + goto err_free_data; + + pr_debug("CGEB: Mapping memory\n"); + ret = cgeb_map_memory(board); + if (ret) + goto err_free_map; + pr_debug("CGEB: Memory is mapped, getting board info\n"); + + ret = cgeb_call_simple(board, CgebBoardGetInfo, 0, &pbi, + sizeof(pbi), NULL); + if (ret) + goto err_free_map; + + 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_free_data: + cgeb_user_free(board->data); +err_munmap: + cgeb_munmap(board->code, board->code_size); +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, 0, NULL); + + cgeb_unmap_memory(board); + + cgeb_user_free(board->data); + + cgeb_munmap(board->code, board->code_size); +} + +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, &info, + sizeof(info), NULL); + if (ret) + return ret; + return info.type; +} + +static struct cgeb_board_data *cgeb_board; + +static int __init cgeb_init(void) +{ + struct cgeb_board_data *board; + resource_size_t base; + int i, ret, req; + struct cgeb_pdata pdata; + u32 i2c_count, watchdog_count; + int num_devices = 0; + + for (req = 0; req < CGEB_REQUEST_MAX; ++req) { + init_completion(&cgeb_requests[req].done); + } + + ret = cn_add_callback(&cgeb_cn_id, "cgeb", cgeb_cn_callback); + if (ret) + return ret; + + pr_debug("CGEB: Opening board\n"); + for (base = 0xf0000; base >= 0xc0000; base -= 0x10000) { + board = cgeb_open(base, 0x10000); + if (board) + break; + } + + if (!board) { + ret = -ENODEV; + goto err_no_board; + } + + cgeb_board = board; + + pdata.board = board; + + cgeb_call_simple(board, CgebI2CCount, 0, NULL, 0, &i2c_count); + cgeb_call_simple(board, CgebWDogCount, 0, NULL, 0, &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); + +err_no_board: + cn_del_callback(&cgeb_cn_id); + + return ret; +} + +static void __exit 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); + cn_del_callback(&cgeb_cn_id); +} + +module_init(cgeb_init); +module_exit(cgeb_exit); + +MODULE_AUTHOR("Mary Strodl <mstrodl@csh.rit.edu>"); +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 000000000000..47b594cc557b --- /dev/null +++ b/include/linux/mfd/congatec-cgeb.h @@ -0,0 +1,111 @@ +#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 */ + size_t iptr_size; /* size of input pointer */ + size_t optr_size; /* size of output pointer */ + void *callback_data; /* callback data pointer */ + int (*callback)(void __user *, void *, void *); + /* Run when CGEB call finishes with userspace */ + /* optr, kernel optr, and callback_data */ +}; + +struct cgeb_board_data; + +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, u32, + void *, size_t size, u32 *); + +/* + * Platform data for child devices + */ +struct cgeb_pdata { + struct cgeb_board_data *board; + int unit; +}; + +#endif /* __CONGATEC_CGEB_H */ diff --git a/include/uapi/linux/connector.h b/include/uapi/linux/connector.h index 5ae131c3f145..f64862bee5ce 100644 --- a/include/uapi/linux/connector.h +++ b/include/uapi/linux/connector.h @@ -47,9 +47,11 @@ #define CN_KVP_VAL 0x1 /* queries from the kernel */ #define CN_VSS_IDX 0xA /* HyperV VSS */ #define CN_VSS_VAL 0x1 /* queries from the kernel */ +#define CN_IDX_CGEB 0xB /* congatec CGEB */ +#define CN_VAL_CGEB 0x1 -#define CN_NETLINK_USERS 11 /* Highest index + 1 */ +#define CN_NETLINK_USERS 12 /* Highest index + 1 */ /* * Maximum connector's message size. -- 2.45.2 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/2] x86: Add basic support for the Congatec CGEB BIOS interface 2024-08-01 16:06 ` [PATCH v2 1/2] x86: Add basic support for the " Mary Strodl @ 2024-08-03 21:46 ` kernel test robot 2024-08-03 22:07 ` kernel test robot ` (2 subsequent siblings) 3 siblings, 0 replies; 10+ messages in thread From: kernel test robot @ 2024-08-03 21:46 UTC (permalink / raw) To: Mary Strodl, linux-kernel Cc: oe-kbuild-all, akpm, urezki, hch, linux-mm, lee, andi.shyti, linux-i2c, s.hauer, christian.gmeiner, Mary Strodl Hi Mary, kernel test robot noticed the following build errors: [auto build test ERROR on lee-mfd/for-mfd-next] [also build test ERROR on lee-mfd/for-mfd-fixes andi-shyti/i2c/i2c-host akpm-mm/mm-everything linus/master v6.11-rc1 next-20240802] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Mary-Strodl/x86-Add-basic-support-for-the-Congatec-CGEB-BIOS-interface/20240803-013725 base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next patch link: https://lore.kernel.org/r/20240801160610.101859-2-mstrodl%40csh.rit.edu patch subject: [PATCH v2 1/2] x86: Add basic support for the Congatec CGEB BIOS interface config: x86_64-randconfig-002-20240804 (https://download.01.org/0day-ci/archive/20240804/202408040528.OmB08hFQ-lkp@intel.com/config) compiler: gcc-12 (Debian 12.2.0-14) 12.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240804/202408040528.OmB08hFQ-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202408040528.OmB08hFQ-lkp@intel.com/ All errors (new ones prefixed by >>): ld: drivers/mfd/congatec-cgeb.o: in function `cgeb_request': >> drivers/mfd/congatec-cgeb.c:303: undefined reference to `cn_netlink_send' >> ld: drivers/mfd/congatec-cgeb.c:312: undefined reference to `cn_netlink_send' ld: drivers/mfd/congatec-cgeb.o: in function `cgeb_exit': >> drivers/mfd/congatec-cgeb.c:1131: undefined reference to `cn_del_callback' ld: drivers/mfd/congatec-cgeb.o: in function `cgeb_init': >> drivers/mfd/congatec-cgeb.c:1054: undefined reference to `cn_add_callback' >> ld: drivers/mfd/congatec-cgeb.c:1113: undefined reference to `cn_del_callback' vim +303 drivers/mfd/congatec-cgeb.c 266 267 static int cgeb_request(struct cgeb_msg msg, struct cgeb_msg *out, 268 int (*callback)(struct cgeb_msg*, void*), void *user) 269 { 270 static int seq; 271 struct cn_msg *wrapper; 272 struct cgeb_request *req; 273 int err, retries = 0; 274 275 wrapper = (struct cn_msg*) kzalloc(sizeof(*wrapper) + sizeof(msg), 276 GFP_KERNEL); 277 if (!wrapper) 278 return -ENOMEM; 279 280 memset(wrapper, 0, sizeof(*wrapper)); 281 memcpy(&wrapper->id, &cgeb_cn_id, sizeof(cgeb_cn_id)); 282 283 wrapper->len = sizeof(msg); 284 wrapper->ack = get_random_u32(); 285 memcpy(wrapper + 1, &msg, sizeof(msg)); 286 287 mutex_lock(&cgeb_lock); 288 289 req = &cgeb_requests[seq]; 290 291 if (req->busy) { 292 mutex_unlock(&cgeb_lock); 293 err = -EBUSY; 294 goto out; 295 } 296 wrapper->seq = seq; 297 req->busy = CGEB_REQ_ACTIVE; 298 req->ack = wrapper->ack; 299 req->out = out; 300 req->callback = callback; 301 req->user = user; 302 > 303 err = cn_netlink_send(wrapper, 0, 0, GFP_KERNEL); 304 if (err == -ESRCH) { 305 err = cgeb_helper_start(); 306 if (err) { 307 pr_err("failed to execute %s\n", cgeb_helper_path); 308 pr_err("make sure that the cgeb helper is installed and" 309 " executable\n"); 310 } else { 311 do { > 312 err = cn_netlink_send(wrapper, 0, 0, 313 GFP_KERNEL); 314 if (err == -ENOBUFS) 315 err = 0; 316 if (err == -ESRCH) 317 msleep(30); 318 } while (err == -ESRCH && ++retries < 5); 319 } 320 } else if (err == -ENOBUFS) 321 err = 0; 322 323 kfree(wrapper); 324 325 if (++seq >= CGEB_REQUEST_MAX) 326 seq = 0; 327 328 mutex_unlock(&cgeb_lock); 329 330 if (err) 331 goto out; 332 333 /* Wait for a response to the request */ 334 err = wait_for_completion_interruptible_timeout( 335 &req->done, msecs_to_jiffies(20000)); 336 if (err == 0) { 337 pr_err("CGEB: Timed out running request of type %d!\n", 338 msg.type); 339 err = -ETIMEDOUT; 340 } else if (err > 0) 341 err = 0; 342 343 if (err) 344 goto out; 345 346 mutex_lock(&cgeb_lock); 347 348 if (req->busy != CGEB_REQ_DONE) { 349 pr_err("CGEB: BUG: Request is in a bad state?\n"); 350 err = -EINVAL; 351 } 352 353 req->busy = CGEB_REQ_IDLE; 354 mutex_unlock(&cgeb_lock); 355 out: 356 return err; 357 } 358 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/2] x86: Add basic support for the Congatec CGEB BIOS interface 2024-08-01 16:06 ` [PATCH v2 1/2] x86: Add basic support for the " Mary Strodl 2024-08-03 21:46 ` kernel test robot @ 2024-08-03 22:07 ` kernel test robot 2024-08-03 22:37 ` kernel test robot 2024-08-07 23:16 ` Andi Shyti 3 siblings, 0 replies; 10+ messages in thread From: kernel test robot @ 2024-08-03 22:07 UTC (permalink / raw) To: Mary Strodl, linux-kernel Cc: llvm, oe-kbuild-all, akpm, urezki, hch, linux-mm, lee, andi.shyti, linux-i2c, s.hauer, christian.gmeiner, Mary Strodl Hi Mary, kernel test robot noticed the following build errors: [auto build test ERROR on lee-mfd/for-mfd-next] [also build test ERROR on lee-mfd/for-mfd-fixes andi-shyti/i2c/i2c-host akpm-mm/mm-everything linus/master v6.11-rc1 next-20240802] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Mary-Strodl/x86-Add-basic-support-for-the-Congatec-CGEB-BIOS-interface/20240803-013725 base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next patch link: https://lore.kernel.org/r/20240801160610.101859-2-mstrodl%40csh.rit.edu patch subject: [PATCH v2 1/2] x86: Add basic support for the Congatec CGEB BIOS interface config: i386-buildonly-randconfig-005-20240804 (https://download.01.org/0day-ci/archive/20240804/202408040501.vvHCkNqn-lkp@intel.com/config) compiler: clang version 18.1.5 (https://github.com/llvm/llvm-project 617a15a9eac96088ae5e9134248d8236e34b91b1) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240804/202408040501.vvHCkNqn-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202408040501.vvHCkNqn-lkp@intel.com/ All errors (new ones prefixed by >>, old ones prefixed by <<): >> ERROR: modpost: "cn_netlink_send" [drivers/mfd/congatec-cgeb.ko] undefined! >> ERROR: modpost: "cn_add_callback" [drivers/mfd/congatec-cgeb.ko] undefined! >> ERROR: modpost: "cn_del_callback" [drivers/mfd/congatec-cgeb.ko] undefined! -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/2] x86: Add basic support for the Congatec CGEB BIOS interface 2024-08-01 16:06 ` [PATCH v2 1/2] x86: Add basic support for the " Mary Strodl 2024-08-03 21:46 ` kernel test robot 2024-08-03 22:07 ` kernel test robot @ 2024-08-03 22:37 ` kernel test robot 2024-08-07 23:16 ` Andi Shyti 3 siblings, 0 replies; 10+ messages in thread From: kernel test robot @ 2024-08-03 22:37 UTC (permalink / raw) To: Mary Strodl, linux-kernel Cc: oe-kbuild-all, akpm, urezki, hch, linux-mm, lee, andi.shyti, linux-i2c, s.hauer, christian.gmeiner, Mary Strodl Hi Mary, kernel test robot noticed the following build warnings: [auto build test WARNING on lee-mfd/for-mfd-next] [also build test WARNING on lee-mfd/for-mfd-fixes andi-shyti/i2c/i2c-host akpm-mm/mm-everything linus/master v6.11-rc1 next-20240802] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Mary-Strodl/x86-Add-basic-support-for-the-Congatec-CGEB-BIOS-interface/20240803-013725 base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next patch link: https://lore.kernel.org/r/20240801160610.101859-2-mstrodl%40csh.rit.edu patch subject: [PATCH v2 1/2] x86: Add basic support for the Congatec CGEB BIOS interface config: x86_64-randconfig-121-20240804 (https://download.01.org/0day-ci/archive/20240804/202408040629.HQM8a5ga-lkp@intel.com/config) compiler: gcc-12 (Debian 12.2.0-14) 12.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240804/202408040629.HQM8a5ga-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202408040629.HQM8a5ga-lkp@intel.com/ sparse warnings: (new ones prefixed by >>) >> drivers/mfd/congatec-cgeb.c:407:43: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void [noderef] __user *to @@ got void *data @@ drivers/mfd/congatec-cgeb.c:407:43: sparse: expected void [noderef] __user *to drivers/mfd/congatec-cgeb.c:407:43: sparse: got void *data >> drivers/mfd/congatec-cgeb.c:436:21: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected void [noderef] __user *code @@ got void *[addressable] data @@ drivers/mfd/congatec-cgeb.c:436:21: sparse: expected void [noderef] __user *code drivers/mfd/congatec-cgeb.c:436:21: sparse: got void *[addressable] data drivers/mfd/congatec-cgeb.c:527:38: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void [noderef] __user *to @@ got void *data @@ drivers/mfd/congatec-cgeb.c:527:38: sparse: expected void [noderef] __user *to drivers/mfd/congatec-cgeb.c:527:38: sparse: got void *data >> drivers/mfd/congatec-cgeb.c:535:21: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected void [noderef] __user * @@ got void *data @@ drivers/mfd/congatec-cgeb.c:535:21: sparse: expected void [noderef] __user * drivers/mfd/congatec-cgeb.c:535:21: sparse: got void *data >> drivers/mfd/congatec-cgeb.c:564:25: sparse: sparse: incorrect type in return expression (different address spaces) @@ expected void [noderef] __user * @@ got void *[addressable] data @@ drivers/mfd/congatec-cgeb.c:564:25: sparse: expected void [noderef] __user * drivers/mfd/congatec-cgeb.c:564:25: sparse: got void *[addressable] data >> drivers/mfd/congatec-cgeb.c:572:23: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected void *[assigned] data @@ got void [noderef] __user *memory @@ drivers/mfd/congatec-cgeb.c:572:23: sparse: expected void *[assigned] data drivers/mfd/congatec-cgeb.c:572:23: sparse: got void [noderef] __user *memory >> drivers/mfd/congatec-cgeb.c:612:49: sparse: sparse: Using plain integer as NULL pointer >> drivers/mfd/congatec-cgeb.c:651:35: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected void [noderef] __user *optr_user @@ got void *[addressable] optr @@ drivers/mfd/congatec-cgeb.c:651:35: sparse: expected void [noderef] __user *optr_user drivers/mfd/congatec-cgeb.c:651:35: sparse: got void *[addressable] optr >> drivers/mfd/congatec-cgeb.c:786:39: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected void *[assigned] data @@ got void [noderef] __user *off @@ drivers/mfd/congatec-cgeb.c:786:39: sparse: expected void *[assigned] data drivers/mfd/congatec-cgeb.c:786:39: sparse: got void [noderef] __user *off drivers/mfd/congatec-cgeb.c:788:42: sparse: sparse: Using plain integer as NULL pointer >> drivers/mfd/congatec-cgeb.c:863:40: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected void [noderef] __user *off @@ got void *[addressable] virt @@ drivers/mfd/congatec-cgeb.c:863:40: sparse: expected void [noderef] __user *off drivers/mfd/congatec-cgeb.c:863:40: sparse: got void *[addressable] virt drivers/mfd/congatec-cgeb.c:865:42: sparse: sparse: Using plain integer as NULL pointer >> drivers/mfd/congatec-cgeb.c:884:23: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected void *[assigned] data @@ got void [noderef] __user *ptr @@ drivers/mfd/congatec-cgeb.c:884:23: sparse: expected void *[assigned] data drivers/mfd/congatec-cgeb.c:884:23: sparse: got void [noderef] __user *ptr >> drivers/mfd/congatec-cgeb.c:911:36: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void *_mem @@ got void [noderef] __iomem *[assigned] pcur @@ drivers/mfd/congatec-cgeb.c:911:36: sparse: expected void *_mem drivers/mfd/congatec-cgeb.c:911:36: sparse: got void [noderef] __iomem *[assigned] pcur >> drivers/mfd/congatec-cgeb.c:938:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void *_mem @@ got void [noderef] __iomem *[assigned] high_desc_virt @@ drivers/mfd/congatec-cgeb.c:938:37: sparse: expected void *_mem drivers/mfd/congatec-cgeb.c:938:37: sparse: got void [noderef] __iomem *[assigned] high_desc_virt vim +407 drivers/mfd/congatec-cgeb.c 401 402 static int cgeb_copy_to_user(struct cgeb_msg *resp, void *user) 403 { 404 struct cgeb_high_desc *high_desc; 405 unsigned long uncopied; 406 high_desc = user; > 407 uncopied = copy_to_user(resp->code.data, high_desc, 408 high_desc->code_size); 409 if (uncopied) { 410 pr_err("CGEB: Couldn't copy code into userspace! %ld\n", 411 uncopied); 412 return -ENOMEM; 413 } 414 return 0; 415 } 416 417 static int cgeb_upload_code(struct cgeb_high_desc *high_desc, 418 struct cgeb_board_data *board) 419 { 420 struct cgeb_msg req = {0}, resp; 421 size_t len = high_desc->code_size; 422 int ret = 0; 423 424 req.type = CGEB_MSG_ALLOC_CODE; 425 req.code.length = len; 426 pr_debug("CGEB: Allocating memory for code\n"); 427 ret = cgeb_request(req, &resp, cgeb_copy_to_user, high_desc); 428 if (ret) 429 goto out; 430 if (resp.type != CGEB_MSG_CODE) { 431 pr_err("CGEB: Bad response type for alloc: %d\n", resp.type); 432 ret = -EINVAL; 433 goto out; 434 } 435 > 436 board->code = resp.code.data; 437 board->code_size = len; 438 439 req.type = CGEB_MSG_CODE; 440 req.code.data = resp.code.data; 441 req.code.entry_rel = high_desc->entry_rel; 442 req.code.length = len; 443 444 pr_debug("CGEB: Uploading code\n"); 445 ret = cgeb_request(req, &resp, NULL, NULL); 446 447 if (ret) 448 goto out; 449 450 /* Do stuff with response */ 451 if (resp.type != CGEB_MSG_ACK) { 452 pr_err("CGEB: Failed to upload code! Got non-ack response!\n"); 453 ret = -EINVAL; 454 } 455 456 out: 457 return ret; 458 } 459 460 static unsigned short get_data_segment(void) 461 { 462 unsigned short ret; 463 464 #ifdef CONFIG_X86_64 465 ret = 0; 466 #else 467 asm volatile("mov %%ds, %0\n" 468 : "=r"(ret) 469 : 470 : "memory" 471 ); 472 #endif 473 474 return ret; 475 } 476 477 static int cgeb_after_call(struct cgeb_msg *resp, void *user) 478 { 479 int ret = 0; 480 int alloc_size; 481 struct cgeb_call_user *data = user; 482 if (!resp->fps.fps.optr) 483 return ret; 484 485 switch(resp->fps.fps.status) { 486 case CGEB_NEXT: 487 case CGEB_NOIRQS: 488 case CGEB_DELAY: 489 case CGEB_DBG_HEX: 490 case CGEB_DBG_DEC: 491 /* These lead to continuations, we don't need their memory */ 492 return ret; 493 494 /* Everything else we could need */ 495 case CGEB_DBG_STR: 496 data->size = alloc_size = strnlen_user(resp->fps.fps.optr, 1023); 497 if (alloc_size > 1023) { 498 data->size = 1023; 499 alloc_size = data->size + 1; 500 } 501 /* Special case, because these come from program memory */ 502 data->optr = kzalloc(alloc_size, GFP_KERNEL); 503 if (!data->optr) 504 return -ENOMEM; 505 } 506 507 ret = copy_from_user(data->optr, resp->fps.fps.optr, data->size); 508 509 if (ret) { 510 pr_err("CGEB: Couldn't copy optr out of userspace! %d\n", ret); 511 ret = -ENOMEM; 512 } 513 514 if (resp->fps.fps.status == CGEB_SUCCESS && data->callback) { 515 data->callback(resp->fps.fps.optr, data->optr, 516 data->callback_data); 517 } 518 519 return ret; 520 } 521 522 static int cgeb_after_alloc(struct cgeb_msg *resp, void *user) 523 { 524 int ret; 525 struct cgeb_after_alloc_data *data = user; 526 > 527 ret = copy_to_user(resp->code.data, data->kernel, data->length); 528 529 if (ret) { 530 pr_err("CGEB: Couldn't copy iptr into userspace! %d\n", ret); 531 532 ret = -ENOMEM; 533 } 534 > 535 *data->user = resp->code.data; 536 537 return ret; 538 } 539 540 static int cgeb_get_user_ptr(void *kernel, void __user **user, size_t length) 541 { 542 struct cgeb_msg req = {0}, resp; 543 struct cgeb_after_alloc_data data; 544 545 data.kernel = kernel; 546 data.user = user; 547 data.length = length; 548 549 req.type = CGEB_MSG_ALLOC; 550 req.code.length = length; 551 return cgeb_request(req, &resp, cgeb_after_alloc, &data); 552 } 553 554 static void __user *cgeb_user_alloc(size_t length) 555 { 556 int ret; 557 struct cgeb_msg req = {0}, resp; 558 req.type = CGEB_MSG_ALLOC; 559 req.code.length = length; 560 ret = cgeb_request(req, &resp, NULL, NULL); 561 if (ret) 562 return NULL; 563 > 564 return resp.code.data; 565 } 566 567 static int cgeb_user_free(void __user *memory) 568 { 569 struct cgeb_msg req = {0}, resp; 570 571 req.type = CGEB_MSG_FREE; > 572 req.code.data = memory; 573 return cgeb_request(req, &resp, NULL, NULL); 574 } 575 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/2] x86: Add basic support for the Congatec CGEB BIOS interface 2024-08-01 16:06 ` [PATCH v2 1/2] x86: Add basic support for the " Mary Strodl ` (2 preceding siblings ...) 2024-08-03 22:37 ` kernel test robot @ 2024-08-07 23:16 ` Andi Shyti 3 siblings, 0 replies; 10+ messages in thread From: Andi Shyti @ 2024-08-07 23:16 UTC (permalink / raw) To: Mary Strodl Cc: linux-kernel, akpm, urezki, hch, linux-mm, lee, linux-i2c, s.hauer, christian.gmeiner Hi Mary, On Thu, Aug 01, 2024 at 12:06:09PM GMT, Mary Strodl wrote: > 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@pengutronix.de> > Signed-off-by: Christian Gmeiner <christian.gmeiner@gmail.com> > Signed-off-by: Mary Strodl <mstrodl@csh.rit.edu> Please, next time make sure to run sparse before sending the patch and avoid the errors signalled by the kernel test robot. Thanks, Andi ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 2/2] i2c: Add Congatec CGEB I2C driver 2024-08-01 16:06 [PATCH v2 0/2] Add support for Congatec CGEB BIOS interface Mary Strodl 2024-08-01 16:06 ` [PATCH v2 1/2] x86: Add basic support for the " Mary Strodl @ 2024-08-01 16:06 ` Mary Strodl 2024-08-07 23:21 ` Andi Shyti 2024-08-07 23:14 ` [PATCH v2 0/2] Add support for Congatec CGEB BIOS interface Andi Shyti 2 siblings, 1 reply; 10+ messages in thread From: Mary Strodl @ 2024-08-01 16:06 UTC (permalink / raw) To: linux-kernel Cc: akpm, urezki, hch, linux-mm, lee, andi.shyti, linux-i2c, s.hauer, christian.gmeiner, Mary Strodl From: Sascha Hauer <s.hauer@pengutronix.de> This driver provides a I2C bus driver for the CGEB interface found on some Congatec x86 modules. No devices are registered on the bus, the user has to do this via the i2c device /sys interface. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Signed-off-by: Christian Gmeiner <christian.gmeiner@gmail.com> Signed-off-by: Mary Strodl <mstrodl@csh.rit.edu> --- drivers/i2c/busses/Kconfig | 7 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-congatec-cgeb.c | 189 +++++++++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 drivers/i2c/busses/i2c-congatec-cgeb.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index fe6e8a1bb607..504a0be54f04 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1261,6 +1261,13 @@ config I2C_RCAR This driver can also be built as a module. If so, the module will be called i2c-rcar. +config I2C_CONGATEC_CGEB + tristate "Congatec CGEB I2C driver" + depends on MFD_CONGATEC_CGEB + help + This driver provides support for the I2C busses accssable via + the Congatec CGEB interface found on Congatec boards. + comment "External I2C/SMBus adapter drivers" config I2C_DIOLAN_U2C diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 78d0561339e5..f4e9fa7542be 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -128,6 +128,7 @@ obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o obj-$(CONFIG_I2C_GXP) += i2c-gxp.o +obj-$(CONFIG_I2C_CONGATEC_CGEB) += i2c-congatec-cgeb.o # External I2C/SMBus adapter drivers obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o diff --git a/drivers/i2c/busses/i2c-congatec-cgeb.c b/drivers/i2c/busses/i2c-congatec-cgeb.c new file mode 100644 index 000000000000..5afee15ebdb3 --- /dev/null +++ b/drivers/i2c/busses/i2c-congatec-cgeb.c @@ -0,0 +1,189 @@ +/* + * CGEB i2c driver + * + * (c) 2011 Sascha Hauer, Pengutronix + * + * 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/i2c.h> +#include <linux/mfd/congatec-cgeb.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#define CG_I2C_FLAG_START 0x00080 /* send START condition */ +#define CG_I2C_FLAG_STOP 0x00040 /* send STOP condition */ +#define CG_I2C_FLAG_ALL_ACK 0x08000 /* send ACK on all read bytes */ +#define CG_I2C_FLAG_ALL_NAK 0x04000 /* send NAK on all read bytes */ + +struct cgeb_i2c_priv { + struct cgeb_board_data *board; + struct i2c_adapter adapter; + int unit; +}; + +static u32 cgeb_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static int cgeb_i2c_set_speed(struct cgeb_i2c_priv *priv, int speed) +{ + struct cgeb_function_parameters fps; + + memset(&fps, 0, sizeof(fps)); + + fps.unit = priv->unit; + fps.pars[0] = speed; + + return cgeb_call(priv->board, &fps, CgebI2CSetFrequency); +} + +static int cgeb_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num) +{ + struct cgeb_function_parameters fps; + int i, ret; + unsigned long flags = CG_I2C_FLAG_START; + struct cgeb_i2c_priv *priv = i2c_get_adapdata(adapter); + unsigned long rdlen, wrlen; + unsigned char *rdbuf, *wrbuf, *raw_wrbuf; + unsigned short lmax = 0; + + /* + * With cgeb the I2C address is part of the write data + * buffer, so allocate a buffer with the length of the + * longest write buffer + 1 + */ + for (i = 0; i < num; i++) + if (!(msgs[i].flags & I2C_M_RD)) + lmax = max(lmax, msgs[i].len); + + raw_wrbuf = kmalloc(lmax + 1, GFP_KERNEL); + if (!raw_wrbuf) + return -ENOMEM; + + for (i = 0; i < num; i++) { + if (msgs[i].flags & I2C_M_RD) { + rdbuf = msgs[i].buf; + rdlen = msgs[i].len; + wrbuf = NULL; + wrlen = 0; + } else { + rdbuf = NULL; + rdlen = 0; + wrbuf = msgs[i].buf; + wrlen = msgs[i].len; + } + + raw_wrbuf[0] = msgs[i].addr << 1; + if (wrlen) + memcpy(&raw_wrbuf[1], wrbuf, wrlen); + + if (msgs[i].flags & I2C_M_RD) + raw_wrbuf[0] |= 1; + + if (i == num - 1) + flags |= CG_I2C_FLAG_STOP; + + dev_dbg(&adapter->dev, + "%s: rd: %p/%ld wr: %p/%ld flags: 0x%08lx %s\n", + __func__, rdbuf, rdlen, raw_wrbuf, wrlen + 1, + flags, + msgs[i].flags & I2C_M_RD ? "READ" : "WRITE"); + + memset(&fps, 0, sizeof(fps)); + + fps.unit = priv->unit; + fps.pars[0] = wrlen + 1; + fps.pars[1] = rdlen; + fps.pars[2] = flags; + fps.iptr = raw_wrbuf; + fps.optr = rdbuf; + fps.optr_size = sizeof(*rdbuf) * rdlen; + fps.iptr_size = (wrlen + 1) * sizeof(*raw_wrbuf); + + ret = cgeb_call(priv->board, &fps, CgebI2CTransfer); + if (ret) { + ret = -EREMOTEIO; + goto out; + } + } + + ret = num; + +out: + kfree(raw_wrbuf); + + return ret; +} + +static struct i2c_algorithm cgeb_i2c_algo = { + .master_xfer = cgeb_i2c_xfer, + .functionality = cgeb_i2c_func, +}; + +static int cgeb_i2c_probe(struct platform_device *pdev) +{ + struct cgeb_i2c_priv *priv; + struct cgeb_pdata *pdata = pdev->dev.platform_data; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + strcpy(priv->adapter.name, pdev->name); + priv->adapter.owner = THIS_MODULE; + priv->adapter.algo = &cgeb_i2c_algo; + priv->adapter.dev.parent = &pdev->dev; + priv->unit = pdata->unit; + priv->board = pdata->board; + i2c_set_adapdata(&priv->adapter, priv); + + platform_set_drvdata(pdev, priv); + + ret = cgeb_i2c_set_speed(priv, 400000); + if (ret) + /* not a critical error, we can continue with the default speed. */ + dev_warn(&pdev->dev, "Could not set speed to 400KHz\n"); + + ret = i2c_add_adapter(&priv->adapter); + if (ret < 0) { + dev_err(&pdev->dev, "registration failed\n"); + return ret; + } + + dev_info(&pdev->dev, "registered\n"); + + return 0; +}; + +static int cgeb_i2c_remove(struct platform_device *pdev) +{ + struct cgeb_i2c_priv *priv = platform_get_drvdata(pdev); + + i2c_del_adapter(&priv->adapter); + + return 0; +} + +static struct platform_driver cgeb_i2c_driver = { + .probe = cgeb_i2c_probe, + .remove = cgeb_i2c_remove, + .driver = { + .name = "cgeb-i2c", + }, +}; +module_platform_driver(cgeb_i2c_driver); + +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); +MODULE_DESCRIPTION("cgeb i2c driver"); +MODULE_LICENSE("GPL"); -- 2.45.2 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/2] i2c: Add Congatec CGEB I2C driver 2024-08-01 16:06 ` [PATCH v2 2/2] i2c: Add Congatec CGEB I2C driver Mary Strodl @ 2024-08-07 23:21 ` Andi Shyti 2024-08-08 18:44 ` Mary Strodl 0 siblings, 1 reply; 10+ messages in thread From: Andi Shyti @ 2024-08-07 23:21 UTC (permalink / raw) To: Mary Strodl Cc: linux-kernel, akpm, urezki, hch, linux-mm, lee, linux-i2c, s.hauer, christian.gmeiner Hi Mary, On Thu, Aug 01, 2024 at 12:06:10PM GMT, Mary Strodl wrote: > From: Sascha Hauer <s.hauer@pengutronix.de> > > This driver provides a I2C bus driver for the CGEB interface > found on some Congatec x86 modules. No devices are registered > on the bus, the user has to do this via the i2c device /sys > interface. > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> > Signed-off-by: Christian Gmeiner <christian.gmeiner@gmail.com> > Signed-off-by: Mary Strodl <mstrodl@csh.rit.edu> > --- > drivers/i2c/busses/Kconfig | 7 + > drivers/i2c/busses/Makefile | 1 + > drivers/i2c/busses/i2c-congatec-cgeb.c | 189 +++++++++++++++++++++++++ > 3 files changed, 197 insertions(+) > create mode 100644 drivers/i2c/busses/i2c-congatec-cgeb.c Thanks for your patch, doesn't look bad. However, I'm going to ask to please run checkpatch.pl fix the suggested output and resend it. Thanks, Andi ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/2] i2c: Add Congatec CGEB I2C driver 2024-08-07 23:21 ` Andi Shyti @ 2024-08-08 18:44 ` Mary Strodl 0 siblings, 0 replies; 10+ messages in thread From: Mary Strodl @ 2024-08-08 18:44 UTC (permalink / raw) To: Andi Shyti Cc: Mary Strodl, linux-kernel, akpm, urezki, hch, linux-mm, lee, linux-i2c, s.hauer, christian.gmeiner On Thu, Aug 08, 2024 at 12:21:24AM +0100, Andi Shyti wrote: > However, I'm going to ask to please run checkpatch.pl fix the > suggested output and resend it. Thanks for taking a look! I didn't know about checkpatch and sparse before now, so I appreciate the messages :) I sent a new version (v3) that should address the feedback. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 0/2] Add support for Congatec CGEB BIOS interface 2024-08-01 16:06 [PATCH v2 0/2] Add support for Congatec CGEB BIOS interface Mary Strodl 2024-08-01 16:06 ` [PATCH v2 1/2] x86: Add basic support for the " Mary Strodl 2024-08-01 16:06 ` [PATCH v2 2/2] i2c: Add Congatec CGEB I2C driver Mary Strodl @ 2024-08-07 23:14 ` Andi Shyti 2 siblings, 0 replies; 10+ messages in thread From: Andi Shyti @ 2024-08-07 23:14 UTC (permalink / raw) To: Mary Strodl Cc: linux-kernel, akpm, urezki, hch, linux-mm, lee, linux-i2c, s.hauer, christian.gmeiner Hi Mary, On Thu, Aug 01, 2024 at 12:06:08PM GMT, Mary Strodl wrote: > The following series adds support for the Congatec CGEB interface > found on some Congatec x86 boards. The CGEB interface is a BIOS > interface which provides access to onboard peripherals like I2C > busses and watchdogs. It works by mapping BIOS code and searching > for magic values which specify the entry points to the CGEB call. > The CGEB call is an API provided by the BIOS which provides access > to the functions in an ioctl like fashion. > > At the request of some folks last time this series went out, CGEB > now has a userspace component which runs the x86 blob (rather than > running it directly in the kernel), which sends requests back and > forth using the cn_netlink API. this little paragraph is the closest to a changelog I can see. Could you please write up a real changlog and list all the changes from v1 to v2? You can do it as reply to this e-mail, but please, next time do it either in the cover letter or for each patch. Thanks, Andi > You can find a reference implementation of the userspace helper here: > https://github.com/Mstrodl/cgeb-helper > > I didn't get an answer when I asked where the userspace component > should live, so I didn't put a ton of work into getting the helper > up to snuff since similar userspace helpers (like v86d) are not > in-tree. If folks would like the helper in-tree, that's fine too. > > This series is based on the excellent work of Sascha Hauer and > Christian Gmeiner. You can find their original work here: > > http://patchwork.ozlabs.org/patch/219756/ > http://patchwork.ozlabs.org/patch/219755/ > http://patchwork.ozlabs.org/patch/219757/ > > http://patchwork.ozlabs.org/patch/483262/ > http://patchwork.ozlabs.org/patch/483264/ > http://patchwork.ozlabs.org/patch/483261/ > http://patchwork.ozlabs.org/patch/483263/ > > Mary Strodl (1): > x86: Add basic support for the Congatec CGEB BIOS interface > > Sascha Hauer (1): > i2c: Add Congatec CGEB I2C driver > > drivers/i2c/busses/Kconfig | 7 + > drivers/i2c/busses/Makefile | 1 + > drivers/i2c/busses/i2c-congatec-cgeb.c | 189 ++++ > drivers/mfd/Kconfig | 10 + > drivers/mfd/Makefile | 1 + > drivers/mfd/congatec-cgeb.c | 1139 ++++++++++++++++++++++++ > include/linux/mfd/congatec-cgeb.h | 111 +++ > include/uapi/linux/connector.h | 4 +- > 8 files changed, 1461 insertions(+), 1 deletion(-) > create mode 100644 drivers/i2c/busses/i2c-congatec-cgeb.c > create mode 100644 drivers/mfd/congatec-cgeb.c > create mode 100644 include/linux/mfd/congatec-cgeb.h > > -- > 2.45.2 > ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2024-08-08 18:44 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-08-01 16:06 [PATCH v2 0/2] Add support for Congatec CGEB BIOS interface Mary Strodl 2024-08-01 16:06 ` [PATCH v2 1/2] x86: Add basic support for the " Mary Strodl 2024-08-03 21:46 ` kernel test robot 2024-08-03 22:07 ` kernel test robot 2024-08-03 22:37 ` kernel test robot 2024-08-07 23:16 ` Andi Shyti 2024-08-01 16:06 ` [PATCH v2 2/2] i2c: Add Congatec CGEB I2C driver Mary Strodl 2024-08-07 23:21 ` Andi Shyti 2024-08-08 18:44 ` Mary Strodl 2024-08-07 23:14 ` [PATCH v2 0/2] Add support for Congatec CGEB BIOS interface Andi Shyti
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox