2014-12-13 11:46:50

by Muthu Mani

[permalink] [raw]
Subject: [PATCH v5 1/3] mfd: add support for Cypress CYUSBS234 USB Serial Bridge controller

Adds support for USB-I2C/GPIO interfaces of Cypress Semiconductor
CYUSBS234 USB-Serial Bridge controller.

Details about the device can be found at:
http://www.cypress.com/?rID=84126

Separate cell drivers are available for I2C and GPIO. SPI and UART
are not supported yet.

Signed-off-by: Muthu Mani <[email protected]>
Signed-off-by: Rajaram Regupathy <[email protected]>
---
Changes since v4:
* Used macros for subclass values

Changes since v3:
* Added devm_* allocation function
* Added vendor commands to read configuration info

Changes since v2:
* Used auto mfd id to support multiple devices
* Cleaned up the code

Changes since v1:
* Identified different serial interface and loaded correct cell driver
* Formed a mfd id to support multiple devices
* Removed unused platform device

drivers/mfd/Kconfig | 11 +++
drivers/mfd/Makefile | 1 +
drivers/mfd/cyusbs23x.c | 153 ++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/cyusbs23x.h | 55 +++++++++++++++
4 files changed, 220 insertions(+)
create mode 100644 drivers/mfd/cyusbs23x.c
create mode 100644 include/linux/mfd/cyusbs23x.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 2e6b731..109358e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -128,6 +128,17 @@ config MFD_ASIC3
This driver supports the ASIC3 multifunction chip found on many
PDAs (mainly iPAQ and HTC based ones)

+config MFD_CYUSBS23X
+ tristate "Cypress CYUSBS23x USB Serial Bridge controller"
+ select MFD_CORE
+ depends on USB
+ help
+ Say yes here if you want support for Cypress Semiconductor
+ CYUSBS23x USB-Serial Bridge controller.
+
+ This driver can also be built as a module. If so, the module will be
+ called cyusbs23x.
+
config PMIC_DA903X
bool "Dialog Semiconductor DA9030/DA9034 PMIC Support"
depends on I2C=y
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 53467e2..c0f6193 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -151,6 +151,7 @@ si476x-core-y := si476x-cmd.o si476x-prop.o si476x-i2c.o
obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o

obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
+obj-$(CONFIG_MFD_CYUSBS23X) += cyusbs23x.o
obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o
obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o
obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o
diff --git a/drivers/mfd/cyusbs23x.c b/drivers/mfd/cyusbs23x.c
new file mode 100644
index 0000000..18ebe6a
--- /dev/null
+++ b/drivers/mfd/cyusbs23x.c
@@ -0,0 +1,153 @@
+/*
+ * Cypress USB-Serial Bridge Controller USB adapter driver
+ *
+ * Copyright (c) 2014 Cypress Semiconductor Corporation.
+ *
+ * Author:
+ * Muthu Mani <[email protected]>
+ *
+ * Additional contributors include:
+ * Rajaram Regupathy <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This is core MFD driver for Cypress Semiconductor CYUSBS234 USB-Serial
+ * Bridge controller. CYUSBS234 offers a single channel serial interface
+ * (I2C/SPI/UART). It can be configured to enable either of I2C, SPI, UART
+ * interfaces. The GPIOs are also available to access.
+ * Details about the device can be found at:
+ * http://www.cypress.com/?rID=84126
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/cyusbs23x.h>
+#include <linux/usb.h>
+
+/* Serial interfaces (I2C, SPI, UART) differ in interface subclass */
+#define CY_USBS_SCB_DISABLED 0
+#define CY_USBS_SCB_UART 1
+#define CY_USBS_SCB_SPI 2
+#define CY_USBS_SCB_I2C 3
+
+static const struct usb_device_id cyusbs23x_usb_table[] = {
+ { USB_DEVICE(0x04b4, 0x0004) }, /* Cypress Semiconductor CYUSBS234 */
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, cyusbs23x_usb_table);
+
+static const struct mfd_cell cyusbs23x_i2c_gpio_devs[] = {
+ { .name = "cyusbs23x-i2c", },
+ { .name = "cyusbs23x-gpio", },
+};
+
+static int update_ep_details(struct usb_interface *interface,
+ struct cyusbs23x *cyusbs)
+{
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *ep;
+ int i;
+
+ iface_desc = interface->cur_altsetting;
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+
+ ep = &iface_desc->endpoint[i].desc;
+
+ if (!cyusbs->bulk_in_ep_num && usb_endpoint_is_bulk_in(ep))
+ cyusbs->bulk_in_ep_num = ep->bEndpointAddress;
+ if (!cyusbs->bulk_out_ep_num && usb_endpoint_is_bulk_out(ep))
+ cyusbs->bulk_out_ep_num = ep->bEndpointAddress;
+ if (!cyusbs->intr_in_ep_num && usb_endpoint_is_int_in(ep))
+ cyusbs->intr_in_ep_num = ep->bEndpointAddress;
+ }
+
+ dev_dbg(&interface->dev, "%s intr_in=%d, bulk_in=%d, bulk_out=%d\n",
+ __func__, cyusbs->intr_in_ep_num,
+ cyusbs->bulk_in_ep_num, cyusbs->bulk_out_ep_num);
+
+ if (!cyusbs->bulk_in_ep_num || !cyusbs->bulk_out_ep_num ||
+ !cyusbs->intr_in_ep_num) {
+ dev_err(&interface->dev, "%s invalid endpoints\n", __func__);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int cyusbs23x_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct cyusbs23x *cyusbs;
+ const struct mfd_cell *cyusbs23x_devs;
+ int ret, ndevs;
+
+ cyusbs = devm_kzalloc(&interface->dev, sizeof(*cyusbs), GFP_KERNEL);
+ if (!cyusbs)
+ return -ENOMEM;
+
+ ret = update_ep_details(interface, cyusbs);
+ if (ret)
+ return -ENODEV;
+
+ /* Serial interfaces (I2C, SPI, UART) differ in interface subclass */
+ switch (interface->cur_altsetting->desc.bInterfaceSubClass) {
+ case CY_USBS_SCB_I2C:
+ dev_info(&interface->dev, "using I2C interface\n");
+ cyusbs23x_devs = cyusbs23x_i2c_gpio_devs;
+ ndevs = ARRAY_SIZE(cyusbs23x_i2c_gpio_devs);
+ break;
+ default:
+ dev_err(&interface->dev, "unsupported subclass\n");
+ return -ENODEV;
+ }
+
+ cyusbs->usb_dev = usb_get_dev(interface_to_usbdev(interface));
+ cyusbs->usb_intf = interface;
+ usb_set_intfdata(interface, cyusbs);
+
+ ret = mfd_add_devices(&interface->dev, PLATFORM_DEVID_AUTO,
+ cyusbs23x_devs, ndevs, NULL, 0, NULL);
+ if (ret) {
+ dev_err(&interface->dev, "Failed to register devices\n");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ usb_put_dev(cyusbs->usb_dev);
+
+ return ret;
+}
+
+static void cyusbs23x_disconnect(struct usb_interface *interface)
+{
+ struct cyusbs23x *cyusbs = usb_get_intfdata(interface);
+
+ mfd_remove_devices(&interface->dev);
+ usb_put_dev(cyusbs->usb_dev);
+}
+
+static struct usb_driver cyusbs23x_usb_driver = {
+ .name = "cyusbs23x",
+ .probe = cyusbs23x_probe,
+ .disconnect = cyusbs23x_disconnect,
+ .id_table = cyusbs23x_usb_table,
+};
+
+module_usb_driver(cyusbs23x_usb_driver);
+
+MODULE_AUTHOR("Rajaram Regupathy <[email protected]>");
+MODULE_AUTHOR("Muthu Mani <[email protected]>");
+MODULE_DESCRIPTION("Cypress CYUSBS23x MFD core driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/cyusbs23x.h b/include/linux/mfd/cyusbs23x.h
new file mode 100644
index 0000000..10899ba
--- /dev/null
+++ b/include/linux/mfd/cyusbs23x.h
@@ -0,0 +1,55 @@
+/*
+ * Cypress USB-Serial Bridge Controller definitions
+ *
+ * Copyright (c) 2014 Cypress Semiconductor Corporation.
+ *
+ * Author:
+ * Muthu Mani <[email protected]>
+ *
+ * Additional contributors include:
+ * Rajaram Regupathy <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef __MFD_CYUSBS23X_H__
+#define __MFD_CYUSBS23X_H__
+
+#include <linux/types.h>
+#include <linux/usb.h>
+
+/* Structure to hold all device specific stuff */
+struct cyusbs23x {
+ struct usb_device *usb_dev;
+ struct usb_interface *usb_intf;
+
+ u8 bulk_in_ep_num;
+ u8 bulk_out_ep_num;
+ u8 intr_in_ep_num;
+};
+
+enum cy_usbs_vendor_cmds {
+ CY_DEV_READ_CONFIG_CMD = 0xB5,
+ CY_I2C_GET_CONFIG_CMD = 0xC4,
+ CY_I2C_SET_CONFIG_CMD = 0xC5,
+ CY_I2C_WRITE_CMD = 0xC6,
+ CY_I2C_READ_CMD = 0xC7,
+ CY_I2C_GET_STATUS_CMD = 0xC8,
+ CY_I2C_RESET_CMD = 0xC9,
+ CY_GPIO_GET_CONFIG_CMD = 0xD8,
+ CY_GPIO_SET_CONFIG_CMD = 0xD9,
+ CY_GPIO_GET_VALUE_CMD = 0xDA,
+ CY_GPIO_SET_VALUE_CMD = 0xDB,
+ CY_DEV_ENABLE_CONFIG_READ_CMD = 0xE2
+};
+
+/* SCB index shift */
+#define CY_SCB_INDEX_SHIFT 15
+
+#define CY_USBS_CTRL_XFER_TIMEOUT 2000
+#define CY_USBS_BULK_XFER_TIMEOUT 5000
+#define CY_USBS_INTR_XFER_TIMEOUT 5000
+
+#endif /* __MFD_CYUSBS23X_H__ */
--
1.8.3.2


This message and any attachments may contain Cypress (or its subsidiaries) confidential information. If it has been received in error, please advise the sender and immediately delete this message.


2014-12-13 11:47:07

by Muthu Mani

[permalink] [raw]
Subject: [PATCH v5 3/3] gpio: add support for Cypress CYUSBS234 USB-GPIO adapter

Adds support for USB-GPIO interface of Cypress Semiconductor
CYUSBS234 USB-Serial Bridge controller.

The GPIO get/set can be done through vendor command on control endpoint
for the configured gpios.

Details about the device can be found at:
http://www.cypress.com/?rID=84126

Signed-off-by: Muthu Mani <[email protected]>
Signed-off-by: Rajaram Regupathy <[email protected]>
---
Changes since v4:
* used BIT macro for the mask
* used macros for magic numbers

Changes since v3:
* added reading gpio configuration from device
* exposed only the gpios available
* exposed correct direction of gpios
* removed the direction input/output handler as
setting gpio direction is not supported by the device

Changes since v2:
* added helper macros
* removed lock
* given gpio chip device for dev_xxx
* cleaned up the code

Changes since v1:
* allocated memory on heap for usb transfer data
* changed gpio label as platform device name to identify multiple devices

drivers/gpio/Kconfig | 13 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-cyusbs23x.c | 291 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 305 insertions(+)
create mode 100644 drivers/gpio/gpio-cyusbs23x.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 23dfd5f..0b7b344 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -894,6 +894,19 @@ config GPIO_BCM_KONA

comment "USB GPIO expanders:"

+config GPIO_CYUSBS23X
+ tristate "CYUSBS23x GPIO support"
+ depends on MFD_CYUSBS23X
+ help
+ Say yes here to access the GPIO signals of Cypress
+ Semiconductor CYUSBS23x USB Serial Bridge Controller.
+
+ This driver enables the GPIO interface of CYUSBS23x USB Serial
+ Bridge controller.
+
+ This driver can also be built as a module. If so, the module will be
+ called gpio-cyusbs23x.
+
config GPIO_VIPERBOARD
tristate "Viperboard GPIO a & b support"
depends on MFD_VIPERBOARD && USB
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index e60677b..c269174 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o
obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o
+obj-$(CONFIG_GPIO_CYUSBS23X) += gpio-cyusbs23x.o
obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o
obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o
obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o
diff --git a/drivers/gpio/gpio-cyusbs23x.c b/drivers/gpio/gpio-cyusbs23x.c
new file mode 100644
index 0000000..523b4ec
--- /dev/null
+++ b/drivers/gpio/gpio-cyusbs23x.c
@@ -0,0 +1,291 @@
+/*
+ * GPIO subdriver for Cypress CYUSBS234 USB-Serial Bridge controller.
+ * Details about the device can be found at:
+ * http://www.cypress.com/?rID=84126
+ *
+ * Copyright (c) 2014 Cypress Semiconductor Corporation.
+ *
+ * Author:
+ * Muthu Mani <[email protected]>
+ *
+ * Additional contributors include:
+ * Rajaram Regupathy <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * Only the GPIOs configured as input and output using Configuration Utility
+ * can be requested. The GPIO configuration information is stored in the
+ * internal flash memory as bitmap. GPIO information is available at the
+ * following offset: Drive 0: offset 436, Drive 1: offset 440, Input: 444
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/mfd/cyusbs23x.h>
+
+#define CY_GPIO_GET_LEN 2
+#define CY_DEVICE_CONFIG_SIZE 512
+
+/* total GPIOs present */
+#define CYUSBS234_GPIO_NUM 12
+
+#define CY_USBS_READ_CONFIG 0xA6BC
+#define CY_USBS_ENABLE_READ 0xB1B0
+#define CY_USBS_DISABLE_READ 0xB9B0
+
+#define CY_CFG_DRIVE0_GPIO_OFFSET 436
+#define CY_CFG_DRIVE1_GPIO_OFFSET 440
+#define CY_CFG_INPUT_GPIO_OFFSET 444
+
+struct cyusbs_gpio {
+ struct gpio_chip gpio;
+ struct cyusbs23x *cyusbs;
+ u32 out_gpio; /* Bitmap of output GPIOs */
+ u32 out_value; /* Bitmap of output GPIOs value */
+ u32 in_gpio; /* Bitmap of input GPIOs */
+};
+
+#define to_cyusbs_gpio(chip) container_of(chip, struct cyusbs_gpio, gpio)
+
+static int cy_gpio_get(struct gpio_chip *chip,
+ unsigned offset)
+{
+ int ret;
+ char *buf;
+ u16 wIndex, wValue;
+ struct cyusbs_gpio *gpio = to_cyusbs_gpio(chip);
+ struct cyusbs23x *cyusbs = gpio->cyusbs;
+
+ if (gpio->out_gpio & BIT(offset))
+ return !!(gpio->out_value & BIT(offset));
+
+ wValue = offset;
+ wIndex = 0;
+ buf = kmalloc(CY_GPIO_GET_LEN, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ ret = usb_control_msg(cyusbs->usb_dev,
+ usb_rcvctrlpipe(cyusbs->usb_dev, 0),
+ CY_GPIO_GET_VALUE_CMD,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ wValue, wIndex, buf, CY_GPIO_GET_LEN,
+ CY_USBS_CTRL_XFER_TIMEOUT);
+ if (ret == CY_GPIO_GET_LEN) {
+ dev_dbg(chip->dev, "%s: offset=%d %02X %02X\n",
+ __func__, offset, buf[0], buf[1]);
+ if (buf[0] == 0)
+ ret = !!buf[1];
+ else
+ ret = -EIO;
+ } else {
+ dev_err(chip->dev, "%s: offset=%d %d\n", __func__, offset, ret);
+ ret = -EIO;
+ }
+
+ kfree(buf);
+ return ret;
+}
+
+static void cy_gpio_set(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ int ret;
+ u16 wIndex, wValue;
+ struct cyusbs_gpio *gpio = to_cyusbs_gpio(chip);
+ struct cyusbs23x *cyusbs = gpio->cyusbs;
+
+ wValue = offset;
+ wIndex = value;
+
+ ret = usb_control_msg(cyusbs->usb_dev,
+ usb_sndctrlpipe(cyusbs->usb_dev, 0),
+ CY_GPIO_SET_VALUE_CMD,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ wValue, wIndex, NULL, 0, CY_USBS_CTRL_XFER_TIMEOUT);
+ if (ret < 0)
+ dev_err(chip->dev, "error setting gpio %d: %d\n", offset, ret);
+ else {
+ if (value)
+ gpio->out_value |= BIT(offset);
+ else
+ gpio->out_value &= ~BIT(offset);
+ }
+}
+
+static int cy_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ u32 gpios;
+ struct cyusbs_gpio *gpio = to_cyusbs_gpio(chip);
+
+ gpios = gpio->out_gpio | gpio->in_gpio;
+ if (gpios & BIT(offset))
+ return 0;
+
+ return -ENODEV;
+}
+
+static int cy_gpio_get_direction(struct gpio_chip *chip,
+ unsigned offset)
+{
+ struct cyusbs_gpio *gpio = to_cyusbs_gpio(chip);
+
+ if (gpio->out_gpio & BIT(offset))
+ return GPIOF_DIR_OUT;
+ else if (gpio->in_gpio & BIT(offset))
+ return GPIOF_DIR_IN;
+
+ return -EIO;
+}
+
+static int cy_get_device_config(struct cyusbs23x *cyusbs, u8 *buf)
+{
+ int ret;
+
+ ret = usb_control_msg(cyusbs->usb_dev,
+ usb_sndctrlpipe(cyusbs->usb_dev, 0),
+ CY_DEV_ENABLE_CONFIG_READ_CMD,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ CY_USBS_READ_CONFIG, CY_USBS_ENABLE_READ, NULL, 0,
+ CY_USBS_CTRL_XFER_TIMEOUT);
+ if (ret) {
+ dev_err(&cyusbs->usb_dev->dev,
+ "%s: enable config read status %d\n", __func__, ret);
+ return -ENODEV;
+ }
+
+ ret = usb_control_msg(cyusbs->usb_dev,
+ usb_rcvctrlpipe(cyusbs->usb_dev, 0),
+ CY_DEV_READ_CONFIG_CMD,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0, 0, buf, CY_DEVICE_CONFIG_SIZE,
+ CY_USBS_CTRL_XFER_TIMEOUT);
+ if (ret != CY_DEVICE_CONFIG_SIZE) {
+ dev_err(&cyusbs->usb_dev->dev,
+ "%s: read config status %d\n", __func__, ret);
+ return -ENODEV;
+ }
+
+ ret = usb_control_msg(cyusbs->usb_dev,
+ usb_sndctrlpipe(cyusbs->usb_dev, 0),
+ CY_DEV_ENABLE_CONFIG_READ_CMD,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ CY_USBS_READ_CONFIG, CY_USBS_DISABLE_READ, NULL, 0,
+ CY_USBS_CTRL_XFER_TIMEOUT);
+ if (ret) {
+ dev_err(&cyusbs->usb_dev->dev,
+ "%s: disable config read status %d\n", __func__, ret);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int cy_gpio_retrieve_gpio_details(struct cyusbs_gpio *gpio)
+{
+ int ret;
+ u32 drive0, drive1;
+ u8 *buf;
+ struct cyusbs23x *cyusbs = gpio->cyusbs;
+
+ buf = kzalloc(CY_DEVICE_CONFIG_SIZE, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ ret = cy_get_device_config(cyusbs, buf);
+ if (ret) {
+ dev_err(&cyusbs->usb_dev->dev,
+ "could not retrieve device configuration\n");
+ goto error;
+ }
+
+ /* Retrieve the GPIO configuration details */
+ drive0 = le32_to_cpu(*((u32 *)&buf[CY_CFG_DRIVE0_GPIO_OFFSET]));
+ drive1 = le32_to_cpu(*((u32 *)&buf[CY_CFG_DRIVE1_GPIO_OFFSET]));
+ gpio->out_gpio = drive0 | drive1;
+ gpio->out_value = drive1;
+ gpio->in_gpio = le32_to_cpu(*((u32 *)&buf[CY_CFG_INPUT_GPIO_OFFSET]));
+
+error:
+ kfree(buf);
+ return ret;
+}
+
+static int cyusbs23x_gpio_probe(struct platform_device *pdev)
+{
+ struct cyusbs23x *cyusbs;
+ struct cyusbs_gpio *cy_gpio;
+ int ret = 0;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ cyusbs = dev_get_drvdata(pdev->dev.parent);
+
+ cy_gpio = devm_kzalloc(&pdev->dev, sizeof(*cy_gpio), GFP_KERNEL);
+ if (cy_gpio == NULL)
+ return -ENOMEM;
+
+ cy_gpio->cyusbs = cyusbs;
+ /* retrieve GPIO configuration info */
+ ret = cy_gpio_retrieve_gpio_details(cy_gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "could not retrieve gpio details\n");
+ return -ENODEV;
+ }
+
+ /* registering gpio */
+ cy_gpio->gpio.label = dev_name(&pdev->dev);
+ cy_gpio->gpio.dev = &pdev->dev;
+ cy_gpio->gpio.owner = THIS_MODULE;
+ cy_gpio->gpio.base = -1;
+ cy_gpio->gpio.ngpio = CYUSBS234_GPIO_NUM;
+ cy_gpio->gpio.can_sleep = true;
+ cy_gpio->gpio.request = cy_gpio_request;
+ cy_gpio->gpio.set = cy_gpio_set;
+ cy_gpio->gpio.get = cy_gpio_get;
+ cy_gpio->gpio.get_direction = cy_gpio_get_direction;
+ ret = gpiochip_add(&cy_gpio->gpio);
+ if (ret < 0) {
+ dev_err(cy_gpio->gpio.dev, "could not add gpio\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, cy_gpio);
+
+ dev_dbg(&pdev->dev, "added GPIO\n");
+ return ret;
+}
+
+static int cyusbs23x_gpio_remove(struct platform_device *pdev)
+{
+ struct cyusbs_gpio *cy_gpio = platform_get_drvdata(pdev);
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+ gpiochip_remove(&cy_gpio->gpio);
+ return 0;
+}
+
+static struct platform_driver cyusbs23x_gpio_driver = {
+ .driver.name = "cyusbs23x-gpio",
+ .probe = cyusbs23x_gpio_probe,
+ .remove = cyusbs23x_gpio_remove,
+};
+
+module_platform_driver(cyusbs23x_gpio_driver);
+
+MODULE_AUTHOR("Rajaram Regupathy <[email protected]>");
+MODULE_AUTHOR("Muthu Mani <[email protected]>");
+MODULE_DESCRIPTION("GPIO driver for CYUSBS23x");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cyusbs23x-gpio");
--
1.8.3.2


This message and any attachments may contain Cypress (or its subsidiaries) confidential information. If it has been received in error, please advise the sender and immediately delete this message.

2014-12-13 12:01:45

by Muthu Mani

[permalink] [raw]
Subject: [PATCH v5 2/3] i2c: add support for Cypress CYUSBS234 USB-I2C adapter

Adds support for USB-I2C interface of Cypress Semiconductor
CYUSBS234 USB-Serial Bridge controller.

The read/write operation is setup using vendor command through control endpoint
and actual data transfer happens through bulk in/out endpoints.

Details about the device can be found at:
http://www.cypress.com/?rID=84126

Signed-off-by: Muthu Mani <[email protected]>
Signed-off-by: Rajaram Regupathy <[email protected]>
---
Changes since v4:
* used interface number from interface pointer instead of separate member var

Changes since v3:
* corrected typo

Changes since v2:
* Retrieved the i2c transfer status using interrupt in endpoint
* Used kstrtol instead of manually parsing and scnprintf instead of sprintf
* Given i2c adapter device for dev_xxx
* cleaned up the code

Changes since v1:
* allocated memory on heap for usb transfer data
* Used DEVICE_ATTR_xx and friends and attribute groups
* Added the device files under i2c-adapter rather than platform device

drivers/i2c/busses/Kconfig | 12 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-cyusbs23x.c | 585 +++++++++++++++++++++++++++++++++++++
3 files changed, 598 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-cyusbs23x.c

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index b4d135c..a9cd3e2 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -871,6 +871,18 @@ config I2C_RCAR

comment "External I2C/SMBus adapter drivers"

+config I2C_CYUSBS23X
+ tristate "CYUSBS23x I2C adapter"
+ depends on MFD_CYUSBS23X
+ help
+ Say yes if you would like to access Cypress CYUSBS23x I2C device.
+
+ This driver enables the I2C interface of CYUSBS23x USB Serial Bridge
+ controller.
+
+ This driver can also be built as a module. If so, the module will be
+ called i2c-cyusbs23x.
+
config I2C_DIOLAN_U2C
tristate "Diolan U2C-12 USB adapter"
depends on USB
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index cdac7f1..ad2b283 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o

# External I2C/SMBus adapter drivers
+obj-$(CONFIG_I2C_CYUSBS23X) += i2c-cyusbs23x.o
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
diff --git a/drivers/i2c/busses/i2c-cyusbs23x.c b/drivers/i2c/busses/i2c-cyusbs23x.c
new file mode 100644
index 0000000..452c370
--- /dev/null
+++ b/drivers/i2c/busses/i2c-cyusbs23x.c
@@ -0,0 +1,585 @@
+/*
+ * I2C subdriver for Cypress CYUSBS234 USB-Serial Bridge controller.
+ * Details about the device can be found at:
+ * http://www.cypress.com/?rID=84126
+ *
+ * Copyright (c) 2014 Cypress Semiconductor Corporation.
+ *
+ * Author:
+ * Rajaram Regupathy <[email protected]>
+ *
+ * Additional contributors include:
+ * Muthu Mani <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * It exposes sysfs entries under the i2c adapter for getting the i2c transfer
+ * status, reset i2c read/write module, get/set nak and stop bits.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/mfd/cyusbs23x.h>
+
+#define CY_I2C_MODE_READ 0
+#define CY_I2C_MODE_WRITE 1
+
+#define CY_I2C_XFER_STATUS_LEN 3
+
+struct cyusbs_i2c_config {
+ /* Frequency of operation. Only valid values are 100KHz and 400KHz */
+ u32 frequency;
+ u8 slave_addr; /* Slave address to be used when in slave mode */
+ u8 is_msb_first; /* Whether to transmit MSB first */
+ /*
+ * Whether block is configured as a master:
+ * 1 - The block functions as I2C master;
+ * 0 - The block functions as I2C slave
+ */
+ u8 is_master;
+ u8 s_ignore; /* Ignore general call in slave mode */
+ /* Whether to stretch clock in case of no FIFO availability */
+ u8 clock_stretch;
+ /* Whether to loop back TX data to RX. Valid only for debug purposes */
+ u8 is_loopback;
+ u8 reserved[6]; /* Reserved for future use */
+} __packed;
+
+struct cyusbs_i2c {
+ struct i2c_adapter i2c_adapter;
+ struct cyusbs_i2c_config *i2c_config;
+ struct mutex lock;
+
+ bool is_stop_bit; /* set the stop bit for i2c transfer */
+ bool is_nak_bit; /* set the nak bit for i2c transfer */
+};
+
+#define to_cyusbs_i2c(a) container_of(a, struct cyusbs_i2c, i2c_adapter)
+
+static int cy_i2c_get_status(struct i2c_adapter *adapter, u8 *buf, u16 mode);
+static int cy_i2c_reset(struct i2c_adapter *adapter, u16 mode);
+
+static ssize_t i2c_read_status_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(d);
+
+ /* Updates buffer with 3 bytes status (hex data) */
+ return cy_i2c_get_status(adapter, buf, CY_I2C_MODE_READ);
+}
+
+static ssize_t i2c_write_status_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(d);
+
+ /* Updates buffer with 3 bytes status (hex data) */
+ return cy_i2c_get_status(adapter, buf, CY_I2C_MODE_WRITE);
+}
+
+static ssize_t i2c_read_reset_store(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ long value;
+ struct i2c_adapter *adapter = to_i2c_adapter(d);
+
+ ret = kstrtol(buf, 0, &value);
+ if (ret)
+ return ret;
+
+ if (value != 1)
+ return -EINVAL;
+
+ ret = cy_i2c_reset(adapter, CY_I2C_MODE_READ);
+ if (!ret)
+ ret = count;
+
+ return ret;
+}
+
+static ssize_t i2c_write_reset_store(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ long value;
+ struct i2c_adapter *adapter = to_i2c_adapter(d);
+
+ ret = kstrtol(buf, 0, &value);
+ if (ret)
+ return ret;
+
+ if (value != 1)
+ return -EINVAL;
+
+ ret = cy_i2c_reset(adapter, CY_I2C_MODE_WRITE);
+ if (!ret)
+ ret = count;
+
+ return ret;
+}
+
+static ssize_t is_stop_bit_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(d);
+ struct cyusbs_i2c *cy_i2c = to_cyusbs_i2c(adapter);
+
+ return scnprintf(buf, 3, "%d\n", cy_i2c->is_stop_bit);
+}
+
+static ssize_t is_stop_bit_store(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(d);
+ struct cyusbs_i2c *cy_i2c = to_cyusbs_i2c(adapter);
+ long value;
+ int ret;
+
+ ret = kstrtol(buf, 0, &value);
+ if (ret)
+ return ret;
+
+ if (value != 0 && value != 1)
+ return -EINVAL;
+
+ cy_i2c->is_stop_bit = (bool)value;
+
+ return count;
+}
+
+static ssize_t is_nak_bit_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(d);
+ struct cyusbs_i2c *cy_i2c = to_cyusbs_i2c(adapter);
+
+ return scnprintf(buf, 3, "%d\n", cy_i2c->is_nak_bit);
+}
+
+static ssize_t is_nak_bit_store(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(d);
+ struct cyusbs_i2c *cy_i2c = to_cyusbs_i2c(adapter);
+ long value;
+ int ret;
+
+ ret = kstrtol(buf, 0, &value);
+ if (ret)
+ return ret;
+
+ if (value != 0 && value != 1)
+ return -EINVAL;
+
+ cy_i2c->is_nak_bit = (bool)value;
+
+ return count;
+}
+
+static DEVICE_ATTR_RO(i2c_read_status);
+static DEVICE_ATTR_RO(i2c_write_status);
+static DEVICE_ATTR_WO(i2c_read_reset);
+static DEVICE_ATTR_WO(i2c_write_reset);
+static DEVICE_ATTR_RW(is_stop_bit);
+static DEVICE_ATTR_RW(is_nak_bit);
+
+static struct attribute *cyusbs_i2c_device_attrs[] = {
+ &dev_attr_i2c_read_status.attr,
+ &dev_attr_i2c_write_status.attr,
+ &dev_attr_i2c_read_reset.attr,
+ &dev_attr_i2c_write_reset.attr,
+ &dev_attr_is_stop_bit.attr,
+ &dev_attr_is_nak_bit.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(cyusbs_i2c_device);
+
+static int cy_i2c_get_status(struct i2c_adapter *adapter, u8 *buf, u16 mode)
+{
+ int ret;
+ u16 wIndex, wValue, scb_index;
+ u8 *data;
+ struct cyusbs23x *cyusbs = (struct cyusbs23x *)adapter->algo_data;
+ struct cyusbs_i2c *cy_i2c = to_cyusbs_i2c(adapter);
+ u8 ifnum = cyusbs->usb_intf->cur_altsetting->desc.bInterfaceNumber;
+
+ scb_index = ifnum & 0x01;
+ wValue = ((scb_index << CY_SCB_INDEX_SHIFT) | mode);
+ wIndex = 0;
+ data = kmalloc(CY_I2C_XFER_STATUS_LEN, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ mutex_lock(&cy_i2c->lock);
+ /* read the i2c transfer status */
+ ret = usb_control_msg(cyusbs->usb_dev,
+ usb_rcvctrlpipe(cyusbs->usb_dev, 0),
+ CY_I2C_GET_STATUS_CMD,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ wValue, wIndex, data, CY_I2C_XFER_STATUS_LEN,
+ CY_USBS_CTRL_XFER_TIMEOUT);
+ mutex_unlock(&cy_i2c->lock);
+ if (ret != CY_I2C_XFER_STATUS_LEN) {
+ dev_err(&adapter->dev, "%s: failed to get status: %d",
+ __func__, ret);
+ ret = usb_translate_errors(ret);
+ goto status_error;
+ }
+
+ memcpy(buf, data, CY_I2C_XFER_STATUS_LEN);
+ buf[CY_I2C_XFER_STATUS_LEN] = 0;
+
+ dev_dbg(&adapter->dev, "%s: %02x %02x %02x\n", __func__,
+ buf[0], buf[1], buf[2]);
+
+status_error:
+ kfree(data);
+ return ret;
+}
+
+static int cy_i2c_reset(struct i2c_adapter *adapter, u16 mode)
+{
+ int ret;
+ u16 wIndex, wValue, scb_index;
+ struct cyusbs23x *cyusbs = (struct cyusbs23x *)adapter->algo_data;
+ struct cyusbs_i2c *cy_i2c = to_cyusbs_i2c(adapter);
+ u8 ifnum = cyusbs->usb_intf->cur_altsetting->desc.bInterfaceNumber;
+
+ scb_index = ifnum & 0x01;
+ wValue = ((scb_index << CY_SCB_INDEX_SHIFT) | mode);
+ wIndex = 0;
+
+ mutex_lock(&cy_i2c->lock);
+ ret = usb_control_msg(cyusbs->usb_dev,
+ usb_sndctrlpipe(cyusbs->usb_dev, 0),
+ CY_I2C_RESET_CMD,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ wValue, wIndex, NULL, 0, CY_USBS_CTRL_XFER_TIMEOUT);
+ mutex_unlock(&cy_i2c->lock);
+ if (ret) {
+ dev_err(&adapter->dev, "%s: failed to reset: %d",
+ __func__, ret);
+ ret = usb_translate_errors(ret);
+ }
+
+ return ret;
+}
+
+static int cy_i2c_recv_status(struct i2c_adapter *adapter)
+{
+ int ret, actual_len;
+ u8 *data;
+ struct cyusbs23x *cyusbs = (struct cyusbs23x *)adapter->algo_data;
+
+ data = kmalloc(CY_I2C_XFER_STATUS_LEN, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ /* read the i2c transfer status */
+ ret = usb_interrupt_msg(cyusbs->usb_dev,
+ usb_rcvintpipe(cyusbs->usb_dev, cyusbs->intr_in_ep_num),
+ data, CY_I2C_XFER_STATUS_LEN, &actual_len,
+ CY_USBS_INTR_XFER_TIMEOUT);
+ if (ret < 0) {
+ dev_err(&adapter->dev,
+ "Failed to read from interrupt ep: %d\n", ret);
+ ret = usb_translate_errors(ret);
+ goto intr_ep_error;
+ }
+
+ dev_dbg(&adapter->dev, "%s: %02x %02x %02x\n", __func__,
+ data[0], data[1], data[2]);
+
+ if (data[0] & 0x1)
+ ret = -ETIMEDOUT;
+
+intr_ep_error:
+ kfree(data);
+ return ret;
+}
+
+static int cy_get_i2c_config(struct cyusbs23x *cyusbs,
+ struct cyusbs_i2c *cy_i2c)
+{
+ int ret;
+ u16 scb_index;
+ u8 ifnum = cyusbs->usb_intf->cur_altsetting->desc.bInterfaceNumber;
+
+ scb_index = (ifnum & 0x01) << CY_SCB_INDEX_SHIFT;
+
+ ret = usb_control_msg(cyusbs->usb_dev,
+ usb_rcvctrlpipe(cyusbs->usb_dev, 0),
+ CY_I2C_GET_CONFIG_CMD,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ scb_index, 0, cy_i2c->i2c_config,
+ sizeof(*cy_i2c->i2c_config), CY_USBS_CTRL_XFER_TIMEOUT);
+ if (ret != sizeof(*cy_i2c->i2c_config)) {
+ dev_err(&cyusbs->usb_intf->dev, "%s: %d\n",
+ __func__, ret);
+ ret = usb_translate_errors(ret);
+ goto config_error;
+ }
+
+ dev_dbg(&cyusbs->usb_intf->dev,
+ "%s: freq=%d, slave_addr=0x%02x, msb_first=%d, master=%d, ignore=%d, clock_stretch=%d, loopback=%d\n",
+ __func__, cy_i2c->i2c_config->frequency,
+ cy_i2c->i2c_config->slave_addr,
+ cy_i2c->i2c_config->is_msb_first,
+ cy_i2c->i2c_config->is_master,
+ cy_i2c->i2c_config->s_ignore,
+ cy_i2c->i2c_config->clock_stretch,
+ cy_i2c->i2c_config->is_loopback);
+
+config_error:
+ return ret;
+}
+
+static int cy_i2c_set_data_config(struct cyusbs23x *cyusbs,
+ struct cyusbs_i2c *cy_i2c, u16 slave_addr,
+ u16 length, u8 command)
+{
+ int ret;
+ u16 wIndex, wValue, scb_index;
+ u8 ifnum = cyusbs->usb_intf->cur_altsetting->desc.bInterfaceNumber;
+
+ scb_index = (ifnum & 0x01) << CY_SCB_INDEX_SHIFT;
+ slave_addr = (slave_addr & 0x7F);
+ wValue = scb_index | cy_i2c->is_stop_bit | cy_i2c->is_nak_bit << 1;
+ wValue |= (slave_addr << 8);
+ wIndex = length;
+
+ dev_dbg(&cy_i2c->i2c_adapter.dev,
+ "%s, cmd=0x%x, val=0x%x, idx=0x%x\n",
+ __func__, command, wValue, wIndex);
+
+ ret = usb_control_msg(cyusbs->usb_dev,
+ usb_sndctrlpipe(cyusbs->usb_dev, 0),
+ command, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ wValue, wIndex, NULL, 0, CY_USBS_CTRL_XFER_TIMEOUT);
+
+ return ret;
+}
+
+static int cy_i2c_read(struct i2c_adapter *adapter, struct i2c_msg *msgs)
+{
+ int ret;
+ int actual_read_len = 0;
+ struct cyusbs23x *cyusbs = (struct cyusbs23x *)adapter->algo_data;
+
+ dev_dbg(&adapter->dev, "%s\n", __func__);
+
+ ret = usb_bulk_msg(cyusbs->usb_dev,
+ usb_rcvbulkpipe(cyusbs->usb_dev, cyusbs->bulk_in_ep_num),
+ msgs[0].buf,
+ msgs[0].len,
+ &actual_read_len, CY_USBS_BULK_XFER_TIMEOUT);
+ if (ret)
+ dev_err(&adapter->dev,
+ "read %d/%d returned %d\n",
+ actual_read_len, msgs[0].len, ret);
+
+ ret = cy_i2c_recv_status(adapter);
+
+ return ret;
+}
+
+static int cy_i2c_write(struct i2c_adapter *adapter, struct i2c_msg *msgs)
+{
+ int ret;
+ int actual_write_len = 0;
+ struct cyusbs23x *cyusbs = (struct cyusbs23x *)adapter->algo_data;
+
+ dev_dbg(&adapter->dev, "%s\n", __func__);
+
+ ret = usb_bulk_msg(cyusbs->usb_dev,
+ usb_sndbulkpipe(cyusbs->usb_dev, cyusbs->bulk_out_ep_num),
+ msgs[0].buf,
+ msgs[0].len,
+ &actual_write_len, CY_USBS_BULK_XFER_TIMEOUT);
+ if (ret)
+ dev_err(&adapter->dev,
+ "write %d/%d returned %d\n",
+ actual_write_len, msgs[0].len, ret);
+
+ ret = cy_i2c_recv_status(adapter);
+
+ return ret;
+}
+
+static int cy_i2c_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ int ret = 0;
+ struct cyusbs_i2c *cy_i2c;
+ struct cyusbs23x *cyusbs = (struct cyusbs23x *)adapter->algo_data;
+
+ dev_dbg(&adapter->dev, "%s\n", __func__);
+
+ if (num > 1) {
+ dev_err(&adapter->dev, "i2c_msg number is > 1\n");
+ return -EIO;
+ }
+
+ cy_i2c = to_cyusbs_i2c(adapter);
+
+ mutex_lock(&cy_i2c->lock);
+ if (msgs[0].flags & I2C_M_RD) {
+ dev_dbg(&adapter->dev,
+ "I2C read requested for addr 0x%02x, data length %d\n",
+ msgs[0].addr, msgs[0].len);
+
+ ret = cy_i2c_set_data_config(cyusbs, cy_i2c, msgs[0].addr,
+ msgs[0].len, CY_I2C_READ_CMD);
+ if (ret < 0) {
+ dev_err(&adapter->dev,
+ "Set Config (read) failed with %d\n", ret);
+ goto io_error;
+ }
+
+ ret = cy_i2c_read(adapter, msgs);
+ if (ret) {
+ dev_err(&adapter->dev,
+ "Read failed with error code %d\n", ret);
+ goto io_error;
+ }
+ } else {
+ dev_dbg(&adapter->dev,
+ "I2C write requested for addr 0x%02x, data length %d\n",
+ msgs[0].addr, msgs[0].len);
+
+ ret = cy_i2c_set_data_config(cyusbs, cy_i2c, msgs[0].addr,
+ msgs[0].len, CY_I2C_WRITE_CMD);
+ if (ret < 0) {
+ dev_err(&adapter->dev,
+ "Set Config (write) failed with %d\n", ret);
+ goto io_error;
+ }
+
+ ret = cy_i2c_write(adapter, msgs);
+ if (ret) {
+ dev_err(&adapter->dev,
+ "Write failed with error code %d\n", ret);
+ goto io_error;
+ }
+ }
+ mutex_unlock(&cy_i2c->lock);
+ return ret;
+
+io_error:
+ mutex_unlock(&cy_i2c->lock);
+ return ret;
+}
+
+static u32 cy_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static const struct i2c_algorithm i2c_cyusbs23x_algorithm = {
+ .master_xfer = cy_i2c_xfer,
+ .functionality = cy_i2c_func,
+};
+
+static int cyusbs23x_i2c_probe(struct platform_device *pdev)
+{
+ struct cyusbs23x *cyusbs;
+ struct cyusbs_i2c *cy_i2c;
+ int ret = 0;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ cyusbs = dev_get_drvdata(pdev->dev.parent);
+
+ cy_i2c = devm_kzalloc(&pdev->dev, sizeof(*cy_i2c), GFP_KERNEL);
+ if (cy_i2c == NULL)
+ return -ENOMEM;
+
+ cy_i2c->i2c_config = devm_kzalloc(&pdev->dev,
+ sizeof(struct cyusbs_i2c_config),
+ GFP_KERNEL);
+ if (cy_i2c->i2c_config == NULL)
+ return -ENOMEM;
+
+ mutex_init(&cy_i2c->lock);
+ cy_i2c->is_stop_bit = 1;
+ cy_i2c->is_nak_bit = 1;
+
+ cy_i2c->i2c_adapter.owner = THIS_MODULE;
+ cy_i2c->i2c_adapter.class = I2C_CLASS_HWMON;
+ cy_i2c->i2c_adapter.algo = &i2c_cyusbs23x_algorithm;
+ cy_i2c->i2c_adapter.algo_data = cyusbs;
+ snprintf(cy_i2c->i2c_adapter.name, sizeof(cy_i2c->i2c_adapter.name),
+ "cyusbs23x_i2c at bus %03d dev %03d intf %03d",
+ cyusbs->usb_dev->bus->busnum, cyusbs->usb_dev->devnum,
+ cyusbs->usb_intf->cur_altsetting->desc.bInterfaceNumber);
+
+ cy_i2c->i2c_adapter.dev.parent = &pdev->dev;
+ cy_i2c->i2c_adapter.dev.groups = cyusbs_i2c_device_groups;
+
+ ret = cy_get_i2c_config(cyusbs, cy_i2c);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "error to get i2c config\n");
+ goto error;
+ }
+
+ if (!cy_i2c->i2c_config->is_master) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "not an I2C master\n");
+ goto error;
+ }
+
+ ret = i2c_add_adapter(&cy_i2c->i2c_adapter);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "failed to add i2c adapter\n");
+ goto error;
+ }
+
+ platform_set_drvdata(pdev, cy_i2c);
+
+ dev_dbg(&pdev->dev, "added I2C adapter\n");
+ return 0;
+
+error:
+ dev_err(&pdev->dev, "error occurred %d\n", ret);
+ return ret;
+}
+
+static int cyusbs23x_i2c_remove(struct platform_device *pdev)
+{
+ struct cyusbs_i2c *cy_i2c = platform_get_drvdata(pdev);
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+ i2c_del_adapter(&cy_i2c->i2c_adapter);
+ return 0;
+}
+
+static struct platform_driver cyusbs23x_i2c_driver = {
+ .driver.name = "cyusbs23x-i2c",
+ .probe = cyusbs23x_i2c_probe,
+ .remove = cyusbs23x_i2c_remove,
+};
+
+module_platform_driver(cyusbs23x_i2c_driver);
+
+MODULE_AUTHOR("Rajaram Regupathy <[email protected]>");
+MODULE_AUTHOR("Muthu Mani <[email protected]>");
+MODULE_DESCRIPTION("I2C adapter driver for CYUSBS23x");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cyusbs23x-i2c");
--
1.8.3.2


