HID: mcp2221: correct undefined references when CONFIG_GPIOLIB isn't defined
[linux-block.git] / drivers / hid / hid-mcp2221.c
index de52e9f7bb8cbd26cc7ccefec6bcf3128f923bfd..5886543b17f347476e3b8c85aea410b5aaec4415 100644 (file)
 #include <linux/module.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
+#include <linux/bitfield.h>
 #include <linux/completion.h>
 #include <linux/delay.h>
 #include <linux/hid.h>
 #include <linux/hidraw.h>
 #include <linux/i2c.h>
 #include <linux/gpio/driver.h>
+#include <linux/iio/iio.h>
 #include "hid-ids.h"
 
 /* Commands codes in a raw output report */
@@ -30,6 +32,9 @@ enum {
        MCP2221_I2C_CANCEL = 0x10,
        MCP2221_GPIO_SET = 0x50,
        MCP2221_GPIO_GET = 0x51,
+       MCP2221_SET_SRAM_SETTINGS = 0x60,
+       MCP2221_GET_SRAM_SETTINGS = 0x61,
+       MCP2221_READ_FLASH_DATA = 0xb0,
 };
 
 /* Response codes in a raw input report */
@@ -89,6 +94,7 @@ struct mcp2221 {
        struct i2c_adapter adapter;
        struct mutex lock;
        struct completion wait_in_report;
+       struct delayed_work init_work;
        u8 *rxbuf;
        u8 txbuf[64];
        int rxbuf_idx;
@@ -97,6 +103,18 @@ struct mcp2221 {
        struct gpio_chip *gc;
        u8 gp_idx;
        u8 gpio_dir;
+       u8 mode[4];
+#if IS_REACHABLE(CONFIG_IIO)
+       struct iio_chan_spec iio_channels[3];
+       u16 adc_values[3];
+       u8 adc_scale;
+       u8 dac_value;
+       u16 dac_scale;
+#endif
+};
+
+struct mcp2221_iio {
+       struct mcp2221 *mcp;
 };
 
 /*
@@ -567,6 +585,7 @@ static const struct i2c_algorithm mcp_i2c_algo = {
        .functionality = mcp_i2c_func,
 };
 
+#if IS_REACHABLE(CONFIG_GPIOLIB)
 static int mcp_gpio_get(struct gpio_chip *gc,
                                unsigned int offset)
 {
@@ -670,6 +689,7 @@ static int mcp_gpio_get_direction(struct gpio_chip *gc,
 
        return GPIO_LINE_DIRECTION_OUT;
 }
+#endif
 
 /* Gives current state of i2c engine inside mcp2221 */
 static int mcp_get_i2c_eng_state(struct mcp2221 *mcp,
@@ -745,6 +765,9 @@ static int mcp2221_raw_event(struct hid_device *hdev,
                                break;
                        }
                        mcp->status = mcp_get_i2c_eng_state(mcp, data, 8);
+#if IS_REACHABLE(CONFIG_IIO)
+                       memcpy(&mcp->adc_values, &data[50], sizeof(mcp->adc_values));
+#endif
                        break;
                default:
                        mcp->status = -EIO;
@@ -816,6 +839,69 @@ static int mcp2221_raw_event(struct hid_device *hdev,
                complete(&mcp->wait_in_report);
                break;
 
+       case MCP2221_SET_SRAM_SETTINGS:
+               switch (data[1]) {
+               case MCP2221_SUCCESS:
+                       mcp->status = 0;
+                       break;
+               default:
+                       mcp->status = -EAGAIN;
+               }
+               complete(&mcp->wait_in_report);
+               break;
+
+       case MCP2221_GET_SRAM_SETTINGS:
+               switch (data[1]) {
+               case MCP2221_SUCCESS:
+                       memcpy(&mcp->mode, &data[22], 4);
+#if IS_REACHABLE(CONFIG_IIO)
+                       mcp->dac_value = data[6] & GENMASK(4, 0);
+#endif
+                       mcp->status = 0;
+                       break;
+               default:
+                       mcp->status = -EAGAIN;
+               }
+               complete(&mcp->wait_in_report);
+               break;
+
+       case MCP2221_READ_FLASH_DATA:
+               switch (data[1]) {
+               case MCP2221_SUCCESS:
+                       mcp->status = 0;
+
+                       /* Only handles CHIP SETTINGS subpage currently */
+                       if (mcp->txbuf[1] != 0) {
+                               mcp->status = -EIO;
+                               break;
+                       }
+
+#if IS_REACHABLE(CONFIG_IIO)
+                       {
+                               u8 tmp;
+                               /* DAC scale value */
+                               tmp = FIELD_GET(GENMASK(7, 6), data[6]);
+                               if ((data[6] & BIT(5)) && tmp)
+                                       mcp->dac_scale = tmp + 4;
+                               else
+                                       mcp->dac_scale = 5;
+
+                               /* ADC scale value */
+                               tmp = FIELD_GET(GENMASK(4, 3), data[7]);
+                               if ((data[7] & BIT(2)) && tmp)
+                                       mcp->adc_scale = tmp - 1;
+                               else
+                                       mcp->adc_scale = 0;
+                       }
+#endif
+
+                       break;
+               default:
+                       mcp->status = -EAGAIN;
+               }
+               complete(&mcp->wait_in_report);
+               break;
+
        default:
                mcp->status = -EIO;
                complete(&mcp->wait_in_report);
@@ -824,6 +910,190 @@ static int mcp2221_raw_event(struct hid_device *hdev,
        return 1;
 }
 
+/* Device resource managed function for HID unregistration */
+static void mcp2221_hid_unregister(void *ptr)
+{
+       struct hid_device *hdev = ptr;
+
+       hid_hw_close(hdev);
+       hid_hw_stop(hdev);
+}
+
+/* This is needed to be sure hid_hw_stop() isn't called twice by the subsystem */
+static void mcp2221_remove(struct hid_device *hdev)
+{
+}
+
+#if IS_REACHABLE(CONFIG_IIO)
+static int mcp2221_read_raw(struct iio_dev *indio_dev,
+                           struct iio_chan_spec const *channel, int *val,
+                           int *val2, long mask)
+{
+       struct mcp2221_iio *priv = iio_priv(indio_dev);
+       struct mcp2221 *mcp = priv->mcp;
+       int ret;
+
+       if (mask == IIO_CHAN_INFO_SCALE) {
+               if (channel->output)
+                       *val = 1 << mcp->dac_scale;
+               else
+                       *val = 1 << mcp->adc_scale;
+
+               return IIO_VAL_INT;
+       }
+
+       mutex_lock(&mcp->lock);
+
+       if (channel->output) {
+               *val = mcp->dac_value;
+               ret = IIO_VAL_INT;
+       } else {
+               /* Read ADC values */
+               ret = mcp_chk_last_cmd_status(mcp);
+
+               if (!ret) {
+                       *val = le16_to_cpu((__force __le16) mcp->adc_values[channel->address]);
+                       if (*val >= BIT(10))
+                               ret =  -EINVAL;
+                       else
+                               ret = IIO_VAL_INT;
+               }
+       }
+
+       mutex_unlock(&mcp->lock);
+
+       return ret;
+}
+
+static int mcp2221_write_raw(struct iio_dev *indio_dev,
+                            struct iio_chan_spec const *chan,
+                            int val, int val2, long mask)
+{
+       struct mcp2221_iio *priv = iio_priv(indio_dev);
+       struct mcp2221 *mcp = priv->mcp;
+       int ret;
+
+       if (val < 0 || val >= BIT(5))
+               return -EINVAL;
+
+       mutex_lock(&mcp->lock);
+
+       memset(mcp->txbuf, 0, 12);
+       mcp->txbuf[0] = MCP2221_SET_SRAM_SETTINGS;
+       mcp->txbuf[4] = BIT(7) | val;
+
+       ret = mcp_send_data_req_status(mcp, mcp->txbuf, 12);
+       if (!ret)
+               mcp->dac_value = val;
+
+       mutex_unlock(&mcp->lock);
+
+       return ret;
+}
+
+static const struct iio_info mcp2221_info = {
+       .read_raw = &mcp2221_read_raw,
+       .write_raw = &mcp2221_write_raw,
+};
+
+static int mcp_iio_channels(struct mcp2221 *mcp)
+{
+       int idx, cnt = 0;
+       bool dac_created = false;
+
+       /* GP0 doesn't have ADC/DAC alternative function */
+       for (idx = 1; idx < MCP_NGPIO; idx++) {
+               struct iio_chan_spec *chan = &mcp->iio_channels[cnt];
+
+               switch (mcp->mode[idx]) {
+               case 2:
+                       chan->address = idx - 1;
+                       chan->channel = cnt++;
+                       break;
+               case 3:
+                       /* GP1 doesn't have DAC alternative function */
+                       if (idx == 1 || dac_created)
+                               continue;
+                       /* DAC1 and DAC2 outputs are connected to the same DAC */
+                       dac_created = true;
+                       chan->output = 1;
+                       cnt++;
+                       break;
+               default:
+                       continue;
+               };
+
+               chan->type = IIO_VOLTAGE;
+               chan->indexed = 1;
+               chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+               chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+               chan->scan_index = -1;
+       }
+
+       return cnt;
+}
+
+static void mcp_init_work(struct work_struct *work)
+{
+       struct iio_dev *indio_dev;
+       struct mcp2221 *mcp = container_of(work, struct mcp2221, init_work.work);
+       struct mcp2221_iio *data;
+       static int retries = 5;
+       int ret, num_channels;
+
+       hid_hw_power(mcp->hdev, PM_HINT_FULLON);
+       mutex_lock(&mcp->lock);
+
+       mcp->txbuf[0] = MCP2221_GET_SRAM_SETTINGS;
+       ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
+
+       if (ret == -EAGAIN)
+               goto reschedule_task;
+
+       num_channels = mcp_iio_channels(mcp);
+       if (!num_channels)
+               goto unlock;
+
+       mcp->txbuf[0] = MCP2221_READ_FLASH_DATA;
+       mcp->txbuf[1] = 0;
+       ret = mcp_send_data_req_status(mcp, mcp->txbuf, 2);
+
+       if (ret == -EAGAIN)
+               goto reschedule_task;
+
+       indio_dev = devm_iio_device_alloc(&mcp->hdev->dev, sizeof(*data));
+       if (!indio_dev)
+               goto unlock;
+
+       data = iio_priv(indio_dev);
+       data->mcp = mcp;
+
+       indio_dev->name = "mcp2221";
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->info = &mcp2221_info;
+       indio_dev->channels = mcp->iio_channels;
+       indio_dev->num_channels = num_channels;
+
+       devm_iio_device_register(&mcp->hdev->dev, indio_dev);
+
+unlock:
+       mutex_unlock(&mcp->lock);
+       hid_hw_power(mcp->hdev, PM_HINT_NORMAL);
+
+       return;
+
+reschedule_task:
+       mutex_unlock(&mcp->lock);
+       hid_hw_power(mcp->hdev, PM_HINT_NORMAL);
+
+       if (!retries--)
+               return;
+
+       /* Device is not ready to read SRAM or FLASH data, try again */
+       schedule_delayed_work(&mcp->init_work, msecs_to_jiffies(100));
+}
+#endif
+
 static int mcp2221_probe(struct hid_device *hdev,
                                        const struct hid_device_id *id)
 {
@@ -849,7 +1119,8 @@ static int mcp2221_probe(struct hid_device *hdev,
        ret = hid_hw_open(hdev);
        if (ret) {
                hid_err(hdev, "can't open device\n");
-               goto err_hstop;
+               hid_hw_stop(hdev);
+               return ret;
        }
 
        mutex_init(&mcp->lock);
@@ -857,6 +1128,10 @@ static int mcp2221_probe(struct hid_device *hdev,
        hid_set_drvdata(hdev, mcp);
        mcp->hdev = hdev;
 
+       ret = devm_add_action_or_reset(&hdev->dev, mcp2221_hid_unregister, hdev);
+       if (ret)
+               return ret;
+
        /* Set I2C bus clock diviser */
        if (i2c_clk_freq > 400)
                i2c_clk_freq = 400;
@@ -873,19 +1148,18 @@ static int mcp2221_probe(struct hid_device *hdev,
                        "MCP2221 usb-i2c bridge on hidraw%d",
                        ((struct hidraw *)hdev->hidraw)->minor);
 
-       ret = i2c_add_adapter(&mcp->adapter);
+       ret = devm_i2c_add_adapter(&hdev->dev, &mcp->adapter);
        if (ret) {
                hid_err(hdev, "can't add usb-i2c adapter: %d\n", ret);
-               goto err_i2c;
+               return ret;
        }
        i2c_set_adapdata(&mcp->adapter, mcp);
 
+#if IS_REACHABLE(CONFIG_GPIOLIB)
        /* Setup GPIO chip */
        mcp->gc = devm_kzalloc(&hdev->dev, sizeof(*mcp->gc), GFP_KERNEL);
-       if (!mcp->gc) {
-               ret = -ENOMEM;
-               goto err_gc;
-       }
+       if (!mcp->gc)
+               return -ENOMEM;
 
        mcp->gc->label = "mcp2221_gpio";
        mcp->gc->direction_input = mcp_gpio_direction_input;
@@ -900,26 +1174,15 @@ static int mcp2221_probe(struct hid_device *hdev,
 
        ret = devm_gpiochip_add_data(&hdev->dev, mcp->gc, mcp);
        if (ret)
-               goto err_gc;
-
-       return 0;
-
-err_gc:
-       i2c_del_adapter(&mcp->adapter);
-err_i2c:
-       hid_hw_close(mcp->hdev);
-err_hstop:
-       hid_hw_stop(mcp->hdev);
-       return ret;
-}
+               return ret;
+#endif
 
-static void mcp2221_remove(struct hid_device *hdev)
-{
-       struct mcp2221 *mcp = hid_get_drvdata(hdev);
+#if IS_REACHABLE(CONFIG_IIO)
+       INIT_DELAYED_WORK(&mcp->init_work, mcp_init_work);
+       schedule_delayed_work(&mcp->init_work, msecs_to_jiffies(100));
+#endif
 
-       i2c_del_adapter(&mcp->adapter);
-       hid_hw_close(mcp->hdev);
-       hid_hw_stop(mcp->hdev);
+       return 0;
 }
 
 static const struct hid_device_id mcp2221_devices[] = {