Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1305032 > unrolled thread
| Started by | richard.dorsch@gmail.com |
|---|---|
| First post | 2016-01-08 23:30 +0100 |
| Last post | 2016-01-08 23:40 +0100 |
| Articles | 11 — 2 participants |
Back to article view | Back to linux.kernel
[PATCH 0/6] Add Advantech iManager EC driver set richard.dorsch@gmail.com - 2016-01-08 23:30 +0100
[PATCH 2/6] Add Advantech iManager GPIO driver richard.dorsch@gmail.com - 2016-01-08 23:30 +0100
[PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot <lkp@intel.com> - 2016-01-09 02:00 +0100
Re: [PATCH 2/6] Add Advantech iManager GPIO driver kbuild test robot <lkp@intel.com> - 2016-01-09 02:00 +0100
[PATCH 7/8] Remove .owner from platform_driver struct richard.dorsch@gmail.com - 2016-01-10 08:50 +0100
[PATCH 6/6] Add Advantech iManager Watchdog driver richard.dorsch@gmail.com - 2016-01-08 23:30 +0100
[PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot <lkp@intel.com> - 2016-01-09 03:10 +0100
Re: [PATCH 6/6] Add Advantech iManager Watchdog driver kbuild test robot <lkp@intel.com> - 2016-01-09 03:10 +0100
[PATCH 8/8] Remove .owner from platform_driver struct richard.dorsch@gmail.com - 2016-01-10 08:50 +0100
[PATCH 5/6] Add Advantech iManager Backlight driver richard.dorsch@gmail.com - 2016-01-08 23:40 +0100
[PATCH 4/6] Add Advantech iManager I2C driver richard.dorsch@gmail.com - 2016-01-08 23:40 +0100
| From | richard.dorsch@gmail.com |
|---|---|
| Date | 2016-01-08 23:30 +0100 |
| Subject | [PATCH 0/6] Add Advantech iManager EC driver set |
| Message-ID | <qONHI-4JZ-3@gated-at.bofh.it> |
From: Richard Vidal-Dorsch <richard.dorsch@gmail.com> The Advantech iManager is an embedded controller (depending on board type, ITE it8518/it8528) on which a custom firmware runs. All drivers (GPIO, I2C, HWmon, Watchdog, and backlight) are being managed by the imanager mfd driver. The imanager mfd driver acts as a 'gateway' and handles all communications between the EC and sub-drivers (GPIO, I2C, ...). The feature specific communication portions are split up into core files such as imanager-ec-[gpio, i2c, ...]. Richard Vidal-Dorsch (6): Add Advantech iManager MFD core driver Add Advantech iManager GPIO driver Add Advantech iManager HWmon driver Add Advantech iManager I2C driver Add Advantech iManager Backlight driver Add Advantech iManager Watchdog driver Documentation/devicetree/bindings/mfd/imanager.txt | 33 + Documentation/hwmon/imanager | 59 + Documentation/i2c/busses/i2c-imanager | 48 + MAINTAINERS | 13 + drivers/gpio/Kconfig | 8 + drivers/gpio/Makefile | 2 + drivers/gpio/imanager-ec-gpio.c | 98 ++ drivers/gpio/imanager-gpio.c | 182 +++ drivers/hwmon/Kconfig | 12 + drivers/hwmon/Makefile | 2 + drivers/hwmon/imanager-ec-hwmon.c | 606 +++++++++ drivers/hwmon/imanager-hwmon.c | 1058 +++++++++++++++ drivers/i2c/busses/Kconfig | 11 + drivers/i2c/busses/Makefile | 2 + drivers/i2c/busses/imanager-ec-i2c.c | 466 +++++++ drivers/i2c/busses/imanager-i2c.c | 240 ++++ drivers/mfd/Kconfig | 20 + drivers/mfd/Makefile | 2 + drivers/mfd/imanager-core.c | 288 +++++ drivers/mfd/imanager-ec.c | 1345 ++++++++++++++++++++ drivers/video/backlight/Kconfig | 12 + drivers/video/backlight/Makefile | 2 + drivers/video/backlight/imanager-bl.c | 199 +++ drivers/video/backlight/imanager-ec-bl.c | 118 ++ drivers/watchdog/Kconfig | 12 + drivers/watchdog/Makefile | 2 + drivers/watchdog/imanager-ec-wdt.c | 170 +++ drivers/watchdog/imanager-wdt.c | 334 +++++ include/linux/mfd/imanager/backlight.h | 37 + include/linux/mfd/imanager/core.h | 31 + include/linux/mfd/imanager/ec.h | 210 +++ include/linux/mfd/imanager/gpio.h | 27 + include/linux/mfd/imanager/hwmon.h | 120 ++ include/linux/mfd/imanager/i2c.h | 55 + include/linux/mfd/imanager/wdt.h | 37 + 35 files changed, 5861 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/imanager.txt create mode 100644 Documentation/hwmon/imanager create mode 100644 Documentation/i2c/busses/i2c-imanager create mode 100644 drivers/gpio/imanager-ec-gpio.c create mode 100644 drivers/gpio/imanager-gpio.c create mode 100644 drivers/hwmon/imanager-ec-hwmon.c create mode 100644 drivers/hwmon/imanager-hwmon.c create mode 100644 drivers/i2c/busses/imanager-ec-i2c.c create mode 100644 drivers/i2c/busses/imanager-i2c.c create mode 100644 drivers/mfd/imanager-core.c create mode 100644 drivers/mfd/imanager-ec.c create mode 100644 drivers/video/backlight/imanager-bl.c create mode 100644 drivers/video/backlight/imanager-ec-bl.c create mode 100644 drivers/watchdog/imanager-ec-wdt.c create mode 100644 drivers/watchdog/imanager-wdt.c create mode 100644 include/linux/mfd/imanager/backlight.h create mode 100644 include/linux/mfd/imanager/core.h create mode 100644 include/linux/mfd/imanager/ec.h create mode 100644 include/linux/mfd/imanager/gpio.h create mode 100644 include/linux/mfd/imanager/hwmon.h create mode 100644 include/linux/mfd/imanager/i2c.h create mode 100644 include/linux/mfd/imanager/wdt.h -- 2.6.4
[toc] | [next] | [standalone]
| From | richard.dorsch@gmail.com |
|---|---|
| Date | 2016-01-08 23:30 +0100 |
| Subject | [PATCH 2/6] Add Advantech iManager GPIO driver |
| Message-ID | <qONHI-4JZ-13@gated-at.bofh.it> |
| In reply to | #1305032 |
From: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
---
drivers/gpio/Kconfig | 8 ++
drivers/gpio/Makefile | 2 +
drivers/gpio/imanager-ec-gpio.c | 98 ++++++++++++++++++++
drivers/gpio/imanager-gpio.c | 182 ++++++++++++++++++++++++++++++++++++++
include/linux/mfd/imanager/gpio.h | 27 ++++++
5 files changed, 317 insertions(+)
create mode 100644 drivers/gpio/imanager-ec-gpio.c
create mode 100644 drivers/gpio/imanager-gpio.c
create mode 100644 include/linux/mfd/imanager/gpio.h
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b18bea0..0f80947 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -765,6 +765,14 @@ config GPIO_DLN2
This driver can also be built as a module. If so, the module
will be called gpio-dln2.
+config GPIO_IMANAGER
+ tristate "Advantech iManager GPIO support"
+ depends on MFD_IMANAGER
+ help
+ Say yes here to support Advantech iManager GPIO functionality
+ of some Advantech SOM, MIO, AIMB, and PCM modules/boards.
+ Requires mfd-core and imanager-core to function properly.
+
config GPIO_JANZ_TTL
tristate "Janz VMOD-TTL Digital IO Module"
depends on MFD_JANZ_CMODIO
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 986dbd8..0df55e4 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -41,6 +41,8 @@ obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o
obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o
obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o
obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
+gpio-imanager-objs := imanager-gpio.o imanager-ec-gpio.o
+obj-$(CONFIG_GPIO_IMANAGER) += gpio-imanager.o
obj-$(CONFIG_GPIO_IOP) += gpio-iop.o
obj-$(CONFIG_GPIO_IT87) += gpio-it87.o
obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o
diff --git a/drivers/gpio/imanager-ec-gpio.c b/drivers/gpio/imanager-ec-gpio.c
new file mode 100644
index 0000000..cefc44d
--- /dev/null
+++ b/drivers/gpio/imanager-ec-gpio.c
@@ -0,0 +1,98 @@
+/*
+ * Advantech iManager GPIO core
+ *
+ * Copyright (C) 2015 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/byteorder/generic.h>
+#include <linux/mfd/imanager/ec.h>
+#include <linux/mfd/imanager/gpio.h>
+
+#define EC_GPIOF_DIR_OUT (1 << 6)
+#define EC_GPIOF_DIR_IN (1 << 7)
+#define EC_GPIOF_LOW (0 << 0)
+#define EC_GPIOF_HIGH (1 << 0)
+
+/*
+ * Power-on default:
+ * GPIO[7..4] := Input
+ * GPIO[3..0] := Output
+ */
+
+static const struct imanager_gpio_device *gpio;
+
+int gpio_core_get_state(u32 num)
+{
+ int ret;
+
+ if (WARN_ON(num >= gpio->num))
+ return -EINVAL;
+
+ ret = imanager_read_byte(EC_CMD_HWP_RD, gpio->attr[num].did);
+ if (ret < 0)
+ pr_err("Failed to get GPIO pin state (%x)\n", num);
+
+ return ret;
+}
+
+int gpio_core_set_state(u32 num, bool state)
+{
+ int ret;
+
+ if (WARN_ON(num >= gpio->num))
+ return -EINVAL;
+
+ ret = imanager_write_byte(EC_CMD_HWP_WR, gpio->attr[num].did,
+ state ? EC_GPIOF_HIGH : EC_GPIOF_LOW);
+ if (ret) {
+ pr_err("Failed to set GPIO pin state (%x)\n", num);
+ return ret;
+ }
+
+ return 0;
+}
+
+int gpio_core_set_direction(u32 num, int dir)
+{
+ int ret;
+
+ if (WARN_ON(num >= gpio->num))
+ return -EINVAL;
+
+ ret = imanager_write_byte(EC_CMD_GPIO_DIR_WR, gpio->attr[num].did,
+ dir ? EC_GPIOF_DIR_IN : EC_GPIOF_DIR_OUT);
+ if (ret) {
+ pr_err("Failed to set GPIO direction (%x, '%s')\n", num,
+ dir == GPIOF_DIR_OUT ? "OUT" : "IN");
+ return ret;
+ }
+
+ return 0;
+}
+
+int gpio_core_get_max_count(void)
+{
+ return gpio->num;
+}
+
+int gpio_core_init(void)
+{
+ gpio = imanager_get_gpio_device();
+ if (!gpio)
+ return -ENODEV;
+
+ return 0;
+}
+
diff --git a/drivers/gpio/imanager-gpio.c b/drivers/gpio/imanager-gpio.c
new file mode 100644
index 0000000..3939c0f
--- /dev/null
+++ b/drivers/gpio/imanager-gpio.c
@@ -0,0 +1,182 @@
+/*
+ * Advantech iManager GPIO driver
+ *
+ * Copyright (C) 2015 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/mfd/imanager/core.h>
+#include <linux/mfd/imanager/gpio.h>
+
+struct imanager_gpio_data {
+ struct imanager_device_data *idev;
+ struct gpio_chip chip;
+};
+
+static inline struct imanager_gpio_data *
+to_imanager_gpio_data(struct gpio_chip *chip)
+{
+ return container_of(chip, struct imanager_gpio_data, chip);
+}
+
+static int imanager_direction_in(struct gpio_chip *chip, u32 gpio_num)
+{
+ struct imanager_gpio_data *data = to_imanager_gpio_data(chip);
+ int ret;
+
+ mutex_lock(&data->idev->lock);
+
+ ret = gpio_core_set_direction(gpio_num, GPIOF_DIR_IN);
+ if (ret) {
+ dev_err(chip->dev, "Failed to set direction to 'in' (%d)\n",
+ gpio_num);
+ ret = -EIO;
+ }
+
+ mutex_unlock(&data->idev->lock);
+
+ return ret;
+}
+
+static int
+imanager_direction_out(struct gpio_chip *chip, u32 gpio_num, int val)
+{
+ struct imanager_gpio_data *data = to_imanager_gpio_data(chip);
+ int ret;
+
+ mutex_lock(&data->idev->lock);
+
+ ret = gpio_core_set_direction(gpio_num, GPIOF_DIR_OUT);
+ if (ret) {
+ dev_err(chip->dev, "Failed to set direction to 'out' (%d)\n",
+ gpio_num);
+ ret = -EIO;
+ }
+
+ mutex_unlock(&data->idev->lock);
+
+ return ret;
+}
+
+static int imanager_get(struct gpio_chip *chip, u32 gpio_num)
+{
+ struct imanager_gpio_data *data = to_imanager_gpio_data(chip);
+ int ret;
+
+ mutex_lock(&data->idev->lock);
+
+ ret = gpio_core_get_state(gpio_num);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to get status (%d)\n", gpio_num);
+ ret = -EIO;
+ }
+
+ mutex_unlock(&data->idev->lock);
+
+ return ret;
+}
+
+static void imanager_set(struct gpio_chip *chip, u32 gpio_num,
+ int val)
+{
+ struct imanager_gpio_data *data = to_imanager_gpio_data(chip);
+ int ret;
+
+ mutex_lock(&data->idev->lock);
+
+ ret = gpio_core_set_state(gpio_num, val);
+ if (ret < 0)
+ dev_err(chip->dev, "Failed to set status (%d)\n", gpio_num);
+
+ mutex_unlock(&data->idev->lock);
+}
+
+static int imanager_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imanager_device_data *idev = dev_get_drvdata(dev->parent);
+ struct imanager_gpio_data *data;
+ struct gpio_chip *chip;
+ int ret;
+
+ if (!idev) {
+ dev_err(dev, "Invalid platform data\n");
+ return -EINVAL;
+ }
+
+ ret = gpio_core_init();
+ if (ret) {
+ dev_err(dev, "Failed initializing GPIO core\n");
+ return ret;
+ }
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->idev = idev;
+
+ platform_set_drvdata(pdev, data);
+
+ chip = &data->chip;
+
+ chip->owner = THIS_MODULE;
+ chip->dev = dev;
+ chip->label = "imanager_gpio";
+
+ chip->base = -1;
+ chip->ngpio = gpio_core_get_max_count();
+
+ chip->get = imanager_get;
+ chip->set = imanager_set;
+
+ chip->can_sleep = 1;
+
+ chip->direction_input = imanager_direction_in;
+ chip->direction_output = imanager_direction_out;
+
+ ret = gpiochip_add(chip);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register driver\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imanager_remove(struct platform_device *pdev)
+{
+ struct imanager_gpio_data *data = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&data->chip);
+
+ return 0;
+}
+
+static struct platform_driver imanager_gpio_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "imanager_gpio",
+ },
+ .probe = imanager_gpio_probe,
+ .remove = imanager_remove,
+};
+
+module_platform_driver(imanager_gpio_driver);
+
+MODULE_DESCRIPTION("Advantech iManager GPIO Driver");
+MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imanager_gpio");
diff --git a/include/linux/mfd/imanager/gpio.h b/include/linux/mfd/imanager/gpio.h
new file mode 100644
index 0000000..7b76727
--- /dev/null
+++ b/include/linux/mfd/imanager/gpio.h
@@ -0,0 +1,27 @@
+/*
+ * Advantech iManager GPIO core
+ *
+ * Copyright (C) 2015 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __GPIO_H__
+#define __GPIO_H__
+
+#include <linux/gpio.h>
+#include <linux/types.h>
+
+int gpio_core_init(void);
+
+int gpio_core_get_max_count(void);
+
+int gpio_core_get_state(u32 num);
+int gpio_core_set_state(u32 num, bool state);
+int gpio_core_set_direction(u32 num, int dir);
+
+#endif
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | kbuild test robot <lkp@intel.com> |
|---|---|
| Date | 2016-01-09 02:00 +0100 |
| Subject | [PATCH] fix platform_no_drv_owner.cocci warnings |
| Message-ID | <qOQ2S-6c6-3@gated-at.bofh.it> |
| In reply to | #1305035 |
drivers/gpio/imanager-gpio.c:170:3-8: No need to set .owner here. The core will do it.
Remove .owner field if calls are used which set it automatically
Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci
CC: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
---
imanager-gpio.c | 1 -
1 file changed, 1 deletion(-)
--- a/drivers/gpio/imanager-gpio.c
+++ b/drivers/gpio/imanager-gpio.c
@@ -167,7 +167,6 @@ static int imanager_remove(struct platfo
static struct platform_driver imanager_gpio_driver = {
.driver = {
- .owner = THIS_MODULE,
.name = "imanager_gpio",
},
.probe = imanager_gpio_probe,
[toc] | [prev] | [next] | [standalone]
| From | kbuild test robot <lkp@intel.com> |
|---|---|
| Date | 2016-01-09 02:00 +0100 |
| Subject | Re: [PATCH 2/6] Add Advantech iManager GPIO driver |
| Message-ID | <qOQ2S-6c6-5@gated-at.bofh.it> |
| In reply to | #1305035 |
Hi Richard, [auto build test WARNING on hwmon/hwmon-next] [also build test WARNING on v4.4-rc8 next-20160108] [if your patch is applied to the wrong git tree, please drop us a note to help improving the system] url: https://github.com/0day-ci/linux/commits/richard-dorsch-gmail-com/Add-Advantech-iManager-EC-driver-set/20160109-063329 base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next coccinelle warnings: (new ones prefixed by >>) >> drivers/gpio/imanager-gpio.c:170:3-8: No need to set .owner here. The core will do it. Please review and possibly fold the followup patch. --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
[toc] | [prev] | [next] | [standalone]
| From | richard.dorsch@gmail.com |
|---|---|
| Date | 2016-01-10 08:50 +0100 |
| Subject | [PATCH 7/8] Remove .owner from platform_driver struct |
| Message-ID | <qPiVb-1cO-1@gated-at.bofh.it> |
| In reply to | #1305112 |
From: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
Signed-off-by: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
---
drivers/gpio/imanager-gpio.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/gpio/imanager-gpio.c b/drivers/gpio/imanager-gpio.c
index 3939c0f..b6fa689 100644
--- a/drivers/gpio/imanager-gpio.c
+++ b/drivers/gpio/imanager-gpio.c
@@ -167,7 +167,6 @@ static int imanager_remove(struct platform_device *pdev)
static struct platform_driver imanager_gpio_driver = {
.driver = {
- .owner = THIS_MODULE,
.name = "imanager_gpio",
},
.probe = imanager_gpio_probe,
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | richard.dorsch@gmail.com |
|---|---|
| Date | 2016-01-08 23:30 +0100 |
| Subject | [PATCH 6/6] Add Advantech iManager Watchdog driver |
| Message-ID | <qONHI-4JZ-9@gated-at.bofh.it> |
| In reply to | #1305032 |
From: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
---
drivers/watchdog/Kconfig | 12 ++
drivers/watchdog/Makefile | 2 +
drivers/watchdog/imanager-ec-wdt.c | 170 +++++++++++++++++++
drivers/watchdog/imanager-wdt.c | 334 +++++++++++++++++++++++++++++++++++++
include/linux/mfd/imanager/wdt.h | 37 ++++
5 files changed, 555 insertions(+)
create mode 100644 drivers/watchdog/imanager-ec-wdt.c
create mode 100644 drivers/watchdog/imanager-wdt.c
create mode 100644 include/linux/mfd/imanager/wdt.h
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 1c427be..e555127 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -846,6 +846,18 @@ config ITCO_VENDOR_SUPPORT
devices. At this moment we only have additional support for some
SuperMicro Inc. motherboards.
+config IMANAGER_WDT
+ tristate "Advantech iManager Watchdog"
+ depends on MFD_IMANAGER
+ select WATCHDOG_CORE
+ help
+ This driver provides support for Advantech iManager watchdog
+ of Advantech SOM, MIO, AIMB, and PCM modules/boards.
+ Requires mfd-core and imanager-core to function properly.
+
+ This driver can also be built as a module. If so, the module
+ will be called imanager_wdt.
+
config IT8712F_WDT
tristate "IT8712F (Smart Guardian) Watchdog Timer"
depends on X86
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 53d4827..647eca87 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -100,6 +100,8 @@ obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o
ifeq ($(CONFIG_ITCO_VENDOR_SUPPORT),y)
obj-$(CONFIG_ITCO_WDT) += iTCO_vendor_support.o
endif
+imanager_wdt-objs := imanager-wdt.o imanager-ec-wdt.o
+obj-$(CONFIG_IMANAGER_WDT) += imanager_wdt.o
obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o
obj-$(CONFIG_IT87_WDT) += it87_wdt.o
obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o
diff --git a/drivers/watchdog/imanager-ec-wdt.c b/drivers/watchdog/imanager-ec-wdt.c
new file mode 100644
index 0000000..0db803f
--- /dev/null
+++ b/drivers/watchdog/imanager-ec-wdt.c
@@ -0,0 +1,170 @@
+/*
+ * Advantech iManager Watchdog core
+ *
+ * Copyright (C) 2015 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Note: Current release only implements RESET of the EC WDT.
+ * In other words, no PWR button, NMI, SCI, IRQ, or WDPin support yet!
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/byteorder/generic.h>
+#include <linux/swab.h>
+#include <linux/mfd/imanager/ec.h>
+#include <linux/mfd/imanager/wdt.h>
+
+/* Timer resolution */
+#define WDT_FREQ 10 /* Hz */
+
+enum wdt_ctrl {
+ START = 1,
+ STOP,
+ RST,
+ GET_TIMEOUT,
+ SET_TIMEOUT,
+ STOPBOOT = 8,
+};
+
+struct wdt_event_delay {
+ u16 delay,
+ pwrbtn,
+ nmi,
+ reset,
+ wdpin,
+ sci,
+ dummy;
+};
+
+static const struct imanager_watchdog_device *wd;
+
+static inline int set_timer(enum wdt_ctrl ctrl)
+{
+ if (WARN_ON(ctrl == SET_TIMEOUT))
+ return -EINVAL;
+
+ return imanager_msg_write(EC_CMD_WDT_CTRL, ctrl, NULL);
+}
+
+static int
+wdt_ctrl(enum wdt_ctrl ctrl, enum wdt_event type, unsigned int timeout)
+{
+ u16 val;
+ int ret;
+ struct ec_message msg = {
+ .rlen = 0,
+ .wlen = 0,
+ };
+ u8 *fevent = &msg.u.data[0];
+ struct wdt_event_delay *event =
+ (struct wdt_event_delay *)&msg.u.data[1];
+
+ switch (ctrl) {
+ case SET_TIMEOUT:
+ memset(event, 0xff, sizeof(*event));
+ msg.wlen = sizeof(*event);
+ *fevent = 0;
+ val = (!timeout) ? 0xffff : swab16(timeout * WDT_FREQ);
+
+ switch (type) {
+ case DELAY:
+ event->delay = val;
+ break;
+ case PWRBTN:
+ event->pwrbtn = val;
+ break;
+ case NMI:
+ event->nmi = val;
+ break;
+ case RESET:
+ event->reset = val;
+ break;
+ case WDPIN:
+ event->wdpin = val;
+ break;
+ case SCI:
+ event->sci = val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = imanager_msg_write(EC_CMD_WDT_CTRL, SET_TIMEOUT, &msg);
+ if (ret < 0) {
+ pr_err("Failed to set timeout\n");
+ return ret;
+ }
+ break;
+ case START:
+ case STOP:
+ case RST:
+ case STOPBOOT:
+ /* simple command, no data */
+ return imanager_msg_write(EC_CMD_WDT_CTRL, ctrl, NULL);
+ default:
+ return -EINVAL;
+ }
+
+ return timeout;
+}
+
+int wdt_core_start_timer(void)
+{
+ return set_timer(START);
+}
+
+int wdt_core_stop_timer(void)
+{
+ return set_timer(STOP);
+}
+
+int wdt_core_reset_timer(void)
+{
+ return set_timer(RST);
+}
+
+int wdt_core_stop_boot_timer(void)
+{
+ return set_timer(STOPBOOT);
+}
+
+inline int wdt_core_set_timeout(enum wdt_event type, u32 timeout)
+{
+ return wdt_ctrl(SET_TIMEOUT, type, timeout);
+}
+
+int wdt_core_disable_all(void)
+{
+ struct ec_message msg = {
+ .rlen = 0,
+ .wlen = sizeof(struct wdt_event_delay),
+ };
+ struct wdt_event_delay *event =
+ (struct wdt_event_delay *)&msg.u.data[1];
+
+ memset(event, 0xff, sizeof(*event));
+
+ return (wdt_core_stop_timer() ||
+ wdt_core_stop_boot_timer() ||
+ imanager_msg_write(EC_CMD_WDT_CTRL, SET_TIMEOUT, &msg));
+}
+
+int wdt_core_init(void)
+{
+ wd = imanager_get_watchdog_device();
+ if (!wd)
+ return -ENODEV;
+
+ return 0;
+}
+
diff --git a/drivers/watchdog/imanager-wdt.c b/drivers/watchdog/imanager-wdt.c
new file mode 100644
index 0000000..41d44bc
--- /dev/null
+++ b/drivers/watchdog/imanager-wdt.c
@@ -0,0 +1,334 @@
+/*
+ * Advantech iManager Watchdog driver
+ *
+ * Copyright (C) 2015 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/imanager/core.h>
+#include <linux/mfd/imanager/wdt.h>
+
+#define WATCHDOG_TIMEOUT 30 /* in seconds */
+
+static unsigned long last_updated = -1;
+
+static uint timeout = WATCHDOG_TIMEOUT;
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 1 <= timeout <= 65534, default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int wdt_start_timer(void)
+{
+ int ret;
+
+ ret = wdt_core_start_timer();
+ if (ret < 0)
+ return ret;
+
+ last_updated = jiffies;
+
+ return 0;
+}
+
+static int wdt_stop_timer(void)
+{
+ int ret;
+
+ ret = wdt_core_stop_timer();
+ if (ret < 0)
+ return ret;
+
+ last_updated = 0;
+
+ return 0;
+}
+
+static int wdt_ping(void)
+{
+ int ret;
+
+ ret = wdt_core_reset_timer();
+ if (ret < 0)
+ return ret;
+
+ last_updated = jiffies;
+
+ return 0;
+}
+
+static int imanager_wdt_set(unsigned int _timeout)
+{
+ int ret;
+
+ if (timeout != _timeout)
+ timeout = _timeout;
+
+ ret = wdt_core_set_timeout(PWRBTN, timeout);
+ if (ret < 0)
+ return ret;
+
+ if (last_updated != 0)
+ last_updated = jiffies;
+
+ return 0;
+}
+
+static int imanager_wdt_set_timeout(struct watchdog_device *wdt_dev,
+ unsigned int timeout)
+{
+ struct imanager_device_data *idev = watchdog_get_drvdata(wdt_dev);
+ int ret = 0;
+
+ mutex_lock(&idev->lock);
+
+ ret = imanager_wdt_set(timeout);
+ if (ret < 0)
+ dev_err(wdt_dev->dev, "Failed to set timeout\n");
+ mutex_unlock(&idev->lock);
+
+ return ret;
+}
+
+static unsigned int imanager_wdt_get_timeleft(struct watchdog_device *wdt_dev)
+{
+ unsigned int timeleft = 0;
+ unsigned long time_diff = ((jiffies - last_updated) / HZ);
+
+ if (last_updated && (timeout > time_diff))
+ timeleft = timeout - time_diff;
+
+ return timeleft;
+}
+
+static int imanager_wdt_start(struct watchdog_device *wdt_dev)
+{
+ struct imanager_device_data *idev = watchdog_get_drvdata(wdt_dev);
+ int ret = 0;
+
+ mutex_lock(&idev->lock);
+
+ ret = wdt_start_timer();
+ if (ret < 0)
+ dev_err(wdt_dev->dev, "Failed to start timer\n");
+
+ mutex_unlock(&idev->lock);
+
+ return ret;
+}
+
+static int imanager_wdt_stop(struct watchdog_device *wdt_dev)
+{
+ struct imanager_device_data *idev = watchdog_get_drvdata(wdt_dev);
+ int ret = 0;
+
+ mutex_lock(&idev->lock);
+
+ ret = wdt_stop_timer();
+ if (ret < 0)
+ dev_err(wdt_dev->dev, "Failed to stop timer\n");
+
+ mutex_unlock(&idev->lock);
+
+ return ret;
+}
+
+static int imanager_wdt_ping(struct watchdog_device *wdt_dev)
+{
+ struct imanager_device_data *idev = watchdog_get_drvdata(wdt_dev);
+ int ret = 0;
+
+ mutex_lock(&idev->lock);
+
+ ret = wdt_ping();
+ if (ret < 0)
+ dev_err(wdt_dev->dev, "Failed to reset timer\n");
+
+ mutex_unlock(&idev->lock);
+
+ return ret;
+}
+
+static long imanager_wdt_ioctl(struct watchdog_device *wdt_dev,
+ unsigned int cmd, unsigned long arg)
+{
+ struct imanager_device_data *idev = watchdog_get_drvdata(wdt_dev);
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int ret = 0;
+ int timeval, options;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 0,
+ .identity = "imanager_wdt",
+ };
+
+ mutex_lock(&idev->lock);
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ ret = -EFAULT;
+ break;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, p);
+ break;
+ case WDIOC_SETOPTIONS:
+ if (get_user(options, p)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (options & WDIOS_DISABLECARD)
+ wdt_stop_timer();
+ if (options & WDIOS_ENABLECARD) {
+ wdt_ping();
+ wdt_start_timer();
+ }
+ break;
+ case WDIOC_KEEPALIVE:
+ wdt_ping();
+ break;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(timeval, p)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (imanager_wdt_set(timeval)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ wdt_ping();
+ /* Fall through */
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(timeout, p);
+ break;
+ case WDIOC_GETTIMELEFT:
+ timeval = imanager_wdt_get_timeleft(wdt_dev);
+ ret = put_user(timeval, p);
+ break;
+ default:
+ ret = -ENOTTY;
+ }
+
+out:
+ mutex_unlock(&idev->lock);
+
+ return ret;
+}
+
+static const struct watchdog_info imanager_wdt_info = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+ .identity = "imanager_wdt",
+ .firmware_version = 0,
+};
+
+static const struct watchdog_ops imanager_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = imanager_wdt_start,
+ .stop = imanager_wdt_stop,
+ .ping = imanager_wdt_ping,
+ .set_timeout = imanager_wdt_set_timeout,
+ .get_timeleft = imanager_wdt_get_timeleft,
+ .ioctl = imanager_wdt_ioctl,
+};
+
+static struct watchdog_device imanager_wdt_dev = {
+ .info = &imanager_wdt_info,
+ .ops = &imanager_wdt_ops,
+ .timeout = WATCHDOG_TIMEOUT,
+ .min_timeout = 1,
+ .max_timeout = 0xfffe,
+};
+
+static int imanager_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imanager_device_data *idev = dev_get_drvdata(dev->parent);
+ int ret;
+
+ if (!idev) {
+ dev_err(dev, "Invalid platform data\n");
+ return -EINVAL;
+ }
+
+ watchdog_set_nowayout(&imanager_wdt_dev, nowayout);
+ watchdog_set_drvdata(&imanager_wdt_dev, idev);
+
+ ret = watchdog_register_device(&imanager_wdt_dev);
+ if (ret) {
+ dev_err(dev, "Failed to register watchdog device\n");
+ return ret;
+ }
+
+ ret = wdt_core_init();
+ if (ret) {
+ dev_err(dev, "Failed to initialize watchdog core\n");
+ goto unregister_driver;
+ }
+
+ wdt_core_disable_all();
+
+ imanager_wdt_set_timeout(&imanager_wdt_dev, timeout);
+
+ dev_info(dev, "Driver loaded (timeout=%d seconds)\n", timeout);
+
+ return 0;
+
+unregister_driver:
+ watchdog_unregister_device(&imanager_wdt_dev);
+ platform_set_drvdata(pdev, NULL);
+
+ return ret;
+}
+
+static int imanager_wdt_remove(struct platform_device *pdev)
+{
+ wdt_core_disable_all();
+
+ watchdog_unregister_device(&imanager_wdt_dev);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver imanager_wdt_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "imanager_wdt",
+ },
+ .probe = imanager_wdt_probe,
+ .remove = imanager_wdt_remove,
+};
+
+module_platform_driver(imanager_wdt_driver);
+
+MODULE_DESCRIPTION("Advantech iManager Watchdog Driver");
+MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imanager_wdt");
diff --git a/include/linux/mfd/imanager/wdt.h b/include/linux/mfd/imanager/wdt.h
new file mode 100644
index 0000000..727b24e
--- /dev/null
+++ b/include/linux/mfd/imanager/wdt.h
@@ -0,0 +1,37 @@
+/*
+ * Advantech iManager Watchdog core
+ *
+ * Copyright (C) 2015 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __WDT_H__
+#define __WDT_H__
+
+#include <linux/types.h>
+
+enum wdt_event {
+ DELAY,
+ PWRBTN,
+ NMI,
+ RESET,
+ WDPIN,
+ SCI,
+};
+
+int wdt_core_init(void);
+
+int wdt_core_set_timeout(enum wdt_event type, u32 timeout);
+
+int wdt_core_disable_all(void);
+int wdt_core_start_timer(void);
+int wdt_core_stop_timer(void);
+int wdt_core_reset_timer(void);
+int wdt_core_stop_boot_timer(void);
+
+#endif
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | kbuild test robot <lkp@intel.com> |
|---|---|
| Date | 2016-01-09 03:10 +0100 |
| Subject | [PATCH] fix platform_no_drv_owner.cocci warnings |
| Message-ID | <qOR8B-78i-3@gated-at.bofh.it> |
| In reply to | #1305037 |
drivers/watchdog/imanager-wdt.c:322:3-8: No need to set .owner here. The core will do it.
Remove .owner field if calls are used which set it automatically
Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci
CC: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
---
imanager-wdt.c | 1 -
1 file changed, 1 deletion(-)
--- a/drivers/watchdog/imanager-wdt.c
+++ b/drivers/watchdog/imanager-wdt.c
@@ -319,7 +319,6 @@ static int imanager_wdt_remove(struct pl
static struct platform_driver imanager_wdt_driver = {
.driver = {
- .owner = THIS_MODULE,
.name = "imanager_wdt",
},
.probe = imanager_wdt_probe,
[toc] | [prev] | [next] | [standalone]
| From | kbuild test robot <lkp@intel.com> |
|---|---|
| Date | 2016-01-09 03:10 +0100 |
| Subject | Re: [PATCH 6/6] Add Advantech iManager Watchdog driver |
| Message-ID | <qOR8B-78i-5@gated-at.bofh.it> |
| In reply to | #1305037 |
Hi Richard, [auto build test WARNING on hwmon/hwmon-next] [also build test WARNING on v4.4-rc8 next-20160108] [if your patch is applied to the wrong git tree, please drop us a note to help improving the system] url: https://github.com/0day-ci/linux/commits/richard-dorsch-gmail-com/Add-Advantech-iManager-EC-driver-set/20160109-063329 base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next coccinelle warnings: (new ones prefixed by >>) >> drivers/watchdog/imanager-wdt.c:322:3-8: No need to set .owner here. The core will do it. Please review and possibly fold the followup patch. --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
[toc] | [prev] | [next] | [standalone]
| From | richard.dorsch@gmail.com |
|---|---|
| Date | 2016-01-10 08:50 +0100 |
| Subject | [PATCH 8/8] Remove .owner from platform_driver struct |
| Message-ID | <qPiVc-1cO-3@gated-at.bofh.it> |
| In reply to | #1305138 |
From: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
Signed-off-by: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
---
drivers/watchdog/imanager-wdt.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/watchdog/imanager-wdt.c b/drivers/watchdog/imanager-wdt.c
index 41d44bc..8a8b369 100644
--- a/drivers/watchdog/imanager-wdt.c
+++ b/drivers/watchdog/imanager-wdt.c
@@ -319,7 +319,6 @@ static int imanager_wdt_remove(struct platform_device *pdev)
static struct platform_driver imanager_wdt_driver = {
.driver = {
- .owner = THIS_MODULE,
.name = "imanager_wdt",
},
.probe = imanager_wdt_probe,
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | richard.dorsch@gmail.com |
|---|---|
| Date | 2016-01-08 23:40 +0100 |
| Subject | [PATCH 5/6] Add Advantech iManager Backlight driver |
| Message-ID | <qONRn-4P8-7@gated-at.bofh.it> |
| In reply to | #1305032 |
From: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
---
drivers/video/backlight/Kconfig | 12 ++
drivers/video/backlight/Makefile | 2 +
drivers/video/backlight/imanager-bl.c | 199 +++++++++++++++++++++++++++++++
drivers/video/backlight/imanager-ec-bl.c | 118 ++++++++++++++++++
include/linux/mfd/imanager/backlight.h | 37 ++++++
5 files changed, 368 insertions(+)
create mode 100644 drivers/video/backlight/imanager-bl.c
create mode 100644 drivers/video/backlight/imanager-ec-bl.c
create mode 100644 include/linux/mfd/imanager/backlight.h
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 5ffa4b4..8003573 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -251,6 +251,18 @@ config BACKLIGHT_HP700
If you have an HP Jornada 700 series,
say Y to include backlight control driver.
+config BACKLIGHT_IMANAGER
+ tristate "Advantech iManager backlight/brightness"
+ depends on MFD_IMANAGER
+ help
+ This enables support for Advantech iManager Backlight and
+ Brightness control of some Advantech SOM, MIO, AIMB, and
+ PCM modules/boards.
+ Requires mfd-core and imanager-core to function properly.
+
+ This driver can also be built as a module. If so, the module
+ will be called imanager_bl.
+
config BACKLIGHT_CARILLO_RANCH
tristate "Intel Carillo Ranch Backlight Driver"
depends on LCD_CLASS_DEVICE && PCI && X86 && FB_LE80578
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index 16ec534..15bf136 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -26,6 +26,8 @@ obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o
obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o
obj-$(CONFIG_BACKLIGHT_AS3711) += as3711_bl.o
obj-$(CONFIG_BACKLIGHT_BD6107) += bd6107.o
+imanager_bl-objs := imanager-bl.o imanager-ec-bl.o
+obj-$(CONFIG_BACKLIGHT_IMANAGER) += imanager_bl.o
obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o
obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o
diff --git a/drivers/video/backlight/imanager-bl.c b/drivers/video/backlight/imanager-bl.c
new file mode 100644
index 0000000..70d7dc9
--- /dev/null
+++ b/drivers/video/backlight/imanager-bl.c
@@ -0,0 +1,199 @@
+/*
+ * Advantech iManager Backlight driver
+ * Partially derived from wm831x_bl
+ *
+ * Copyright (C) 2015 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/pwm.h>
+#include <linux/mfd/imanager/core.h>
+#include <linux/mfd/imanager/backlight.h>
+
+#define BL_MAX_BRIGHTNESS 100
+
+static bool polarity = PWM_POLARITY_NORMAL;
+module_param(polarity, bool, 0);
+MODULE_PARM_DESC(polarity, "Select backlight polarity (inverted := 1)");
+
+static ushort unit = UNIT_1;
+module_param(unit, ushort, 0);
+MODULE_PARM_DESC(unit, "Select backlight control unit [0, 1] (defaults to 0)");
+
+struct imanager_backlight_data {
+ struct imanager_device_data *idev;
+ struct backlight_device *bl;
+};
+
+static int get_brightness(struct backlight_device *dev)
+{
+ struct imanager_backlight_data *data = bl_get_data(dev);
+ int ret;
+
+ mutex_lock(&data->idev->lock);
+
+ ret = bl_core_get_pulse_width(unit);
+ /* Reverse percentage if polarity is set */
+ if (polarity)
+ ret = 100 - ret;
+
+ mutex_unlock(&data->idev->lock);
+
+ return ret;
+}
+
+static int set_brightness(struct backlight_device *dev)
+{
+ struct imanager_backlight_data *data = bl_get_data(dev);
+ u8 brightness = (u8) dev->props.brightness;
+ int ret;
+
+ if (brightness > BL_MAX_BRIGHTNESS)
+ return -EINVAL;
+
+ if (dev->props.power != FB_BLANK_UNBLANK)
+ brightness = 0;
+
+ if (dev->props.fb_blank != FB_BLANK_UNBLANK)
+ brightness = 0;
+
+ if (dev->props.state & BL_CORE_SUSPENDED)
+ brightness = 0;
+
+ mutex_lock(&data->idev->lock);
+
+ /* Inversed percentage if polarity is set */
+ if (polarity)
+ brightness = 100 - brightness;
+ ret = bl_core_set_pulse_width(unit, brightness);
+
+ mutex_unlock(&data->idev->lock);
+
+ return ret;
+}
+
+static const struct backlight_ops imanager_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .get_brightness = get_brightness,
+ .update_status = set_brightness,
+};
+
+static int imanager_backlight_init(struct device *dev,
+ struct imanager_backlight_data *data)
+{
+ struct backlight_device *bd;
+ struct backlight_properties props;
+
+ memset(&props, 0, sizeof(props));
+ props.type = BACKLIGHT_PLATFORM;
+ props.max_brightness = BL_MAX_BRIGHTNESS;
+ bd = backlight_device_register("imanager_backlight", dev, data,
+ &imanager_bl_ops, &props);
+
+ if (IS_ERR(bd)) {
+ data->bl = NULL;
+ dev_err(dev, "Unable to register backlight device\n");
+ return PTR_ERR(bd);
+ }
+
+ data->bl = bd;
+
+ bd->props.brightness = get_brightness(bd);
+ bd->props.power = FB_BLANK_UNBLANK;
+ backlight_update_status(bd);
+
+ return 0;
+}
+
+static int imanager_backlight_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imanager_device_data *idev = dev_get_drvdata(dev->parent);
+ struct imanager_backlight_data *data;
+ int ret;
+
+ if (!idev) {
+ dev_err(dev, "Invalid platform data\n");
+ return -EINVAL;
+ }
+
+ ret = bl_core_init();
+ if (ret) {
+ dev_err(dev, "Failed to initialize backlight core\n");
+ return -EIO;
+ }
+
+ ret = bl_core_set_state(unit, BL_CTRL_ENABLE);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable backlight control (%d)\n",
+ unit);
+ return -EIO;
+ }
+
+ if (polarity)
+ ret = bl_core_set_polarity(PWM_POLARITY_INVERSED);
+ else
+ ret = bl_core_set_polarity(PWM_POLARITY_NORMAL);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set backlight polarity\n");
+ return -EIO;
+ }
+
+ /* init brightness to 60% */
+ bl_core_set_pulse_width(unit, 60);
+
+ data = devm_kzalloc(dev, sizeof(struct imanager_backlight_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->idev = idev;
+
+ ret = imanager_backlight_init(dev, data);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, data);
+
+ return 0;
+}
+
+static int imanager_backlight_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imanager_backlight_data *data = dev_get_drvdata(dev);
+
+ backlight_device_unregister(data->bl);
+
+ return 0;
+}
+
+static struct platform_driver imanager_backlight_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "imanager_backlight",
+ },
+ .probe = imanager_backlight_probe,
+ .remove = imanager_backlight_remove,
+};
+
+module_platform_driver(imanager_backlight_driver);
+
+MODULE_DESCRIPTION("Advantech iManager Backlight driver");
+MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imanager_backlight");
diff --git a/drivers/video/backlight/imanager-ec-bl.c b/drivers/video/backlight/imanager-ec-bl.c
new file mode 100644
index 0000000..1c93f1e
--- /dev/null
+++ b/drivers/video/backlight/imanager-ec-bl.c
@@ -0,0 +1,118 @@
+/*
+ * Advantech iManager Backlight/Brightness Core
+ *
+ * Copyright (C) 2015 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/byteorder/generic.h>
+#include <linux/mfd/imanager/ec.h>
+#include <linux/mfd/imanager/backlight.h>
+
+struct brightness_level {
+ u32 value : 7, /* Brightness Value - LSB [6..0] */
+ enable : 1; /* Brightness Enable - MSB [7] */
+};
+
+struct backlight_ctrl {
+ u32 enable : 1, /* Backlight Control Enable - LSB [0] */
+ pwmpol : 1, /* PWM Polarity - bit [1] */
+ blpol : 1, /* Backlight Polarity - bit [2] */
+ dnc : 5; /* Don't care - bit [7..3] */
+};
+
+static const struct imanager_backlight_device *bl;
+
+int bl_core_get_pulse_width(u32 unit)
+{
+ int ret;
+
+ if (WARN_ON(unit >= EC_BLC_MAX_NUM))
+ return -EINVAL;
+
+ ret = imanager_read_byte(EC_CMD_HWP_RD, bl->attr[unit].did);
+ if (ret < 0)
+ pr_err("Failed reading PWM (unit=%d)\n", unit);
+
+ return ret;
+}
+
+int bl_core_set_pulse_width(u32 unit, u32 pwm)
+{
+ int ret;
+
+ if (WARN_ON(unit >= EC_BLC_MAX_NUM))
+ return -EINVAL;
+
+ pwm = pwm > 100 ? 100 : pwm;
+
+ ret = imanager_write_byte(EC_CMD_HWP_WR, bl->attr[unit].did, pwm);
+ if (ret < 0)
+ pr_err("Failed writing PWM (val=%d, unit=%d)\n", pwm, unit);
+
+ return ret;
+}
+
+int bl_core_set_state(u32 unit, bool enable)
+{
+ int ret;
+ u8 val8;
+ struct brightness_level *pl = (struct brightness_level *)&val8;
+
+ if (WARN_ON(unit >= EC_BLC_MAX_NUM))
+ return -EINVAL;
+
+ ret = imanager_acpiram_read_byte(bl->brightness[unit]);
+ if (ret < 0)
+ return ret;
+ val8 = ret;
+
+ pl->enable = enable ? 1 : 0;
+
+ ret = imanager_acpiram_write_byte(bl->attr[unit].did, val8);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int bl_core_set_polarity(u32 polarity)
+{
+ int ret;
+ u8 val8;
+ struct backlight_ctrl *ctrl = (struct backlight_ctrl *)&val8;
+
+ ret = imanager_acpiram_read_byte(EC_ACPIRAM_BLC_CTRL);
+ if (ret < 0)
+ return ret;
+ val8 = ret;
+
+ ctrl->blpol = polarity ? 1 : 0;
+
+ ret = imanager_acpiram_write_byte(EC_ACPIRAM_BLC_CTRL, val8);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int bl_core_init(void)
+{
+ bl = imanager_get_backlight_device();
+ if (!bl)
+ return -ENODEV;
+
+ return 0;
+}
+
diff --git a/include/linux/mfd/imanager/backlight.h b/include/linux/mfd/imanager/backlight.h
new file mode 100644
index 0000000..3c74aa6
--- /dev/null
+++ b/include/linux/mfd/imanager/backlight.h
@@ -0,0 +1,37 @@
+/*
+ * Advantech iManager Backlight core
+ *
+ * Copyright (C) 2015 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __BACKLIGHT_H__
+#define __BACKLIGHT_H__
+
+#include <linux/types.h>
+
+enum backlight_control {
+ BL_CTRL_DISABLE,
+ BL_CTRL_ENABLE,
+};
+
+enum backlight_unit {
+ UNIT_1,
+ UNIT_2,
+};
+
+int bl_core_init(void);
+
+int bl_core_set_state(u32 unit, bool enable);
+
+int bl_core_set_polarity(u32 polarity);
+
+int bl_core_get_pulse_width(u32 unit);
+int bl_core_set_pulse_width(u32 unit, u32 pwm);
+
+#endif
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | richard.dorsch@gmail.com |
|---|---|
| Date | 2016-01-08 23:40 +0100 |
| Subject | [PATCH 4/6] Add Advantech iManager I2C driver |
| Message-ID | <qONRo-4P8-11@gated-at.bofh.it> |
| In reply to | #1305032 |
From: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
---
Documentation/i2c/busses/i2c-imanager | 48 ++++
drivers/i2c/busses/Kconfig | 11 +
drivers/i2c/busses/Makefile | 2 +
drivers/i2c/busses/imanager-ec-i2c.c | 466 ++++++++++++++++++++++++++++++++++
drivers/i2c/busses/imanager-i2c.c | 240 +++++++++++++++++
include/linux/mfd/imanager/i2c.h | 55 ++++
6 files changed, 822 insertions(+)
create mode 100644 Documentation/i2c/busses/i2c-imanager
create mode 100644 drivers/i2c/busses/imanager-ec-i2c.c
create mode 100644 drivers/i2c/busses/imanager-i2c.c
create mode 100644 include/linux/mfd/imanager/i2c.h
diff --git a/Documentation/i2c/busses/i2c-imanager b/Documentation/i2c/busses/i2c-imanager
new file mode 100644
index 0000000..d149fbf
--- /dev/null
+++ b/Documentation/i2c/busses/i2c-imanager
@@ -0,0 +1,48 @@
+Kernel driver imanager_i2c
+==========================
+
+This platform driver provides support for iManager I2C/SMBus.
+
+This driver depends on imanager (mfd).
+
+Module Parameters
+-----------------
+
+* bus_frequency (unsigned short)
+Set desired bus frequency. Valid values (kHz) are:
+ 50 Slow
+ 100 Standard (default)
+ 400 Fast
+
+
+Description
+-----------
+
+The Advantech iManager provides up to four SMBus controllers. One of them
+is configured for I2C compatibility.
+
+
+Process Call Support
+--------------------
+
+Not supported.
+
+
+I2C Block Read Support
+----------------------
+
+I2C block read is supported.
+
+
+SMBus 2.0 Support
+-----------------
+
+Several SMBus 2.0 features are supported.
+No PEC support.
+
+
+Interrupt Support
+-----------------
+
+No interrupt support available
+
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 7b0aa82..4c401a4 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -42,6 +42,17 @@ config I2C_ALI15X3
This driver can also be built as a module. If so, the module
will be called i2c-ali15x3.
+config I2C_IMANAGER
+ tristate "Advantech iManager I2C Interface"
+ depends on MFD_IMANAGER
+ help
+ This enables support for Advantech iManager I2C of some
+ Advantech SOM, MIO, AIMB, and PCM modules/boards.
+ Requires mfd-core and imanager-core to function properly.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-imanager.
+
config I2C_AMD756
tristate "AMD 756/766/768/8111 and nVidia nForce"
depends on PCI
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 37f2819..da76404 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -15,6 +15,8 @@ obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o
obj-$(CONFIG_I2C_I801) += i2c-i801.o
obj-$(CONFIG_I2C_ISCH) += i2c-isch.o
obj-$(CONFIG_I2C_ISMT) += i2c-ismt.o
+i2c-imanager-objs := imanager-i2c.o imanager-ec-i2c.o
+obj-$(CONFIG_I2C_IMANAGER) += i2c-imanager.o
obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o
obj-$(CONFIG_I2C_NFORCE2_S4985) += i2c-nforce2-s4985.o
obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o
diff --git a/drivers/i2c/busses/imanager-ec-i2c.c b/drivers/i2c/busses/imanager-ec-i2c.c
new file mode 100644
index 0000000..e94964f
--- /dev/null
+++ b/drivers/i2c/busses/imanager-ec-i2c.c
@@ -0,0 +1,466 @@
+/*
+ * Advantech iManager I2C bus core
+ *
+ * Copyright (C) 2015 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/byteorder/generic.h>
+#include <linux/mfd/imanager/ec.h>
+#include <linux/mfd/imanager/i2c.h>
+
+#define I2C_SMBUS_BLOCK_SIZE 32
+
+#define EVAL_WR_SIZE(x) \
+ (x < I2C_SMBUS_BLOCK_SIZE ? x : I2C_SMBUS_BLOCK_SIZE - 1)
+#define EVAL_RD_SIZE(x) \
+ (x && (x <= I2C_SMBUS_BLOCK_SIZE) ? x : I2C_SMBUS_BLOCK_SIZE)
+
+#define EC_HWRAM_OFFSET_STATUS 0UL
+
+#define I2C_ERR_PROTO 0x19UL
+#define I2C_ERR_TIMEOUT 0x18UL
+#define I2C_ERR_ACCESS 0x17UL
+#define I2C_ERR_UNKNOWN 0x13UL
+#define I2C_ERR_ADDR_NACK 0x10UL
+
+#define EC_SMB_DID(N) (0x28 + N)
+
+struct ec_i2c_status {
+ u32 error : 7;
+ u32 complete : 1;
+};
+
+static const struct imanager_i2c_device *i2c;
+
+static int i2c_core_eval_status(u8 _status)
+{
+ struct ec_i2c_status *status = (struct ec_i2c_status *)&_status;
+ int err = 0;
+
+ switch (status->error) {
+ case 0:
+ break;
+ case I2C_ERR_ADDR_NACK:
+ err = -ENODEV;
+ break;
+ case I2C_ERR_ACCESS:
+ case I2C_ERR_UNKNOWN:
+ err = -EAGAIN;
+ break;
+ case I2C_ERR_TIMEOUT:
+ err = -ETIME;
+ break;
+ case I2C_ERR_PROTO:
+ err = -EPROTO;
+ break;
+ default:
+ pr_err("Undefined status code 0x%02X\n", status->error);
+ err = -EIO;
+ break;
+ }
+
+ return err;
+}
+
+static inline int i2c_send_message(u8 cmd, u8 param, struct ec_message *msg)
+{
+ int err;
+
+ err = imanager_msg_write(cmd, param, msg);
+ if (err)
+ return i2c_core_eval_status(err);
+
+ return 0;
+}
+
+static int i2c_core_blk_wr_rw_combined(u8 proto, struct ec_message *msg)
+{
+ int err;
+
+ if (WARN_ON(!msg))
+ return -EINVAL;
+
+ err = imanager_wait_proc_complete(EC_HWRAM_OFFSET_STATUS, 0);
+ if (err)
+ return err;
+
+ err = i2c_send_message(proto, i2c->i2coem->did, msg);
+ if (err)
+ return err;
+
+ if (msg->rlen) {
+ if (msg->rlen == 1)
+ return msg->u.data[0];
+ else if (msg->rlen == 2)
+ return (msg->u.data[1] << 8) | msg->u.data[0];
+ else
+ return msg->rlen;
+ }
+
+ return 0;
+}
+
+/* Write-Read and Read-Write wrappers */
+static inline int i2c_core_wr_combined(struct ec_message *msg)
+{
+ return i2c_core_blk_wr_rw_combined(EC_CMD_I2C_WR, msg);
+}
+
+static inline int i2c_core_rw_combined(struct ec_message *msg)
+{
+ return i2c_core_blk_wr_rw_combined(EC_CMD_I2C_RW, msg);
+}
+
+/*
+ * iManager I2C core API
+ */
+int i2c_core_write_quick(u16 addr)
+{
+ struct ec_message msg = {
+ .rlen = 0,
+ .wlen = sizeof(struct ec_message_header),
+ .u = {
+ .smb.hdr = {
+ .addr_low = LOADDR16(addr),
+ .addr_high = HIADDR16(addr),
+ .rlen = 0,
+ .wlen = 1,
+ },
+ },
+ };
+
+ return i2c_core_wr_combined(&msg);
+}
+
+int i2c_core_read_byte(u16 addr)
+{
+ struct ec_message msg = {
+ .rlen = 1,
+ .wlen = sizeof(struct ec_message_header),
+ .u = {
+ .smb.hdr = {
+ .addr_low = LOADDR16(addr),
+ .addr_high = HIADDR16(addr),
+ .rlen = 1,
+ .wlen = 0,
+ },
+ },
+ };
+
+ return i2c_core_rw_combined(&msg);
+}
+
+int i2c_core_write_byte(u16 addr, u8 cmd)
+{
+ struct ec_message msg = {
+ .rlen = 1,
+ .wlen = sizeof(struct ec_message_header),
+ .u = {
+ .smb.hdr = {
+ .addr_low = LOADDR16(addr),
+ .addr_high = HIADDR16(addr),
+ .rlen = 1,
+ .wlen = 1,
+ .cmd = cmd,
+ },
+ },
+ };
+
+ return i2c_core_wr_combined(&msg);
+}
+
+int i2c_core_read_byte_data(u16 addr, u8 cmd)
+{
+ struct ec_message msg = {
+ .rlen = 1,
+ .wlen = sizeof(struct ec_message_header),
+ .u = {
+ .smb.hdr = {
+ .addr_low = LOADDR16(addr),
+ .addr_high = HIADDR16(addr),
+ .rlen = 1,
+ .wlen = 1,
+ .cmd = cmd,
+ },
+ },
+ };
+
+ return i2c_core_wr_combined(&msg);
+}
+
+int i2c_core_write_byte_data(u16 addr, u8 cmd, u8 value)
+{
+ struct ec_message msg = {
+ .rlen = 1,
+ .wlen = sizeof(struct ec_message_header) + 1,
+ .u = {
+ .smb.hdr = {
+ .addr_low = LOADDR16(addr),
+ .addr_high = HIADDR16(addr),
+ .rlen = 0,
+ .wlen = 2,
+ .cmd = cmd,
+ },
+ .smb.data[0] = value,
+ },
+ };
+
+ return i2c_core_wr_combined(&msg);
+}
+
+int i2c_core_read_word_data(u16 addr, u8 cmd)
+{
+ struct ec_message msg = {
+ .rlen = 2,
+ .wlen = sizeof(struct ec_message_header),
+ .u = {
+ .smb.hdr = {
+ .addr_low = LOADDR16(addr),
+ .addr_high = HIADDR16(addr),
+ .rlen = 2,
+ .wlen = 1,
+ .cmd = cmd,
+ },
+ },
+ };
+
+ return i2c_core_wr_combined(&msg);
+}
+
+int i2c_core_write_word_data(u16 addr, u8 cmd, u16 value)
+{
+ struct ec_message msg = {
+ .rlen = 1,
+ .wlen = sizeof(struct ec_message_header) + 2,
+ .u = {
+ .smb.hdr = {
+ .addr_low = LOADDR16(addr),
+ .addr_high = HIADDR16(addr),
+ .rlen = 0,
+ .wlen = 3,
+ .cmd = cmd,
+ },
+ .smb.data[0] = LOBYTE16(value),
+ .smb.data[1] = HIBYTE16(value),
+ },
+ };
+
+ return i2c_core_wr_combined(&msg);
+}
+
+int i2c_core_write_block_data(u16 addr, u8 cmd, u8 *buf)
+{
+ int i;
+ struct ec_message msg = {
+ .rlen = 1,
+ .wlen = sizeof(struct ec_message_header) +
+ EVAL_WR_SIZE(buf[0]),
+ .u = {
+ .smb.hdr = {
+ .addr_low = LOADDR16(addr),
+ .addr_high = HIADDR16(addr),
+ .rlen = 0,
+ .wlen = 1 + EVAL_WR_SIZE(buf[0]),
+ .cmd = cmd,
+ },
+ },
+ };
+
+ if ((buf[0] == 0) || (buf[0] >= I2C_MAX_WRITE_BYTES)) {
+ pr_err("Invalid I2C write length %d\n", buf[0]);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < EVAL_WR_SIZE(buf[0]); i++)
+ msg.u.data[i + sizeof(struct ec_message_header)] = buf[i + 1];
+
+ return i2c_core_wr_combined(&msg);
+}
+
+int i2c_core_read_block_data(u16 addr, u8 cmd, u8 *buf)
+{
+ int i;
+ int ret;
+ struct ec_message msg = {
+ .rlen = EVAL_RD_SIZE(buf[0]),
+ .wlen = sizeof(struct ec_message_header),
+ .u = {
+ .smb.hdr = {
+ .addr_low = LOADDR16(addr),
+ .addr_high = HIADDR16(addr),
+ .rlen = EVAL_RD_SIZE(buf[0]),
+ .wlen = 1,
+ .cmd = cmd,
+ },
+ },
+ };
+
+ /*
+ * If buf[0] == 0 EC will read I2C_MAX_READ_BYTES
+ */
+ ret = i2c_core_wr_combined(&msg);
+ if (ret < 0) {
+ pr_err("I2C transaction failed\n");
+ return ret;
+ }
+
+ buf[0] = ret;
+ for (i = 0; i < ret; i++)
+ buf[i + 1] = msg.u.data[i];
+
+ return 0;
+}
+
+int i2c_core_read_i2c_block_data(u16 addr, u8 cmd, u8 *buf)
+{
+ int i;
+ int ret;
+ struct ec_message msg = {
+ .rlen = EVAL_RD_SIZE(buf[0]),
+ .wlen = sizeof(struct ec_message_header),
+ .u = {
+ .smb.hdr = {
+ .addr_low = LOADDR16(addr),
+ .addr_high = HIADDR16(addr),
+ .rlen = EVAL_RD_SIZE(buf[0]),
+ .wlen = 1,
+ .cmd = cmd,
+ },
+ },
+ };
+
+ if ((buf[0] == 0) || (buf[0] > I2C_MAX_READ_BYTES)) {
+ pr_err("Invalid I2C read length\n");
+ return -EINVAL;
+ }
+
+ ret = i2c_core_wr_combined(&msg);
+ if (ret < 0) {
+ pr_err("I2C transaction failed\n");
+ return ret;
+ }
+
+ buf[0] = ret;
+ for (i = 0; i < ret; i++)
+ buf[i + 1] = msg.u.data[i];
+
+ return 0;
+}
+
+int i2c_core_write_i2c_block_data(u16 addr, u8 cmd, u8 *buf)
+{
+ if (WARN_ON(!buf))
+ return -EINVAL;
+
+ return i2c_core_write_block_data(addr, cmd, buf);
+}
+
+int i2c_core_smb_get_freq(u32 bus_id)
+{
+ int ret = 0, f;
+ int freq_id, freq;
+
+ if (WARN_ON(bus_id > I2C_OEM_1))
+ return -EINVAL;
+
+ switch (i2c->ecdev->id) {
+ case IT8518:
+ case IT8528:
+ ret = imanager_read_word(EC_CMD_SMB_FREQ_RD,
+ EC_SMB_DID(bus_id));
+ if (ret < 0) {
+ pr_err("Failed to get bus frequency\n");
+ return ret;
+ }
+
+ freq_id = HIBYTE16(ret);
+ f = LOBYTE16(ret);
+ switch (freq_id) {
+ case 0:
+ freq = f;
+ break;
+ case 1:
+ freq = 50;
+ break;
+ case 2:
+ freq = 100;
+ break;
+ case 3:
+ freq = 400;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ pr_err("EC version not supported!\n");
+ return -ENODEV;
+ }
+
+ return freq;
+}
+
+int i2c_core_smb_set_freq(u32 bus_id, u32 freq)
+{
+ int err;
+ u16 val;
+
+ if (WARN_ON(bus_id > I2C_OEM_1))
+ return -EINVAL;
+
+ switch (i2c->ecdev->id) {
+ case IT8518:
+ case IT8528:
+ switch (freq) {
+ case 50:
+ val = 0x0100;
+ break;
+ case 100:
+ val = 0x0200;
+ break;
+ case 400:
+ val = 0x0300;
+ break;
+ default:
+ if (freq < 50)
+ val = freq;
+ else
+ return -EINVAL;
+ }
+
+ err = imanager_write_word(EC_CMD_SMB_FREQ_WR,
+ EC_SMB_DID(bus_id), val);
+ if (err) {
+ pr_err("Failed to set I2C bus frequency\n");
+ return err;
+ }
+ break;
+ default:
+ pr_err("EC version not supported!\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+int i2c_core_init(void)
+{
+ i2c = imanager_get_i2c_device();
+ if (!i2c)
+ return -ENODEV;
+
+ return 0;
+}
+
diff --git a/drivers/i2c/busses/imanager-i2c.c b/drivers/i2c/busses/imanager-i2c.c
new file mode 100644
index 0000000..fb7236d
--- /dev/null
+++ b/drivers/i2c/busses/imanager-i2c.c
@@ -0,0 +1,240 @@
+/*
+ * Advantech iManager I2C bus driver
+ *
+ * Copyright (C) 2015 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/mfd/imanager/core.h>
+#include <linux/mfd/imanager/i2c.h>
+
+static uint bus_frequency = 100;
+module_param(bus_frequency, uint, 0);
+MODULE_PARM_DESC(bus_frequency,
+ "I2C bus frequency [50, 100, 400]kHz (defaults to 100kHz)");
+
+struct imanager_i2c_data {
+ struct imanager_device_data *idev;
+ struct i2c_adapter adapter;
+};
+
+static int imanager_smb_access(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write, u8 command,
+ int size, union i2c_smbus_data *smb_data)
+{
+ struct imanager_i2c_data *data = i2c_get_adapdata(adap);
+ struct device *dev = data->adapter.dev.parent;
+ int ret = 0;
+ int val = 0;
+
+ if (!data)
+ return -ENODEV;
+
+ addr <<= 1;
+
+ mutex_lock(&data->idev->lock);
+
+ switch (size) {
+ case I2C_SMBUS_QUICK:
+ ret = i2c_core_write_quick(addr);
+ break;
+ case I2C_SMBUS_BYTE:
+ if (read_write == I2C_SMBUS_WRITE) /* NOT tested */
+ val = i2c_core_write_byte(addr, command);
+ else
+ val = i2c_core_read_byte(addr);
+
+ if (val < 0)
+ ret = val;
+ else
+ smb_data->byte = val;
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ if (read_write == I2C_SMBUS_WRITE)
+ val = i2c_core_write_byte_data(addr, command,
+ smb_data->byte);
+ else
+ val = i2c_core_read_byte_data(addr, command);
+
+ if (val < 0)
+ ret = val;
+ else
+ smb_data->byte = val;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ if (read_write == I2C_SMBUS_WRITE)
+ val = i2c_core_write_word_data(addr, command,
+ smb_data->word);
+ else
+ val = i2c_core_read_word_data(addr, command);
+
+ if (val < 0)
+ ret = val;
+ else
+ smb_data->word = val;
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ if (read_write == I2C_SMBUS_WRITE)
+ ret = i2c_core_write_block_data(addr, command,
+ smb_data->block);
+ else
+ ret = i2c_core_read_block_data(addr, command,
+ smb_data->block);
+ break;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ if (read_write == I2C_SMBUS_WRITE)
+ ret = i2c_core_write_i2c_block_data(addr, command,
+ smb_data->block);
+ else
+ ret = i2c_core_read_i2c_block_data(addr, command,
+ smb_data->block);
+ break;
+ default:
+ dev_err(dev, "Unsupported SMB transaction %d\n", size);
+ ret = -EOPNOTSUPP;
+ }
+
+ mutex_unlock(&data->idev->lock);
+
+ return ret;
+}
+
+static int imanager_i2c_access(struct i2c_adapter *adap, struct i2c_msg *msg,
+ int num)
+{
+ struct imanager_i2c_data *data = i2c_get_adapdata(adap);
+ struct device *dev = data->adapter.dev.parent;
+
+ /*
+ * To be implemented
+ */
+
+ dev_info(dev, "i2c_access() is not yet implemented. msg=%p, num=%d\n",
+ msg, num);
+
+ return 0;
+}
+
+static u32 imanager_smb_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_I2C;
+}
+
+static const struct i2c_algorithm imanager_algorithm = {
+ .smbus_xfer = imanager_smb_access,
+ .master_xfer = imanager_i2c_access,
+ .functionality = imanager_smb_i2c_func,
+};
+
+static int imanager_i2c_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imanager_device_data *idev = dev_get_drvdata(dev->parent);
+ struct imanager_i2c_data *i2c;
+ int ret;
+
+ if (!idev) {
+ dev_err(dev, "Invalid platform data\n");
+ return -EINVAL;
+ }
+
+ ret = i2c_core_init();
+ if (ret) {
+ dev_err(dev, "Failed to initialize I2C core\n");
+ return -EIO;
+ }
+
+ if (bus_frequency > 100)
+ bus_frequency = 400;
+ else if (bus_frequency < 50)
+ bus_frequency = 50;
+ else
+ bus_frequency = 100;
+
+ ret = i2c_core_smb_set_freq(I2C_OEM_1, bus_frequency);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set I2C bus frequency to %d kHz\n",
+ bus_frequency);
+ return ret;
+ }
+
+ ret = i2c_core_smb_get_freq(I2C_OEM_1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get I2C bus frequency\n");
+ return ret;
+ }
+ bus_frequency = ret;
+ dev_info(dev, "Bus frequency: %d kHz\n", bus_frequency);
+
+ i2c = devm_kzalloc(dev, sizeof(struct imanager_i2c_data), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, i2c);
+ i2c_set_adapdata(&i2c->adapter, i2c);
+
+ i2c->idev = idev;
+
+ i2c->adapter.owner = THIS_MODULE;
+ i2c->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ i2c->adapter.algo = &imanager_algorithm;
+
+ /* set up the sysfs linkage to our parent device */
+ i2c->adapter.dev.parent = dev;
+
+ /* Retry up to 3 times on lost arbitration */
+ i2c->adapter.retries = 3;
+
+ snprintf(i2c->adapter.name, sizeof(i2c->adapter.name),
+ "iManager I2C driver");
+
+ ret = i2c_add_adapter(&i2c->adapter);
+ if (ret) {
+ dev_err(dev, "Failed to add SMBus adapter\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imanager_i2c_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imanager_i2c_data *i2c = dev_get_drvdata(dev);
+
+ i2c_del_adapter(&i2c->adapter);
+ i2c_set_adapdata(&i2c->adapter, NULL);
+
+ return 0;
+}
+
+static struct platform_driver imanager_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "imanager_i2c",
+ },
+ .probe = imanager_i2c_probe,
+ .remove = imanager_i2c_remove,
+};
+
+module_platform_driver(imanager_i2c_driver);
+
+MODULE_DESCRIPTION("Advantech iManager I2C Driver");
+MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imanager_i2c");
diff --git a/include/linux/mfd/imanager/i2c.h b/include/linux/mfd/imanager/i2c.h
new file mode 100644
index 0000000..455f89f
--- /dev/null
+++ b/include/linux/mfd/imanager/i2c.h
@@ -0,0 +1,55 @@
+/*
+ * Advantech iManager I2C bus core
+ *
+ * Copyright (C) 2015 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __I2C_H__
+#define __I2C_H__
+
+#include <linux/types.h>
+
+#define I2C_MAX_READ_BYTES 32
+#define I2C_MAX_WRITE_BYTES 32
+
+/* Only for setting SMBus frequency */
+enum smb_bus_id {
+ SMB_OEM_0,
+ SMB_OEM_1,
+ SMB_OEM_2,
+ SMB_EEPROM,
+ SMB_TH_0,
+ SMB_TH_1,
+ SMB_SECURITY_EEPROM,
+ I2C_OEM_1,
+};
+
+int i2c_core_init(void);
+
+int i2c_core_write_quick(u16 addr);
+
+int i2c_core_read_byte(u16 addr);
+int i2c_core_write_byte(u16 addr, u8 cmd);
+
+int i2c_core_write_byte_data(u16 addr, u8 cmd, u8 value);
+int i2c_core_read_byte_data(u16 addr, u8 cmd);
+
+int i2c_core_write_word_data(u16 addr, u8 cmd, u16 value);
+int i2c_core_read_word_data(u16 addr, u8 cmd);
+
+int i2c_core_write_block_data(u16 addr, u8 cmd, u8 *buf);
+int i2c_core_read_block_data(u16 addr, u8 cmd, u8 *buf);
+
+int i2c_core_write_i2c_block_data(u16 addr, u8 cmd, u8 *buf);
+int i2c_core_read_i2c_block_data(u16 addr, u8 cmd, u8 *buf);
+
+int i2c_core_smb_get_freq(u32 bus_id);
+int i2c_core_smb_set_freq(u32 bus_id, u32 freq);
+
+#endif
--
2.6.4
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web