This message and any attachments may contain Cypress (or its subsidiaries) confidential information. If it has been received in error, please advise the sender and immediately delete this message.

2014-12-14 02:14:11

by Varka Bhadram

[permalink] [raw]
Subject: Re: [PATCH v5 3/3] gpio: add support for Cypress CYUSBS234 USB-GPIO adapter

On Saturday 13 December 2014 05:17 PM, Muthu Mani wrote:

> Adds support for USB-GPIO interface of Cypress Semiconductor
> CYUSBS234 USB-Serial Bridge controller.
>
> The GPIO get/set can be done through vendor command on control endpoint
> for the configured gpios.
>
> Details about the device can be found at:
> http://www.cypress.com/?rID=84126
>
> Signed-off-by: Muthu Mani <[email protected]>
> Signed-off-by: Rajaram Regupathy <[email protected]>

(...)

> +static int cy_gpio_get(struct gpio_chip *chip,
> + unsigned offset)
> +{
> + int ret;
> + char *buf;
> + u16 wIndex, wValue;
> + struct cyusbs_gpio *gpio = to_cyusbs_gpio(chip);
> + struct cyusbs23x *cyusbs = gpio->cyusbs;
> +
> + if (gpio->out_gpio & BIT(offset))
> + return !!(gpio->out_value & BIT(offset));
> +
> + wValue = offset;
> + wIndex = 0;
> + buf = kmalloc(CY_GPIO_GET_LEN, GFP_KERNEL);
> + if (buf == NULL)
> + return -ENOMEM;
> +

Using '!' operator is preferred!
if(!buf)
return -ENOMEM;

> + ret = usb_control_msg(cyusbs->usb_dev,
> + usb_rcvctrlpipe(cyusbs->usb_dev, 0),
> + CY_GPIO_GET_VALUE_CMD,
> + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
> + wValue, wIndex, buf, CY_GPIO_GET_LEN,
> + CY_USBS_CTRL_XFER_TIMEOUT);
> + if (ret == CY_GPIO_GET_LEN) {
> + dev_dbg(chip->dev, "%s: offset=%d %02X %02X\n",
> + __func__, offset, buf[0], buf[1]);
> + if (buf[0] == 0)
> + ret = !!buf[1];
> + else
> + ret = -EIO;
> + } else {
> + dev_err(chip->dev, "%s: offset=%d %d\n", __func__, offset, ret);
> + ret = -EIO;
> + }
> +
> + kfree(buf);
> + return ret;
> +}
> +
> +static void cy_gpio_set(struct gpio_chip *chip,
> + unsigned offset, int value)
> +{
> + int ret;
> + u16 wIndex, wValue;
> + struct cyusbs_gpio *gpio = to_cyusbs_gpio(chip);
> + struct cyusbs23x *cyusbs = gpio->cyusbs;
> +
> + wValue = offset;
> + wIndex = value;
> +
> + ret = usb_control_msg(cyusbs->usb_dev,
> + usb_sndctrlpipe(cyusbs->usb_dev, 0),
> + CY_GPIO_SET_VALUE_CMD,
> + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
> + wValue, wIndex, NULL, 0, CY_USBS_CTRL_XFER_TIMEOUT);
> + if (ret < 0)
> + dev_err(chip->dev, "error setting gpio %d: %d\n", offset, ret);
> + else {
> + if (value)
> + gpio->out_value |= BIT(offset);
> + else
> + gpio->out_value &= ~BIT(offset);
> + }
> +}
> +

