Message ID | 20220921063026.89619-6-matt.ranostay@konsulko.com |
---|---|
State | New |
Headers | show |
Series | HID: mcp2221: iio support and device resource management | expand |
Hi Matt, I love your patch! Yet something to improve: [auto build test ERROR on jic23-iio/togreg] [also build test ERROR on next-20220921] [cannot apply to hid/for-next wsa/i2c/for-next linus/master v6.0-rc6] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Matt-Ranostay/HID-mcp2221-iio-support-and-device-resource-management/20220921-143207 base: https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg config: hexagon-randconfig-r023-20220921 (https://download.01.org/0day-ci/archive/20220922/202209221851.IOfsmA0z-lkp@intel.com/config) compiler: clang version 16.0.0 (https://github.com/llvm/llvm-project 791a7ae1ba3efd6bca96338e10ffde557ba83920) reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/9576b88476cb586e6d9f8ef77969f1acd5e4a241 git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Matt-Ranostay/HID-mcp2221-iio-support-and-device-resource-management/20220921-143207 git checkout 9576b88476cb586e6d9f8ef77969f1acd5e4a241 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=hexagon SHELL=/bin/bash drivers/hid/ If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot <lkp@intel.com> All errors (new ones prefixed by >>): >> drivers/hid/hid-mcp2221.c:879:10: error: no member named 'dac_scale' in 'struct mcp2221' mcp->dac_scale = tmp + 4; ~~~ ^ drivers/hid/hid-mcp2221.c:881:10: error: no member named 'dac_scale' in 'struct mcp2221' mcp->dac_scale = 5; ~~~ ^ >> drivers/hid/hid-mcp2221.c:886:10: error: no member named 'adc_scale' in 'struct mcp2221' mcp->adc_scale = tmp - 1; ~~~ ^ drivers/hid/hid-mcp2221.c:888:10: error: no member named 'adc_scale' in 'struct mcp2221' mcp->adc_scale = 0; ~~~ ^ 4 errors generated. vim +879 drivers/hid/hid-mcp2221.c 720 721 /* 722 * MCP2221 uses interrupt endpoint for input reports. This function 723 * is called by HID layer when it receives i/p report from mcp2221, 724 * which is actually a response to the previously sent command. 725 * 726 * MCP2221A firmware specific return codes are parsed and 0 or 727 * appropriate negative error code is returned. Delayed response 728 * results in timeout error and stray reponses results in -EIO. 729 */ 730 static int mcp2221_raw_event(struct hid_device *hdev, 731 struct hid_report *report, u8 *data, int size) 732 { 733 u8 *buf, tmp; 734 struct mcp2221 *mcp = hid_get_drvdata(hdev); 735 736 switch (data[0]) { 737 738 case MCP2221_I2C_WR_DATA: 739 case MCP2221_I2C_WR_NO_STOP: 740 case MCP2221_I2C_RD_DATA: 741 case MCP2221_I2C_RD_RPT_START: 742 switch (data[1]) { 743 case MCP2221_SUCCESS: 744 mcp->status = 0; 745 break; 746 default: 747 mcp->status = mcp_get_i2c_eng_state(mcp, data, 2); 748 } 749 complete(&mcp->wait_in_report); 750 break; 751 752 case MCP2221_I2C_PARAM_OR_STATUS: 753 switch (data[1]) { 754 case MCP2221_SUCCESS: 755 if ((mcp->txbuf[3] == MCP2221_I2C_SET_SPEED) && 756 (data[3] != MCP2221_I2C_SET_SPEED)) { 757 mcp->status = -EAGAIN; 758 break; 759 } 760 if (data[20] & MCP2221_I2C_MASK_ADDR_NACK) { 761 mcp->status = -ENXIO; 762 break; 763 } 764 mcp->status = mcp_get_i2c_eng_state(mcp, data, 8); 765 #if IS_REACHABLE(CONFIG_IIO) 766 memcpy(&mcp->adc_values, &data[50], sizeof(mcp->adc_values)); 767 #endif 768 break; 769 default: 770 mcp->status = -EIO; 771 } 772 complete(&mcp->wait_in_report); 773 break; 774 775 case MCP2221_I2C_GET_DATA: 776 switch (data[1]) { 777 case MCP2221_SUCCESS: 778 if (data[2] == MCP2221_I2C_ADDR_NACK) { 779 mcp->status = -ENXIO; 780 break; 781 } 782 if (!mcp_get_i2c_eng_state(mcp, data, 2) 783 && (data[3] == 0)) { 784 mcp->status = 0; 785 break; 786 } 787 if (data[3] == 127) { 788 mcp->status = -EIO; 789 break; 790 } 791 if (data[2] == MCP2221_I2C_READ_COMPL) { 792 buf = mcp->rxbuf; 793 memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]); 794 mcp->rxbuf_idx = mcp->rxbuf_idx + data[3]; 795 mcp->status = 0; 796 break; 797 } 798 mcp->status = -EIO; 799 break; 800 default: 801 mcp->status = -EIO; 802 } 803 complete(&mcp->wait_in_report); 804 break; 805 806 case MCP2221_GPIO_GET: 807 switch (data[1]) { 808 case MCP2221_SUCCESS: 809 if ((data[mcp->gp_idx] == MCP2221_ALT_F_NOT_GPIOV) || 810 (data[mcp->gp_idx + 1] == MCP2221_ALT_F_NOT_GPIOD)) { 811 mcp->status = -ENOENT; 812 } else { 813 mcp->status = !!data[mcp->gp_idx]; 814 mcp->gpio_dir = data[mcp->gp_idx + 1]; 815 } 816 break; 817 default: 818 mcp->status = -EAGAIN; 819 } 820 complete(&mcp->wait_in_report); 821 break; 822 823 case MCP2221_GPIO_SET: 824 switch (data[1]) { 825 case MCP2221_SUCCESS: 826 if ((data[mcp->gp_idx] == MCP2221_ALT_F_NOT_GPIOV) || 827 (data[mcp->gp_idx - 1] == MCP2221_ALT_F_NOT_GPIOV)) { 828 mcp->status = -ENOENT; 829 } else { 830 mcp->status = 0; 831 } 832 break; 833 default: 834 mcp->status = -EAGAIN; 835 } 836 complete(&mcp->wait_in_report); 837 break; 838 839 case MCP2221_SET_SRAM_SETTINGS: 840 switch (data[1]) { 841 case MCP2221_SUCCESS: 842 mcp->status = 0; 843 break; 844 default: 845 mcp->status = -EAGAIN; 846 } 847 complete(&mcp->wait_in_report); 848 break; 849 850 case MCP2221_GET_SRAM_SETTINGS: 851 switch (data[1]) { 852 case MCP2221_SUCCESS: 853 memcpy(&mcp->mode, &data[22], 4); 854 #if IS_REACHABLE(CONFIG_IIO) 855 mcp->dac_value = data[6] & GENMASK(4, 0); 856 #endif 857 mcp->status = 0; 858 break; 859 default: 860 mcp->status = -EAGAIN; 861 } 862 complete(&mcp->wait_in_report); 863 break; 864 865 case MCP2221_READ_FLASH_DATA: 866 switch (data[1]) { 867 case MCP2221_SUCCESS: 868 mcp->status = 0; 869 870 /* Only handles CHIP SETTINGS subpage currently */ 871 if (mcp->txbuf[1] != 0) { 872 mcp->status = -EIO; 873 break; 874 } 875 876 /* DAC scale value */ 877 tmp = (data[6] >> 6) & 0x3; 878 if ((data[6] & BIT(5)) && tmp) > 879 mcp->dac_scale = tmp + 4; 880 else 881 mcp->dac_scale = 5; 882 883 /* ADC scale value */ 884 tmp = (data[7] >> 3) & 0x3; 885 if ((data[7] & BIT(2)) && tmp) > 886 mcp->adc_scale = tmp - 1; 887 else 888 mcp->adc_scale = 0; 889 890 break; 891 default: 892 mcp->status = -EAGAIN; 893 } 894 complete(&mcp->wait_in_report); 895 break; 896 897 default: 898 mcp->status = -EIO; 899 complete(&mcp->wait_in_report); 900 } 901 902 return 1; 903 } 904
Hi Matt, I love your patch! Yet something to improve: [auto build test ERROR on jic23-iio/togreg] [also build test ERROR on next-20220921] [cannot apply to hid/for-next wsa/i2c/for-next linus/master v6.0-rc6] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Matt-Ranostay/HID-mcp2221-iio-support-and-device-resource-management/20220921-143207 base: https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg config: sparc-randconfig-r013-20220921 compiler: sparc-linux-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/9576b88476cb586e6d9f8ef77969f1acd5e4a241 git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Matt-Ranostay/HID-mcp2221-iio-support-and-device-resource-management/20220921-143207 git checkout 9576b88476cb586e6d9f8ef77969f1acd5e4a241 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sparc SHELL=/bin/bash drivers/hid/ If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot <lkp@intel.com> All errors (new ones prefixed by >>): drivers/hid/hid-mcp2221.c: In function 'mcp2221_raw_event': >> drivers/hid/hid-mcp2221.c:879:36: error: 'struct mcp2221' has no member named 'dac_scale' 879 | mcp->dac_scale = tmp + 4; | ^~ drivers/hid/hid-mcp2221.c:881:36: error: 'struct mcp2221' has no member named 'dac_scale' 881 | mcp->dac_scale = 5; | ^~ >> drivers/hid/hid-mcp2221.c:886:36: error: 'struct mcp2221' has no member named 'adc_scale' 886 | mcp->adc_scale = tmp - 1; | ^~ drivers/hid/hid-mcp2221.c:888:36: error: 'struct mcp2221' has no member named 'adc_scale' 888 | mcp->adc_scale = 0; | ^~ vim +879 drivers/hid/hid-mcp2221.c 720 721 /* 722 * MCP2221 uses interrupt endpoint for input reports. This function 723 * is called by HID layer when it receives i/p report from mcp2221, 724 * which is actually a response to the previously sent command. 725 * 726 * MCP2221A firmware specific return codes are parsed and 0 or 727 * appropriate negative error code is returned. Delayed response 728 * results in timeout error and stray reponses results in -EIO. 729 */ 730 static int mcp2221_raw_event(struct hid_device *hdev, 731 struct hid_report *report, u8 *data, int size) 732 { 733 u8 *buf, tmp; 734 struct mcp2221 *mcp = hid_get_drvdata(hdev); 735 736 switch (data[0]) { 737 738 case MCP2221_I2C_WR_DATA: 739 case MCP2221_I2C_WR_NO_STOP: 740 case MCP2221_I2C_RD_DATA: 741 case MCP2221_I2C_RD_RPT_START: 742 switch (data[1]) { 743 case MCP2221_SUCCESS: 744 mcp->status = 0; 745 break; 746 default: 747 mcp->status = mcp_get_i2c_eng_state(mcp, data, 2); 748 } 749 complete(&mcp->wait_in_report); 750 break; 751 752 case MCP2221_I2C_PARAM_OR_STATUS: 753 switch (data[1]) { 754 case MCP2221_SUCCESS: 755 if ((mcp->txbuf[3] == MCP2221_I2C_SET_SPEED) && 756 (data[3] != MCP2221_I2C_SET_SPEED)) { 757 mcp->status = -EAGAIN; 758 break; 759 } 760 if (data[20] & MCP2221_I2C_MASK_ADDR_NACK) { 761 mcp->status = -ENXIO; 762 break; 763 } 764 mcp->status = mcp_get_i2c_eng_state(mcp, data, 8); 765 #if IS_REACHABLE(CONFIG_IIO) 766 memcpy(&mcp->adc_values, &data[50], sizeof(mcp->adc_values)); 767 #endif 768 break; 769 default: 770 mcp->status = -EIO; 771 } 772 complete(&mcp->wait_in_report); 773 break; 774 775 case MCP2221_I2C_GET_DATA: 776 switch (data[1]) { 777 case MCP2221_SUCCESS: 778 if (data[2] == MCP2221_I2C_ADDR_NACK) { 779 mcp->status = -ENXIO; 780 break; 781 } 782 if (!mcp_get_i2c_eng_state(mcp, data, 2) 783 && (data[3] == 0)) { 784 mcp->status = 0; 785 break; 786 } 787 if (data[3] == 127) { 788 mcp->status = -EIO; 789 break; 790 } 791 if (data[2] == MCP2221_I2C_READ_COMPL) { 792 buf = mcp->rxbuf; 793 memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]); 794 mcp->rxbuf_idx = mcp->rxbuf_idx + data[3]; 795 mcp->status = 0; 796 break; 797 } 798 mcp->status = -EIO; 799 break; 800 default: 801 mcp->status = -EIO; 802 } 803 complete(&mcp->wait_in_report); 804 break; 805 806 case MCP2221_GPIO_GET: 807 switch (data[1]) { 808 case MCP2221_SUCCESS: 809 if ((data[mcp->gp_idx] == MCP2221_ALT_F_NOT_GPIOV) || 810 (data[mcp->gp_idx + 1] == MCP2221_ALT_F_NOT_GPIOD)) { 811 mcp->status = -ENOENT; 812 } else { 813 mcp->status = !!data[mcp->gp_idx]; 814 mcp->gpio_dir = data[mcp->gp_idx + 1]; 815 } 816 break; 817 default: 818 mcp->status = -EAGAIN; 819 } 820 complete(&mcp->wait_in_report); 821 break; 822 823 case MCP2221_GPIO_SET: 824 switch (data[1]) { 825 case MCP2221_SUCCESS: 826 if ((data[mcp->gp_idx] == MCP2221_ALT_F_NOT_GPIOV) || 827 (data[mcp->gp_idx - 1] == MCP2221_ALT_F_NOT_GPIOV)) { 828 mcp->status = -ENOENT; 829 } else { 830 mcp->status = 0; 831 } 832 break; 833 default: 834 mcp->status = -EAGAIN; 835 } 836 complete(&mcp->wait_in_report); 837 break; 838 839 case MCP2221_SET_SRAM_SETTINGS: 840 switch (data[1]) { 841 case MCP2221_SUCCESS: 842 mcp->status = 0; 843 break; 844 default: 845 mcp->status = -EAGAIN; 846 } 847 complete(&mcp->wait_in_report); 848 break; 849 850 case MCP2221_GET_SRAM_SETTINGS: 851 switch (data[1]) { 852 case MCP2221_SUCCESS: 853 memcpy(&mcp->mode, &data[22], 4); 854 #if IS_REACHABLE(CONFIG_IIO) 855 mcp->dac_value = data[6] & GENMASK(4, 0); 856 #endif 857 mcp->status = 0; 858 break; 859 default: 860 mcp->status = -EAGAIN; 861 } 862 complete(&mcp->wait_in_report); 863 break; 864 865 case MCP2221_READ_FLASH_DATA: 866 switch (data[1]) { 867 case MCP2221_SUCCESS: 868 mcp->status = 0; 869 870 /* Only handles CHIP SETTINGS subpage currently */ 871 if (mcp->txbuf[1] != 0) { 872 mcp->status = -EIO; 873 break; 874 } 875 876 /* DAC scale value */ 877 tmp = (data[6] >> 6) & 0x3; 878 if ((data[6] & BIT(5)) && tmp) > 879 mcp->dac_scale = tmp + 4; 880 else 881 mcp->dac_scale = 5; 882 883 /* ADC scale value */ 884 tmp = (data[7] >> 3) & 0x3; 885 if ((data[7] & BIT(2)) && tmp) > 886 mcp->adc_scale = tmp - 1; 887 else 888 mcp->adc_scale = 0; 889 890 break; 891 default: 892 mcp->status = -EAGAIN; 893 } 894 complete(&mcp->wait_in_report); 895 break; 896 897 default: 898 mcp->status = -EIO; 899 complete(&mcp->wait_in_report); 900 } 901 902 return 1; 903 } 904
On Tue, 20 Sep 2022 23:30:26 -0700 Matt Ranostay <matt.ranostay@konsulko.com> wrote: > Add support for 3x 10-bit ADC and 1x DAC channels registered via the iio > subsystem. > > To prevent breakage and unexpected dependencies this support only is > only built if CONFIG_IIO is enabled, and is only weakly referenced by > 'imply IIO' within the respective Kconfig. > > Additionally the iio device only gets registered if at least one channel > is enabled in the power-on configuration read from SRAM. > > Signed-off-by: Matt Ranostay <matt.ranostay@konsulko.com> > --- A few comments inline. > + > + 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; > + } > + > + /* DAC scale value */ > + tmp = (data[6] >> 6) & 0x3; Perhaps use FIELD_GET() and a suitably defined mask? > + if ((data[6] & BIT(5)) && tmp) > + mcp->dac_scale = tmp + 4; > + else > + mcp->dac_scale = 5; > + > + /* ADC scale value */ > + tmp = (data[7] >> 3) & 0x3; > + if ((data[7] & BIT(2)) && tmp) > + mcp->adc_scale = tmp - 1; > + else > + mcp->adc_scale = 0; > + > + break; > + default: > + mcp->status = -EAGAIN; > + } > + complete(&mcp->wait_in_report); > + break; > + > ... > + > +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; > + 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); > + No blank line between a call and it's error handlers. Better to visually group them together. > + 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); If you aren't going full devm, I'd keep this as an iio_device_alloc() and release it by hand in remove. > + > +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); > + > + /* Device is not ready to read SRAM or FLASH data, try again */ > + schedule_delayed_work(&mcp->init_work, msecs_to_jiffies(100)); Add a count. If we end up here lots of times, probably want to give up! > +} > +#endif > + > static int mcp2221_probe(struct hid_device *hdev, > const struct hid_device_id *id) > { > @@ -913,6 +1158,11 @@ static int mcp2221_probe(struct hid_device *hdev, > if (ret) > return ret; > > +#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 > + > return 0; > } >
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 863d1f96ea57..cdae312f4795 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1228,6 +1228,7 @@ config HID_MCP2221 tristate "Microchip MCP2221 HID USB-to-I2C/SMbus host support" depends on USB_HID && I2C depends on GPIOLIB + imply IIO help Provides I2C and SMBUS host adapter functionality over USB-HID through MCP2221 device. diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c index 7ba63bcd66de..2a7783b607af 100644 --- a/drivers/hid/hid-mcp2221.c +++ b/drivers/hid/hid-mcp2221.c @@ -16,6 +16,7 @@ #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 +31,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 +93,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 +102,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; }; /* @@ -713,7 +730,7 @@ static int mcp_get_i2c_eng_state(struct mcp2221 *mcp, static int mcp2221_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { - u8 *buf; + u8 *buf, tmp; struct mcp2221 *mcp = hid_get_drvdata(hdev); switch (data[0]) { @@ -745,6 +762,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 +836,64 @@ 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; + } + + /* DAC scale value */ + tmp = (data[6] >> 6) & 0x3; + if ((data[6] & BIT(5)) && tmp) + mcp->dac_scale = tmp + 4; + else + mcp->dac_scale = 5; + + /* ADC scale value */ + tmp = (data[7] >> 3) & 0x3; + if ((data[7] & BIT(2)) && tmp) + mcp->adc_scale = tmp - 1; + else + mcp->adc_scale = 0; + + break; + default: + mcp->status = -EAGAIN; + } + complete(&mcp->wait_in_report); + break; + default: mcp->status = -EIO; complete(&mcp->wait_in_report); @@ -832,6 +910,173 @@ static void mcp2221_hid_remove(void *ptr) hid_hw_stop(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(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; + 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); + + /* 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) { @@ -913,6 +1158,11 @@ static int mcp2221_probe(struct hid_device *hdev, if (ret) return ret; +#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 + return 0; }
Add support for 3x 10-bit ADC and 1x DAC channels registered via the iio subsystem. To prevent breakage and unexpected dependencies this support only is only built if CONFIG_IIO is enabled, and is only weakly referenced by 'imply IIO' within the respective Kconfig. Additionally the iio device only gets registered if at least one channel is enabled in the power-on configuration read from SRAM. Signed-off-by: Matt Ranostay <matt.ranostay@konsulko.com> --- drivers/hid/Kconfig | 1 + drivers/hid/hid-mcp2221.c | 252 +++++++++++++++++++++++++++++++++++++- 2 files changed, 252 insertions(+), 1 deletion(-)