lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20230815165943.GB648357@google.com>
Date:   Tue, 15 Aug 2023 17:59:43 +0100
From:   Lee Jones <lee@...nel.org>
To:     "Wenkai.Chung" <Wenkai.Chung@...antech.com.tw>
Cc:     "'linux-kernel@...r.kernel.org'" <linux-kernel@...r.kernel.org>,
        "Susi.Driver" <Susi.Driver@...antech.com>
Subject: Re: [PATCH] Add a mfd driver to support Advantech EIO-IS200 series
 EC.

On Thu, 10 Aug 2023, Wenkai.Chung wrote:

> Hi Jones,
> 
> I am writing to follow up on the email and patch I sent you on July 27th. 
> Did you receive the patch? I appreciate your response.

I have the patch.  It's in my queue.

> -----Original Message-----
> From: Wenkai.Chung 
> Sent: Thursday, July 27, 2023 1:43 PM
> To: 'Lee Jones' <lee@...nel.org>; 'linux-kernel@...r.kernel.org' <linux-kernel@...r.kernel.org>
> Cc: Susi.Driver <Susi.Driver@...antech.com>
> Subject: [PATCH] Add a mfd driver to support Advantech EIO-IS200 series EC.
> 
> 
> Add a mfd driver to support Advantech EIO-IS200 series EC.
> 
> Signed-off-by: wenkai.chung <wenkai.chung@...antech.com.tw>
> ---
>  drivers/mfd/Kconfig         |  13 +
>  drivers/mfd/Makefile        |   1 +
>  drivers/mfd/eiois200.h      | 146 +++++++++++
>  drivers/mfd/eiois200_core.c | 496 ++++++++++++++++++++++++++++++++++++
>  4 files changed, 656 insertions(+)
>  create mode 100644 drivers/mfd/eiois200.h  create mode 100644 drivers/mfd/eiois200_core.c
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 6f5b259a6d6a..ca792a077da9 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -154,6 +154,19 @@ config MFD_ATMEL_HLCDC
>  	  This driver provides common support for accessing the device,
>  	  additional drivers must be enabled in order to use the
>  	  functionality of the device.
> +	  
> + config MFD_EIOIS200
> +	tristate "Advantech EIO-IS200 Embedded Controller core driver"
> +	depends on X86 && m
> +	default m
> +	select MFD_CORE
> +	help
> +	  Support for the EIO-IS200 controller.
> +
> +	  This driver provides common support for accessing the device,
> +	  additional drivers must be enabled in order to use the functionality
> +	  of the device.
> +
>  
>  config MFD_ATMEL_SMC
>  	bool
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f3d1f1dc73b5..59e911054688 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -276,6 +276,7 @@ obj-$(CONFIG_MFD_INTEL_M10_BMC_PMCI)   += intel-m10-bmc-pmci.o
>  
>  obj-$(CONFIG_MFD_ATC260X)	+= atc260x-core.o
>  obj-$(CONFIG_MFD_ATC260X_I2C)	+= atc260x-i2c.o
> +obj-$(CONFIG_MFD_EIOIS200)	+= eiois200_core.o
>  
>  rsmu-i2c-objs			:= rsmu_core.o rsmu_i2c.o
>  rsmu-spi-objs			:= rsmu_core.o rsmu_spi.o
> diff --git a/drivers/mfd/eiois200.h b/drivers/mfd/eiois200.h new file mode 100644 index 000000000000..24a448d70d00
> --- /dev/null
> +++ b/drivers/mfd/eiois200.h
> @@ -0,0 +1,146 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +
> +#ifndef _MFD_EIOIS200_H_
> +#define _MFD_EIOIS200_H_
> +#include <linux/stddef.h>
> +#include <linux/io.h>
> +#include <linux/regmap.h>
> +
> +// Definition
> +#define EIOIS200_CHIPID1				0x20
> +#define EIOIS200_CHIPID2				0x21
> +#define EIOIS200_CHIPVER				0x22
> +#define EIOIS200_SIOCTRL				0x23
> +#define EIOIS200_SIOCTRL_SIOEN				BIT(0)
> +#define EIOIS200_SIOCTRL_SWRST				BIT(1)
> +#define EIOIS200_IRQCTRL				0x70
> +#define EIOIS200_CHIP_ID				0x9610
> +#define EIO201_211_CHIP_ID				0x9620
> +#define EIOIS200_ICCODE					0x10
> +#define EIO201_ICCODE					0x20
> +#define EIO211_ICCODE					0x21
> +
> +// LPC PNP
> +#define EIOIS200_PNP_INDEX				0x299
> +#define EIOIS200_PNP_DATA				0x29A
> +#define EIOIS200_SUB_PNP_INDEX				0x499
> +#define EIOIS200_SUB_PNP_DATA				0x49A
> +#define EIOIS200_EXT_MODE_ENTER				0x87
> +#define EIOIS200_EXT_MODE_EXIT				0xAA
> +
> +// LPC LDN
> +#define EIOIS200_LDN					0x07
> +#define EIOIS200_LDN_PMC0				0x0C
> +#define EIOIS200_LDN_PMC1				0x0D
> +
> +// PMC common registers
> +#define EIOIS200_LDAR					0x30
> +#define EIOIS200_LDAR_LDACT				BIT(0)
> +#define EIOIS200_IOBA0H					0x60
> +#define EIOIS200_IOBA0L					0x61
> +#define EIOIS200_IOBA1H					0x62
> +#define EIOIS200_IOBA1L					0x63
> +#define EIOIS200_PMC_STATUS_IBF				BIT(1)
> +#define EIOIS200_PMC_STATUS_OBF				BIT(0)
> +#define EIOIS200_PMC_PORT				0x2F0
> +
> +// PMC (Power management channel) command list
> +#define EIOIS200_PMC_CMD_WDT_WRITE			0x2A
> +#define EIOIS200_PMC_CMD_WDT_READ			0x2B
> +#define EIOIS200_PMC_CMD_WDT_START			0x2C
> +#define EIOIS200_PMC_CMD_WDT_STOP			0x2D
> +#define EIOIS200_PMC_CMD_WDT_TRIG			0x2E
> +#define EIOIS200_PMC_CMD_ACPIRAM_READ			0x31
> +#define EIOIS200_PMC_CMD_CFG_SAVE			0x56
> +
> +// New PMC command list
> +#define EIOIS200_NEW_PMC_CMD_DOC_FW_READ		0x03
> +#define EIOIS200_NEW_PMC_CMD_SYSTEM_READ		0x55
> +#define EIOIS200_NEW_PMC_CMD_WDT_WRITE			0x2A
> +#define EIOIS200_NEW_PMC_CMD_WDT_READ			0x2B
> +
> +// OLD PMC
> +#define EIOIS200_PMC_NO_INDEX				0xFF
> +
> +// ACPI RAM Address Table
> +#define EIOIS200_ACPIRAM_VERSIONSECTION	(0xFA)
> +#define EIOIS200_ACPIRAM_ICVENDOR	(EIOIS200_ACPIRAM_VERSIONSECTION + 0x00)
> +#define EIOIS200_ACPIRAM_ICCODE		(EIOIS200_ACPIRAM_VERSIONSECTION + 0x01)
> +#define EIOIS200_ACPIRAM_CODEBASE	(EIOIS200_ACPIRAM_VERSIONSECTION + 0x02)
> +
> +/* Firmware **/
> +#define EIOIS200_F_SUB_NEW_CODE_BASE	BIT(6)	// Identity second EC code base version
> +#define EIOIS200_F_SUB_CHANGED		BIT(7)	// Setting second EC changed
> +#define EIOIS200_F_NEW_CODE_BASE	BIT(8)	// Identity code base version
> +#define EIOIS200_F_CHANGED		BIT(9)	// Setting changed
> +#define EIOIS200_F_SUB_CHIP_EXIST	BIT(30)	// Second EIO-IS200 exist
> +#define EIOIS200_F_CHIP_EXIST		BIT(31)	// EIO-IS200 exist
> +
> +/* Others **/
> +#define EC_NUM	2
> +
> +struct _pmc_port {
> +	union {
> +		u16 cmd;
> +		u16 status;
> +	};
> +	u16 data;
> +};
> +
> +struct _eiois200_dev_port {
> +	u16 idx;
> +	u16 data;
> +};
> +
> +struct _pmc_op {
> +	u8 cmd;
> +	u8 index;	// use 0xFF to identify PMC command with index or not.
> +	u8 offset;
> +	u8 len;
> +	u8 *data;
> +};
> +
> +struct _pmc_new_op {
> +	u8  cmd;
> +	u8  control;
> +	u8  device_id;
> +	u8  size;
> +	u8  *payload;
> +};
> +
> +enum eiois200_rw_operation {
> +	OPERATION_READ,
> +	OPERATION_WRITE,
> +};
> +
> +struct _eiois200_dev {
> +	u32 flag;
> +
> +	struct _pmc_port  pmc;
> +	struct _pmc_port  pmc1;
> +
> +	struct mutex eiois200_io_mutex;		// mutex lock for eiois200 io access
> +};
> +
> +/* exported symbol */
> +//extern struct _eiois200_dev *eiois200_dev; int 
> +eiois200_core_pmc_operation(const struct _pmc_port *pmc,
> +				const struct _pmc_op *operation,
> +				enum eiois200_rw_operation rw);
> +
> +int eiois200_core_new_pmc_operation(const struct _pmc_port *pmc,
> +				    const struct _pmc_new_op *operation);
> +
> +int eiois200_core_pmc_wait_ibf(const struct _pmc_port *pmc); int 
> +eiois200_core_pmc_wait_obf(const struct _pmc_port *pmc);
> +
> +#define WAIT_IBF(pmc)		eiois200_core_pmc_wait_ibf(pmc)
> +#define WAIT_OBF(pmc)		eiois200_core_pmc_wait_obf(pmc)
> +#define NEW_CODE_BASE		(eiois200_dev->flag & EIOIS200_F_NEW_CODE_BASE)
> +
> +#ifdef pr_fmt
> +#undef pr_fmt
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #endif
> +
> +#endif
> diff --git a/drivers/mfd/eiois200_core.c b/drivers/mfd/eiois200_core.c new file mode 100644 index 000000000000..4be6c8651b6a
> --- /dev/null
> +++ b/drivers/mfd/eiois200_core.c
> @@ -0,0 +1,496 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * MFD driver for Advantech EIO-IS200 Embedded controller.
> + *
> + * This driver provides PMC commands interface for subdrivers.
> + *
> + * Copyright (C) 2023 Advantech Corporation. All rights reserved.
> + */
> +
> +#include <linux/uaccess.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mutex.h>
> +#include <linux/delay.h>
> +#include <linux/isa.h>
> +#include "eiois200.h"
> +
> +static int timeout = 500;
> +module_param(timeout, int, 0);
> +MODULE_PARM_DESC(timeout,
> +		 "IO exponential increased timeout value. (default="
> +		 __MODULE_STRING(timeout) ")");
> +
> +struct _eiois200_dev_port eio_pnp_port[EC_NUM] = {
> +	{EIOIS200_PNP_INDEX,	 EIOIS200_PNP_DATA    },
> +	{EIOIS200_SUB_PNP_INDEX, EIOIS200_SUB_PNP_DATA} };
> +
> +struct _eiois200_dev *eiois200_dev;
> +struct regmap *regmap_is200;
> +
> +static struct mfd_cell susi_mfd_devs[] = {
> +	{ .name = "eiois200_wdt"},
> +};
> +
> +/* For regmap */
> +static int reg_read(void *context, unsigned int reg, unsigned int *val) 
> +{
> +	*val = inb(reg);
> +
> +	return 0;
> +}
> +
> +static int reg_write(void *context, unsigned int reg, unsigned int val) 
> +{
> +	outb(val, reg);
> +
> +	return 0;
> +}
> +
> +struct regmap_range is200_rang[] = {
> +	{EIOIS200_PNP_INDEX,	 EIOIS200_PNP_DATA	 },
> +	{EIOIS200_SUB_PNP_INDEX, EIOIS200_SUB_PNP_DATA	 },
> +	{EIOIS200_PMC_PORT,	 EIOIS200_PMC_PORT + 0x0F},
> +};
> +
> +static const struct regmap_access_table volatile_regs = {
> +	.yes_ranges = is200_rang,
> +	.n_yes_ranges = ARRAY_SIZE(is200_rang), };
> +
> +static const struct regmap_config pnp_regmap_config = {
> +	.reg_bits	= 16,
> +	.val_bits	= 8,
> +	.volatile_table = &volatile_regs,
> +
> +	.reg_write	= reg_write,
> +	.reg_read	= reg_read,
> +};
> +
> +/* For EIO-IS200 pnp io port access */
> +static int is200_pnp_in(const struct _eiois200_dev_port *port, u8 idx) 
> +{
> +	int ret;
> +
> +	regmap_write(regmap_is200, port->idx, idx);
> +	regmap_read(regmap_is200, port->data, &ret);
> +
> +	return ret;
> +}
> +
> +static void is200_pnp_out(const struct _eiois200_dev_port *port, u8 
> +idx, u8 data) {
> +	regmap_write(regmap_is200, port->idx, idx);
> +	regmap_write(regmap_is200, port->data, data); }
> +
> +static void is200_ext_entry(const struct _eiois200_dev_port *port) {
> +	regmap_write(regmap_is200, port->idx, EIOIS200_EXT_MODE_ENTER);
> +	regmap_write(regmap_is200, port->idx, EIOIS200_EXT_MODE_ENTER); }
> +
> +static void eio_ext_leave(const struct _eiois200_dev_port *port) {
> +	regmap_write(regmap_is200, port->idx, EIOIS200_EXT_MODE_EXIT); }
> +
> +/* EIO-IS200 io port access function for pmc command */ static int 
> +pmc_out_data(const struct _pmc_port *pmc, u8 value) {
> +	if (WAIT_IBF(pmc))
> +		return -ETIME;
> +
> +	regmap_write(regmap_is200, pmc->data, value);
> +
> +	return 0;
> +}
> +
> +static int pmc_out_cmd(const struct _pmc_port *pmc, u8 value) {
> +	if (WAIT_IBF(pmc))
> +		return -ETIME;
> +
> +	regmap_write(regmap_is200, pmc->cmd, value);
> +
> +	return 0;
> +}
> +
> +static int pmc_in_data(const struct _pmc_port *pmc, u8 *value) {
> +	int val;
> +
> +	if (WAIT_OBF(pmc))
> +		return -ETIME;
> +
> +	regmap_read(regmap_is200, pmc->data, &val);
> +
> +	*value = val & 0xFF;
> +
> +	return 0;
> +}
> +
> +static int pmc_in_status(const struct _pmc_port *pmc) {
> +	int val;
> +
> +	regmap_read(regmap_is200, pmc->data, &val);
> +
> +	return val;
> +}
> +
> +static void pmc_clear(const struct _pmc_port *pmc) {
> +	int val;
> +
> +	/* Read out previous garbage if needed */
> +	if (pmc_in_status(pmc) & EIOIS200_PMC_STATUS_IBF)
> +		regmap_read(regmap_is200, pmc->data, &val); }
> +
> +/* Wait input buffer clear */
> +int eiois200_core_pmc_wait_ibf(const struct _pmc_port *pmc) {
> +	u32 time;
> +	unsigned int val;
> +
> +	for (time = 0 ; time < timeout ; time++) {
> +		regmap_read(regmap_is200, pmc->status, &val);
> +		if ((val & EIOIS200_PMC_STATUS_IBF) == 0)
> +			return 0;
> +
> +		/* incremental delay */
> +		if (time * 10 < 1000)
> +			ndelay(time * 10);
> +		else
> +			msleep(time / 100);
> +	}
> +
> +	/* over 0.5 s */
> +	return -ETIME;
> +}
> +EXPORT_SYMBOL(eiois200_core_pmc_wait_ibf);
> +
> +/* Wait output buffer filled */
> +int eiois200_core_pmc_wait_obf(const struct _pmc_port *pmc) {
> +	u32 time;
> +	unsigned int val;
> +
> +	for (time = 0 ; time < timeout ; time++) {
> +		regmap_read(regmap_is200, pmc->status, &val);
> +		if ((val & EIOIS200_PMC_STATUS_OBF))
> +			return 0;
> +
> +		/* incremental delay */
> +		if (time * 10 < 1000)
> +			ndelay(time * 10);
> +		else
> +			msleep(time / 100);
> +	}
> +
> +	/* over 0.5 s */
> +	return -ETIME;
> +}
> +EXPORT_SYMBOL(eiois200_core_pmc_wait_obf);
> +
> +/* Execute a old pmc (power management channel) command */ int 
> +eiois200_core_pmc_operation(const struct _pmc_port *pmc,
> +				const struct _pmc_op *op,
> +				enum eiois200_rw_operation rw)
> +{
> +	bool read_cmd = rw == OPERATION_READ;
> +	bool has_index = op->index == EIOIS200_PMC_NO_INDEX;
> +
> +	mutex_lock(&eiois200_dev->eiois200_io_mutex);
> +
> +	if (rw != OPERATION_READ && rw != OPERATION_WRITE)
> +		return -EINVAL;
> +
> +	pmc_clear(pmc);
> +
> +	if (pmc_out_cmd(pmc, op->cmd))
> +		goto exit;
> +
> +	if (has_index && pmc_out_data(pmc, op->index))
> +		goto exit;
> +
> +	if (pmc_out_data(pmc, op->offset) ||
> +	    pmc_out_data(pmc, op->len))
> +		goto exit;
> +
> +	for (u8 i = 0 ; i < op->len ; i++)
> +		if (read_cmd) {
> +			if (pmc_in_data(pmc, &op->data[i]))
> +				goto exit;
> +		} else {
> +			if (pmc_out_data(pmc, op->data[i]))
> +				goto exit;
> +		}
> +
> +	mutex_unlock(&eiois200_dev->eiois200_io_mutex);
> +	return 0;
> +
> +exit:
> +	mutex_unlock(&eiois200_dev->eiois200_io_mutex);
> +	return -ETIME;
> +}
> +EXPORT_SYMBOL(eiois200_core_pmc_operation);
> +
> +/* Execute a new pmc command (fixed length) */ int 
> +eiois200_core_new_pmc_operation(const struct _pmc_port *pmc, const 
> +struct _pmc_new_op *op) {
> +	bool read_cmd = op->cmd & 0x01;
> +
> +	mutex_lock(&eiois200_dev->eiois200_io_mutex);
> +
> +	pmc_clear(pmc);
> +
> +	if (pmc_out_cmd(pmc, op->cmd)	     ||
> +	    pmc_out_data(pmc, op->control)   ||
> +	    pmc_out_data(pmc, op->device_id) ||
> +	    pmc_out_data(pmc, op->size))
> +		goto exit;
> +
> +	for (u8 i = 0 ; i < op->size ; i++)
> +		if (read_cmd) {
> +			if (pmc_in_data(pmc, &op->payload[i]))
> +				goto exit;
> +		} else {
> +			if (pmc_out_data(pmc, op->payload[i]))
> +				goto exit;
> +		}
> +
> +	mutex_unlock(&eiois200_dev->eiois200_io_mutex);
> +	return 0;
> +
> +exit:
> +	mutex_unlock(&eiois200_dev->eiois200_io_mutex);
> +	return -ETIME;
> +}
> +EXPORT_SYMBOL(eiois200_core_new_pmc_operation);
> +
> +static int get_pmc_port(void)
> +{
> +	u8 i = 0;
> +
> +	mutex_lock(&eiois200_dev->eiois200_io_mutex);
> +
> +	for (i = 0 ; i < EC_NUM ; i++) {
> +		struct _eiois200_dev_port *port = eio_pnp_port + i;
> +		struct _pmc_port *pmc_ptr = i == 0 ? &eiois200_dev->pmc : 
> +&eiois200_dev->pmc1;
> +
> +		is200_ext_entry(port);
> +
> +		/* Switch to PMC device */
> +		is200_pnp_out(port, EIOIS200_LDN, EIOIS200_LDN_PMC1);
> +
> +		/* Active device */
> +		is200_pnp_out(port, EIOIS200_LDAR, EIOIS200_LDAR_LDACT);
> +
> +		/* Get PMC base port address */
> +		pmc_ptr->data  = is200_pnp_in(port, EIOIS200_IOBA0H) << 8;
> +		pmc_ptr->data |= is200_pnp_in(port, EIOIS200_IOBA0L);
> +		pmc_ptr->cmd   = is200_pnp_in(port, EIOIS200_IOBA1H) << 8;
> +		pmc_ptr->cmd  |= is200_pnp_in(port, EIOIS200_IOBA1L);
> +
> +		/* Disable IRQ */
> +		is200_pnp_out(port, EIOIS200_IRQCTRL, 0);
> +
> +		eio_ext_leave(port);
> +
> +		if ((eiois200_dev->flag & EIOIS200_F_SUB_CHIP_EXIST) == 0)
> +			break;
> +	}
> +
> +	mutex_unlock(&eiois200_dev->eiois200_io_mutex);
> +
> +	return 0;
> +}
> +
> +static int eiois200_exist(void)
> +{
> +	u16 chipid = 0;
> +	u8  tmp = 0, i = 0;
> +
> +	for (i = 0 ; i < EC_NUM ; i++) {
> +		struct _eiois200_dev_port *port = eio_pnp_port + i;
> +
> +		is200_ext_entry(port);
> +
> +		chipid  = is200_pnp_in(port, EIOIS200_CHIPID1) << 8;
> +		chipid |= is200_pnp_in(port, EIOIS200_CHIPID2);
> +
> +		if (chipid == EIOIS200_CHIP_ID || chipid == EIO201_211_CHIP_ID) {
> +			/* Enable SIO devices */
> +			pr_info("chipID = 0x%X\n", chipid);
> +			tmp = is200_pnp_in(port, EIOIS200_SIOCTRL);
> +			tmp |= EIOIS200_SIOCTRL_SIOEN;
> +			is200_pnp_out(port, EIOIS200_SIOCTRL, tmp);
> +
> +			eiois200_dev->flag |= EIOIS200_F_CHIP_EXIST;
> +
> +			if (port->data == EIOIS200_SUB_PNP_DATA)
> +				eiois200_dev->flag |= EIOIS200_F_SUB_CHIP_EXIST;
> +
> +			get_pmc_port();
> +
> +			eio_ext_leave(port);
> +			return 0;
> +		}
> +
> +		eio_ext_leave(port);
> +	}
> +
> +	return -ENODEV;
> +}
> +
> +/* read information of acpi store in EC */ static uint8_t 
> +acpiram_access(const struct _pmc_port *pmc, uint8_t acpi_ram_address) {
> +	u8 ret;
> +
> +	mutex_lock(&eiois200_dev->eiois200_io_mutex);
> +
> +	/* clear previous garbage */
> +	pmc_clear(pmc);
> +	msleep(1);
> +
> +	/* procedure for read data */
> +	if (pmc_out_cmd(pmc, EIOIS200_PMC_CMD_ACPIRAM_READ) ||
> +	    pmc_out_data(pmc, acpi_ram_address)	||
> +	    pmc_out_data(pmc, 1) ||
> +	    pmc_in_data(pmc, &ret)) {
> +		/* return 0 for error */
> +		mutex_unlock(&eiois200_dev->eiois200_io_mutex);
> +		return 0;
> +	}
> +
> +	mutex_unlock(&eiois200_dev->eiois200_io_mutex);
> +	return ret;
> +}
> +
> +static int firmware_code_base(struct device *dev,
> +			      const struct _pmc_port *pmc)
> +{
> +	u8 ic_vendor, ic_code, code_base;
> +
> +	ic_vendor = acpiram_access(pmc, EIOIS200_ACPIRAM_ICVENDOR);
> +	ic_code   = acpiram_access(pmc, EIOIS200_ACPIRAM_ICCODE);
> +	code_base = acpiram_access(pmc, EIOIS200_ACPIRAM_CODEBASE);
> +
> +	if (ic_vendor != 'R')
> +		goto exit;
> +
> +	if (ic_code != EIOIS200_ICCODE &&
> +	    ic_code != EIO201_ICCODE   &&
> +	    ic_code != EIO211_ICCODE)
> +		goto exit;
> +
> +	if (code_base == 0x80) {
> +		eiois200_dev->flag |= EIOIS200_F_NEW_CODE_BASE;
> +		return 0;
> +	}
> +
> +	if (code_base == 0 && (ic_code == EIO201_ICCODE ||
> +			       ic_code == EIO211_ICCODE))
> +		return 0;
> +
> + exit:
> +	dev_err(dev,
> +		"Codebase check fail:\n"
> +		"ic_vendor: 0x%X  ,ic_code : 0x%X ,code_base : 0x%X\n",
> +		ic_vendor, ic_code, code_base);
> +	return -ENODEV;
> +}
> +
> +int eiois200_probe(struct device *dev, unsigned int id) {
> +	int ret = 0;
> +
> +	pr_err("timeout=%d\n", timeout);
> +
> +	eiois200_dev = devm_kzalloc(dev, sizeof(struct _eiois200_dev), GFP_KERNEL);
> +	if (!eiois200_dev)
> +		return -ENOMEM;
> +
> +	mutex_init(&eiois200_dev->eiois200_io_mutex);
> +
> +	regmap_is200 = devm_regmap_init(dev, NULL, eiois200_dev, &pnp_regmap_config);
> +	if (!regmap_is200)
> +		return -ENOMEM;
> +
> +	if (eiois200_exist()) {
> +		dev_err(dev, "EIO IS-200 not detected.\n");
> +		return -ENODEV;
> +	}
> +
> +	if (firmware_code_base(dev, &eiois200_dev->pmc)) {
> +		dev_err(dev, "Codebase check fail\n");
> +		return -EIO;
> +	}
> +
> +	dev_set_drvdata(dev, eiois200_dev);
> +
> +	ret = devm_mfd_add_devices(dev, -1, susi_mfd_devs,
> +				   ARRAY_SIZE(susi_mfd_devs),
> +				   NULL, -1, NULL);
> +	if (ret)
> +		dev_err(dev, "Cannot add sub device (error = %d )\n", ret);
> +
> +	return ret;
> +}
> +
> +void eiois200_config_save(const struct _pmc_port *pmc) {
> +	mutex_lock(&eiois200_dev->eiois200_io_mutex);
> +
> +	pmc_out_cmd(pmc, EIOIS200_PMC_CMD_CFG_SAVE);
> +
> +	mutex_unlock(&eiois200_dev->eiois200_io_mutex);
> +}
> +
> +static int eiois200_store_settings(void) {
> +	if ((eiois200_dev->flag & EIOIS200_F_CHIP_EXIST) == 0)
> +		return 0;
> +
> +	if (NEW_CODE_BASE)
> +		return 0;
> +
> +	if (eiois200_dev->flag & EIOIS200_F_CHANGED)
> +		eiois200_config_save(&eiois200_dev->pmc);
> +
> +	if (eiois200_dev->flag & EIOIS200_F_SUB_CHANGED)
> +		eiois200_config_save(&eiois200_dev->pmc1);
> +
> +	return 0;
> +}
> +
> +void eiois200_shutdown(struct device *dev, unsigned int id) {
> +	eiois200_store_settings();
> +}
> +
> +int eiois200_suspend(struct device *dev, unsigned int id, pm_message_t 
> +msg) {
> +	return eiois200_store_settings();
> +}
> +
> +static struct isa_driver eiois200_driver = {
> +	.probe    = eiois200_probe,
> +	.shutdown = eiois200_shutdown,
> +	.suspend  = eiois200_suspend,
> +
> +	.driver = {
> +		.owner = THIS_MODULE,
> +		.name  = KBUILD_MODNAME,
> +	},
> +};
> +
> +module_isa_driver(eiois200_driver, 1);
> +
> +MODULE_AUTHOR("susi.driver <susi.driver@...antech.com.tw>"); 
> +MODULE_DESCRIPTION("Mfd driver for Advantech EIO-IS200 EC"); 
> +MODULE_LICENSE("GPL v2");
> +
> --
> 2.34.1
> 

-- 
Lee Jones [李琼斯]

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