Following Two functions returning an error code. Generally when control reached
to end of the function indicates that the function call is success.

Returning an error is not best way. On success every function will return a success return code..

> +static int cy_gpio_request(struct gpio_chip *chip, unsigned offset)
> +{
> + u32 gpios;
> + struct cyusbs_gpio *gpio = to_cyusbs_gpio(chip);
> +
> + gpios = gpio->out_gpio | gpio->in_gpio;
> + if (gpios & BIT(offset))
> + return 0;
> +
> + return -ENODEV;
> +}
> +
> +static int cy_gpio_get_direction(struct gpio_chip *chip,
> + unsigned offset)
> +{
> + struct cyusbs_gpio *gpio = to_cyusbs_gpio(chip);
> +
> + if (gpio->out_gpio & BIT(offset))
> + return GPIOF_DIR_OUT;
> + else if (gpio->in_gpio & BIT(offset))
> + return GPIOF_DIR_IN;
> +
> + return -EIO;
> +}
> +
> +static int cy_get_device_config(struct cyusbs23x *cyusbs, u8 *buf)
> +{
> + int ret;
> +
> + ret = usb_control_msg(cyusbs->usb_dev,
> + usb_sndctrlpipe(cyusbs->usb_dev, 0),
> + CY_DEV_ENABLE_CONFIG_READ_CMD,
> + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
> + CY_USBS_READ_CONFIG, CY_USBS_ENABLE_READ, NULL, 0,
> + CY_USBS_CTRL_XFER_TIMEOUT);
> + if (ret) {
> + dev_err(&cyusbs->usb_dev->dev,
> + "%s: enable config read status %d\n", __func__, ret);
> + return -ENODEV;
> + }
> +
> + ret = usb_control_msg(cyusbs->usb_dev,
> + usb_rcvctrlpipe(cyusbs->usb_dev, 0),
> + CY_DEV_READ_CONFIG_CMD,
> + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
> + 0, 0, buf, CY_DEVICE_CONFIG_SIZE,
> + CY_USBS_CTRL_XFER_TIMEOUT);
> + if (ret != CY_DEVICE_CONFIG_SIZE) {
> + dev_err(&cyusbs->usb_dev->dev,
> + "%s: read config status %d\n", __func__, ret);
> + return -ENODEV;
> + }
> +
> + ret = usb_control_msg(cyusbs->usb_dev,
> + usb_sndctrlpipe(cyusbs->usb_dev, 0),
> + CY_DEV_ENABLE_CONFIG_READ_CMD,
> + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
> + CY_USBS_READ_CONFIG, CY_USBS_DISABLE_READ, NULL, 0,
> + CY_USBS_CTRL_XFER_TIMEOUT);
> + if (ret) {
> + dev_err(&cyusbs->usb_dev->dev,
> + "%s: disable config read status %d\n", __func__, ret);
> + return -ENODEV;
> + }
> +
> + return 0;
> +}
> +
> +static int cy_gpio_retrieve_gpio_details(struct cyusbs_gpio *gpio)
> +{
> + int ret;
> + u32 drive0, drive1;
> + u8 *buf;
> + struct cyusbs23x *cyusbs = gpio->cyusbs;
> +
> + buf = kzalloc(CY_DEVICE_CONFIG_SIZE, GFP_KERNEL);
> + if (buf == NULL)
> + return -ENOMEM;
> +

Using '!' operator is preferred!
if(!buf)
return -ENOMEM;

> + ret = cy_get_device_config(cyusbs, buf);
> + if (ret) {
> + dev_err(&cyusbs->usb_dev->dev,
> + "could not retrieve device configuration\n");
> + goto error;
> + }
> +
> + /* Retrieve the GPIO configuration details */
> + drive0 = le32_to_cpu(*((u32 *)&buf[CY_CFG_DRIVE0_GPIO_OFFSET]));
> + drive1 = le32_to_cpu(*((u32 *)&buf[CY_CFG_DRIVE1_GPIO_OFFSET]));
> + gpio->out_gpio = drive0 | drive1;
> + gpio->out_value = drive1;
> + gpio->in_gpio = le32_to_cpu(*((u32 *)&buf[CY_CFG_INPUT_GPIO_OFFSET]));
> +
> +error:
> + kfree(buf);
> + return ret;
> +}
> +
> +static int cyusbs23x_gpio_probe(struct platform_device *pdev)
> +{
> + struct cyusbs23x *cyusbs;
> + struct cyusbs_gpio *cy_gpio;
> + int ret = 0;
> +
> + dev_dbg(&pdev->dev, "%s\n", __func__);
> +
> + cyusbs = dev_get_drvdata(pdev->dev.parent);
> +
> + cy_gpio = devm_kzalloc(&pdev->dev, sizeof(*cy_gpio), GFP_KERNEL);
> + if (cy_gpio == NULL)
> + return -ENOMEM;
> +

Using '!' operator is preferred!
if(!cy_gpio)
return -ENOMEM;

> + cy_gpio->cyusbs = cyusbs;
> + /* retrieve GPIO configuration info */
> + ret = cy_gpio_retrieve_gpio_details(cy_gpio);
> + if (ret) {
> + dev_err(&pdev->dev, "could not retrieve gpio details\n");
> + return -ENODEV;
> + }
> +
> + /* registering gpio */
> + cy_gpio->gpio.label = dev_name(&pdev->dev);
> + cy_gpio->gpio.dev = &pdev->dev;
> + cy_gpio->gpio.owner = THIS_MODULE;
> + cy_gpio->gpio.base = -1;
> + cy_gpio->gpio.ngpio = CYUSBS234_GPIO_NUM;
> + cy_gpio->gpio.can_sleep = true;
> + cy_gpio->gpio.request = cy_gpio_request;
> + cy_gpio->gpio.set = cy_gpio_set;
> + cy_gpio->gpio.get = cy_gpio_get;
> + cy_gpio->gpio.get_direction = cy_gpio_get_direction;

One line space...

--
Thanks and Regards,
Varka Bhadram.

2014-12-14 02:24:59

by Varka Bhadram

[permalink] [raw]
Subject: Re: [PATCH v5 2/3] i2c: add support for Cypress CYUSBS234 USB-I2C adapter


On Saturday 13 December 2014 05:17 PM, Muthu Mani wrote:
> Adds support for USB-I2C interface of Cypress Semiconductor
> CYUSBS234 USB-Serial Bridge controller.
>
> The read/write operation is setup using vendor command through control endpoint
> and actual data transfer happens through bulk in/out endpoints.
>
> Details about the device can be found at:
> http://www.cypress.com/?rID=84126
>
> Signed-off-by: Muthu Mani <[email protected]>
> Signed-off-by: Rajaram Regupathy <[email protected]>
> ---
> Changes since v4:
> * used interface number from interface pointer instead of separate member var
>
> Changes since v3:
> * corrected typo
>
> Changes since v2:
> * Retrieved the i2c transfer status using interrupt in endpoint
> * Used kstrtol instead of manually parsing and scnprintf instead of sprintf
> * Given i2c adapter device for dev_xxx
> * cleaned up the code
>
> Changes since v1:
> * allocated memory on heap for usb transfer data
> * Used DEVICE_ATTR_xx and friends and attribute groups
> * Added the device files under i2c-adapter rather than platform device
>
> drivers/i2c/busses/Kconfig | 12 +
> drivers/i2c/busses/Makefile | 1 +
> drivers/i2c/busses/i2c-cyusbs23x.c | 585 +++++++++++++++++++++++++++++++++++++
> 3 files changed, 598 insertions(+)
> create mode 100644 drivers/i2c/busses/i2c-cyusbs23x.c
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index b4d135c..a9cd3e2 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -871,6 +871,18 @@ config I2C_RCAR
>
> comment "External I2C/SMBus adapter drivers"
>
> +config I2C_CYUSBS23X
> + tristate "CYUSBS23x I2C adapter"
> + depends on MFD_CYUSBS23X
> + help
> + Say yes if you would like to access Cypress CYUSBS23x I2C device.
> +
> + This driver enables the I2C interface of CYUSBS23x USB Serial Bridge
> + controller.
> +
> + This driver can also be built as a module. If so, the module will be
> + called i2c-cyusbs23x.
> +
> config I2C_DIOLAN_U2C
> tristate "Diolan U2C-12 USB adapter"
> depends on USB
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index cdac7f1..ad2b283 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -86,6 +86,7 @@ obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
> obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
>
> # External I2C/SMBus adapter drivers
> +obj-$(CONFIG_I2C_CYUSBS23X) += i2c-cyusbs23x.o
> obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
> obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o
> obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
> diff --git a/drivers/i2c/busses/i2c-cyusbs23x.c b/drivers/i2c/busses/i2c-cyusbs23x.c
> new file mode 100644
> index 0000000..452c370
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-cyusbs23x.c
> @@ -0,0 +1,585 @@
> +/*
> + * I2C subdriver for Cypress CYUSBS234 USB-Serial Bridge controller.
> + * Details about the device can be found at:
> + * http://www.cypress.com/?rID=84126
> + *
> + * Copyright (c) 2014 Cypress Semiconductor Corporation.
> + *
> + * Author:
> + * Rajaram Regupathy <[email protected]>
> + *
> + * Additional contributors include:
> + * Muthu Mani <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + */
> +
> +/*
> + * It exposes sysfs entries under the i2c adapter for getting the i2c transfer
> + * status, reset i2c read/write module, get/set nak and stop bits.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_device.h>
> +#include <linux/usb.h>
> +#include <linux/i2c.h>
> +#include <linux/mfd/cyusbs23x.h>
> +
> +#define CY_I2C_MODE_READ 0
> +#define CY_I2C_MODE_WRITE 1
> +
> +#define CY_I2C_XFER_STATUS_LEN 3
> +
> +struct cyusbs_i2c_config {
> + /* Frequency of operation. Only valid values are 100KHz and 400KHz */
> + u32 frequency;
> + u8 slave_addr; /* Slave address to be used when in slave mode */
> + u8 is_msb_first; /* Whether to transmit MSB first */
> + /*
> + * Whether block is configured as a master:
> + * 1 - The block functions as I2C master;
> + * 0 - The block functions as I2C slave
> + */
> + u8 is_master;
> + u8 s_ignore; /* Ignore general call in slave mode */
> + /* Whether to stretch clock in case of no FIFO availability */
> + u8 clock_stretch;
> + /* Whether to loop back TX data to RX. Valid only for debug purposes */
> + u8 is_loopback;
> + u8 reserved[6]; /* Reserved for future use */
> +} __packed;
> +
> +struct cyusbs_i2c {
> + struct i2c_adapter i2c_adapter;
> + struct cyusbs_i2c_config *i2c_config;
> + struct mutex lock;
> +
> + bool is_stop_bit; /* set the stop bit for i2c transfer */
> + bool is_nak_bit; /* set the nak bit for i2c transfer */
> +};
> +
> +#define to_cyusbs_i2c(a) container_of(a, struct cyusbs_i2c, i2c_adapter)
> +
> +static int cy_i2c_get_status(struct i2c_adapter *adapter, u8 *buf, u16 mode);
> +static int cy_i2c_reset(struct i2c_adapter *adapter, u16 mode);
> +
> +static ssize_t i2c_read_status_show(struct device *d,
> + struct device_attribute *attr, char *buf)
> +{
> + struct i2c_adapter *adapter = to_i2c_adapter(d);
> +
> + /* Updates buffer with 3 bytes status (hex data) */
> + return cy_i2c_get_status(adapter, buf, CY_I2C_MODE_READ);
> +}
> +
> +static ssize_t i2c_write_status_show(struct device *d,
> + struct device_attribute *attr, char *buf)
> +{
> + struct i2c_adapter *adapter = to_i2c_adapter(d);
> +
> + /* Updates buffer with 3 bytes status (hex data) */
> + return cy_i2c_get_status(adapter, buf, CY_I2C_MODE_WRITE);
> +}
> +
> +static ssize_t i2c_read_reset_store(struct device *d,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + int ret;
> + long value;
> + struct i2c_adapter *adapter = to_i2c_adapter(d);
> +
> + ret = kstrtol(buf, 0, &value);
> + if (ret)
> + return ret;
> +
> + if (value != 1)
> + return -EINVAL;
> +
> + ret = cy_i2c_reset(adapter, CY_I2C_MODE_READ);
> + if (!ret)
> + ret = count;
> +
> + return ret;
> +}
> +
> +static ssize_t i2c_write_reset_store(struct device *d,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + int ret;
> + long value;
> + struct i2c_adapter *adapter = to_i2c_adapter(d);
> +
> + ret = kstrtol(buf, 0, &value);
> + if (ret)
> + return ret;
> +
> + if (value != 1)
> + return -EINVAL;
> +
> + ret = cy_i2c_reset(adapter, CY_I2C_MODE_WRITE);
> + if (!ret)
> + ret = count;
> +
> + return ret;
> +}
> +
> +static ssize_t is_stop_bit_show(struct device *d,
> + struct device_attribute *attr, char *buf)
> +{
> + struct i2c_adapter *adapter = to_i2c_adapter(d);
> + struct cyusbs_i2c *cy_i2c = to_cyusbs_i2c(adapter);
> +
> + return scnprintf(buf, 3, "%d\n", cy_i2c->is_stop_bit);
> +}
> +
> +static ssize_t is_stop_bit_store(struct device *d,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct i2c_adapter *adapter = to_i2c_adapter(d);
> + struct cyusbs_i2c *cy_i2c = to_cyusbs_i2c(adapter);
> + long value;
> + int ret;
> +
> + ret = kstrtol(buf, 0, &value);
> + if (ret)
> + return ret;
> +
> + if (value != 0 && value != 1)
> + return -EINVAL;
> +
> + cy_i2c->is_stop_bit = (bool)value;
> +
> + return count;
> +}
> +
> +static ssize_t is_nak_bit_show(struct device *d,
> + struct device_attribute *attr, char *buf)
> +{
> + struct i2c_adapter *adapter = to_i2c_adapter(d);
> + struct cyusbs_i2c *cy_i2c = to_cyusbs_i2c(adapter);
> +
> + return scnprintf(buf, 3, "%d\n", cy_i2c->is_nak_bit);
> +}
> +
> +static ssize_t is_nak_bit_store(struct device *d,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct i2c_adapter *adapter = to_i2c_adapter(d);
> + struct cyusbs_i2c *cy_i2c = to_cyusbs_i2c(adapter);
> + long value;
> + int ret;
> +
> + ret = kstrtol(buf, 0, &value);
> + if (ret)
> + return ret;
> +
> + if (value != 0 && value != 1)
> + return -EINVAL;
> +
> + cy_i2c->is_nak_bit = (bool)value;
> +
> + return count;
> +}
> +
> +static DEVICE_ATTR_RO(i2c_read_status);
> +static DEVICE_ATTR_RO(i2c_write_status);
> +static DEVICE_ATTR_WO(i2c_read_reset);
> +static DEVICE_ATTR_WO(i2c_write_reset);
> +static DEVICE_ATTR_RW(is_stop_bit);
> +static DEVICE_ATTR_RW(is_nak_bit);
> +
> +static struct attribute *cyusbs_i2c_device_attrs[] = {
> + &dev_attr_i2c_read_status.attr,
> + &dev_attr_i2c_write_status.attr,
> + &dev_attr_i2c_read_reset.attr,
> + &dev_attr_i2c_write_reset.attr,
> + &dev_attr_is_stop_bit.attr,
> + &dev_attr_is_nak_bit.attr,
> + NULL
> +};
> +
> +ATTRIBUTE_GROUPS(cyusbs_i2c_device);
> +
> +static int cy_i2c_get_status(struct i2c_adapter *adapter, u8 *buf, u16 mode)
> +{
> + int ret;
> + u16 wIndex, wValue, scb_index;
> + u8 *data;
> + struct cyusbs23x *cyusbs = (struct cyusbs23x *)adapter->algo_data;
> + struct cyusbs_i2c *cy_i2c = to_cyusbs_i2c(adapter);
> + u8 ifnum = cyusbs->usb_intf->cur_altsetting->desc.bInterfaceNumber;
> +
> + scb_index = ifnum & 0x01;
> + wValue = ((scb_index << CY_SCB_INDEX_SHIFT) | mode);
> + wIndex = 0;

One line space..

> + data = kmalloc(CY_I2C_XFER_STATUS_LEN, GFP_KERNEL);
> + if (data == NULL)
> + return -ENOMEM;
> +

if(!data)
return -ENOMEM;

> + mutex_lock(&cy_i2c->lock);
> + /* read the i2c transfer status */
> + ret = usb_control_msg(cyusbs->usb_dev,
> + usb_rcvctrlpipe(cyusbs->usb_dev, 0),
> + CY_I2C_GET_STATUS_CMD,
> + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
> + wValue, wIndex, data, CY_I2C_XFER_STATUS_LEN,
> + CY_USBS_CTRL_XFER_TIMEOUT);
> + mutex_unlock(&cy_i2c->lock);
> + if (ret != CY_I2C_XFER_STATUS_LEN) {
> + dev_err(&adapter->dev, "%s: failed to get status: %d",
> + __func__, ret);
> + ret = usb_translate_errors(ret);
> + goto status_error;
> + }
> +
> + memcpy(buf, data, CY_I2C_XFER_STATUS_LEN);
> + buf[CY_I2C_XFER_STATUS_LEN] = 0;
> +
> + dev_dbg(&adapter->dev, "%s: %02x %02x %02x\n", __func__,
> + buf[0], buf[1], buf[2]);
> +
> +status_error:
> + kfree(data);
> + return ret;
> +}
> +
> +static int cy_i2c_reset(struct i2c_adapter *adapter, u16 mode)
> +{
> + int ret;
> + u16 wIndex, wValue, scb_index;
> + struct cyusbs23x *cyusbs = (struct cyusbs23x *)adapter->algo_data;
> + struct cyusbs_i2c *cy_i2c = to_cyusbs_i2c(adapter);
> + u8 ifnum = cyusbs->usb_intf->cur_altsetting->desc.bInterfaceNumber;
> +
> + scb_index = ifnum & 0x01;
> + wValue = ((scb_index << CY_SCB_INDEX_SHIFT) | mode);
> + wIndex = 0;
> +
> + mutex_lock(&cy_i2c->lock);
> + ret = usb_control_msg(cyusbs->usb_dev,
> + usb_sndctrlpipe(cyusbs->usb_dev, 0),
> + CY_I2C_RESET_CMD,
> + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
> + wValue, wIndex, NULL, 0, CY_USBS_CTRL_XFER_TIMEOUT);
> + mutex_unlock(&cy_i2c->lock);
> + if (ret) {
> + dev_err(&adapter->dev, "%s: failed to reset: %d",
> + __func__, ret);
> + ret = usb_translate_errors(ret);
> + }
> +
> + return ret;
> +}
> +
> +static int cy_i2c_recv_status(struct i2c_adapter *adapter)
> +{
> + int ret, actual_len;
> + u8 *data;
> + struct cyusbs23x *cyusbs = (struct cyusbs23x *)adapter->algo_data;
> +
> + data = kmalloc(CY_I2C_XFER_STATUS_LEN, GFP_KERNEL);
> + if (data == NULL)
> + return -ENOMEM;
> +

if(!data)
return -ENOMEM;

> + /* read the i2c transfer status */
> + ret = usb_interrupt_msg(cyusbs->usb_dev,
> + usb_rcvintpipe(cyusbs->usb_dev, cyusbs->intr_in_ep_num),
> + data, CY_I2C_XFER_STATUS_LEN, &actual_len,
> + CY_USBS_INTR_XFER_TIMEOUT);
> + if (ret < 0) {
> + dev_err(&adapter->dev,
> + "Failed to read from interrupt ep: %d\n", ret);
> + ret = usb_translate_errors(ret);
> + goto intr_ep_error;
> + }
> +
> + dev_dbg(&adapter->dev, "%s: %02x %02x %02x\n", __func__,
> + data[0], data[1], data[2]);
> +
> + if (data[0] & 0x1)
> + ret = -ETIMEDOUT;
> +
> +intr_ep_error:
> + kfree(data);
> + return ret;
> +}
> +
> +static int cy_get_i2c_config(struct cyusbs23x *cyusbs,
> + struct cyusbs_i2c *cy_i2c)
> +{
> + int ret;
> + u16 scb_index;
> + u8 ifnum = cyusbs->usb_intf->cur_altsetting->desc.bInterfaceNumber;
> +
> + scb_index = (ifnum & 0x01) << CY_SCB_INDEX_SHIFT;
> +
> + ret = usb_control_msg(cyusbs->usb_dev,
> + usb_rcvctrlpipe(cyusbs->usb_dev, 0),
> + CY_I2C_GET_CONFIG_CMD,
> + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
> + scb_index, 0, cy_i2c->i2c_config,
> + sizeof(*cy_i2c->i2c_config), CY_USBS_CTRL_XFER_TIMEOUT);
> + if (ret != sizeof(*cy_i2c->i2c_config)) {
> + dev_err(&cyusbs->usb_intf->dev, "%s: %d\n",
> + __func__, ret);
> + ret = usb_translate_errors(ret);
> + goto config_error;
> + }
> +
> + dev_dbg(&cyusbs->usb_intf->dev,
> + "%s: freq=%d, slave_addr=0x%02x, msb_first=%d, master=%d, ignore=%d, clock_stretch=%d, loopback=%d\n",
> + __func__, cy_i2c->i2c_config->frequency,
> + cy_i2c->i2c_config->slave_addr,
> + cy_i2c->i2c_config->is_msb_first,
> + cy_i2c->i2c_config->is_master,
> + cy_i2c->i2c_config->s_ignore,
> + cy_i2c->i2c_config->clock_stretch,
> + cy_i2c->i2c_config->is_loopback);
> +
> +config_error:
> + return ret;
> +}
> +
> +static int cy_i2c_set_data_config(struct cyusbs23x *cyusbs,
> + struct cyusbs_i2c *cy_i2c, u16 slave_addr,
> + u16 length, u8 command)
> +{
> + int ret;
> + u16 wIndex, wValue, scb_index;
> + u8 ifnum = cyusbs->usb_intf->cur_altsetting->desc.bInterfaceNumber;
> +
> + scb_index = (ifnum & 0x01) << CY_SCB_INDEX_SHIFT;
> + slave_addr = (slave_addr & 0x7F);
> + wValue = scb_index | cy_i2c->is_stop_bit | cy_i2c->is_nak_bit << 1;
> + wValue |= (slave_addr << 8);
> + wIndex = length;
> +
> + dev_dbg(&cy_i2c->i2c_adapter.dev,
> + "%s, cmd=0x%x, val=0x%x, idx=0x%x\n",
> + __func__, command, wValue, wIndex);
> +
> + ret = usb_control_msg(cyusbs->usb_dev,
> + usb_sndctrlpipe(cyusbs->usb_dev, 0),
> + command, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
> + wValue, wIndex, NULL, 0, CY_USBS_CTRL_XFER_TIMEOUT);
> +
> + return ret;
> +}
> +
> +static int cy_i2c_read(struct i2c_adapter *adapter, struct i2c_msg *msgs)
> +{
> + int ret;
> + int actual_read_len = 0;
> + struct cyusbs23x *cyusbs = (struct cyusbs23x *)adapter->algo_data;
> +
> + dev_dbg(&adapter->dev, "%s\n", __func__);
> +
> + ret = usb_bulk_msg(cyusbs->usb_dev,
> + usb_rcvbulkpipe(cyusbs->usb_dev, cyusbs->bulk_in_ep_num),
> + msgs[0].buf,
> + msgs[0].len,
> + &actual_read_len, CY_USBS_BULK_XFER_TIMEOUT);
> + if (ret)
> + dev_err(&adapter->dev,
> + "read %d/%d returned %d\n",
> + actual_read_len, msgs[0].len, ret);
> +
> + ret = cy_i2c_recv_status(adapter);
> +
> + return ret;
> +}
> +
> +static int cy_i2c_write(struct i2c_adapter *adapter, struct i2c_msg *msgs)
> +{
> + int ret;
> + int actual_write_len = 0;
> + struct cyusbs23x *cyusbs = (struct cyusbs23x *)adapter->algo_data;
> +
> + dev_dbg(&adapter->dev, "%s\n", __func__);
> +
> + ret = usb_bulk_msg(cyusbs->usb_dev,
> + usb_sndbulkpipe(cyusbs->usb_dev, cyusbs->bulk_out_ep_num),
> + msgs[0].buf,
> + msgs[0].len,
> + &actual_write_len, CY_USBS_BULK_XFER_TIMEOUT);
> + if (ret)
> + dev_err(&adapter->dev,
> + "write %d/%d returned %d\n",
> + actual_write_len, msgs[0].len, ret);
> +
> + ret = cy_i2c_recv_status(adapter);
> +
> + return ret;
> +}
> +
> +static int cy_i2c_xfer(struct i2c_adapter *adapter,
> + struct i2c_msg *msgs, int num)
> +{
> + int ret = 0;
> + struct cyusbs_i2c *cy_i2c;
> + struct cyusbs23x *cyusbs = (struct cyusbs23x *)adapter->algo_data;
> +
> + dev_dbg(&adapter->dev, "%s\n", __func__);
> +
> + if (num > 1) {
> + dev_err(&adapter->dev, "i2c_msg number is > 1\n");
> + return -EIO;
> + }
> +
> + cy_i2c = to_cyusbs_i2c(adapter);
> +
> + mutex_lock(&cy_i2c->lock);
> + if (msgs[0].flags & I2C_M_RD) {
> + dev_dbg(&adapter->dev,
> + "I2C read requested for addr 0x%02x, data length %d\n",
> + msgs[0].addr, msgs[0].len);
> +
> + ret = cy_i2c_set_data_config(cyusbs, cy_i2c, msgs[0].addr,
> + msgs[0].len, CY_I2C_READ_CMD);
> + if (ret < 0) {
> + dev_err(&adapter->dev,
> + "Set Config (read) failed with %d\n", ret);
> + goto io_error;
> + }
> +
> + ret = cy_i2c_read(adapter, msgs);
> + if (ret) {
> + dev_err(&adapter->dev,
> + "Read failed with error code %d\n", ret);
> + goto io_error;
> + }
> + } else {
> + dev_dbg(&adapter->dev,
> + "I2C write requested for addr 0x%02x, data length %d\n",
> + msgs[0].addr, msgs[0].len);
> +
> + ret = cy_i2c_set_data_config(cyusbs, cy_i2c, msgs[0].addr,
> + msgs[0].len, CY_I2C_WRITE_CMD);
> + if (ret < 0) {
> + dev_err(&adapter->dev,
> + "Set Config (write) failed with %d\n", ret);
> + goto io_error;
> + }
> +
> + ret = cy_i2c_write(adapter, msgs);
> + if (ret) {
> + dev_err(&adapter->dev,
> + "Write failed with error code %d\n", ret);
> + goto io_error;
> + }
> + }
> + mutex_unlock(&cy_i2c->lock);
> + return ret;
> +
> +io_error:
> + mutex_unlock(&cy_i2c->lock);
> + return ret;
> +}
> +
> +static u32 cy_i2c_func(struct i2c_adapter *adapter)
> +{
> + return I2C_FUNC_I2C;
> +}
> +
> +static const struct i2c_algorithm i2c_cyusbs23x_algorithm = {
> + .master_xfer = cy_i2c_xfer,
> + .functionality = cy_i2c_func,
> +};
> +
> +static int cyusbs23x_i2c_probe(struct platform_device *pdev)
> +{
> + struct cyusbs23x *cyusbs;
> + struct cyusbs_i2c *cy_i2c;
> + int ret = 0;
> +
> + dev_dbg(&pdev->dev, "%s\n", __func__);
> +
> + cyusbs = dev_get_drvdata(pdev->dev.parent);
> +
> + cy_i2c = devm_kzalloc(&pdev->dev, sizeof(*cy_i2c), GFP_KERNEL);
> + if (cy_i2c == NULL)
> + return -ENOMEM;
> +

dto...

> + cy_i2c->i2c_config = devm_kzalloc(&pdev->dev,
> + sizeof(struct cyusbs_i2c_config),
> + GFP_KERNEL);
> + if (cy_i2c->i2c_config == NULL)
> + return -ENOMEM;
> +

dto...

--
Thanks and Regards,
Varka Bhadram.

2014-12-15 10:07:41

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v5 1/3] mfd: add support for Cypress CYUSBS234 USB Serial Bridge controller

On Sat, Dec 13, 2014 at 05:17:42PM +0530, Muthu Mani wrote:
> Adds support for USB-I2C/GPIO interfaces of Cypress Semiconductor
> CYUSBS234 USB-Serial Bridge controller.
>
> Details about the device can be found at:
> http://www.cypress.com/?rID=84126
>
> Separate cell drivers are available for I2C and GPIO. SPI and UART
> are not supported yet.
>
> Signed-off-by: Muthu Mani <[email protected]>
> Signed-off-by: Rajaram Regupathy <[email protected]>
> ---

The patches in this series are still white-space damaged.

Try sending it to yourself before reposting to the mailings lists again.

And as I already asked you, make sure to remove your corporate
confidentiality footers from mails you post to the lists.

Johan