/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define EPM_ADC_DRIVER_NAME "epm_adc" #define EPM_ADC_MAX_FNAME 20 #define EPM_ADC_CONVERSION_DELAY 100 /* milliseconds */ #define EPM_ADC_SPI_BITS_PER_WORD 8 #define GPIO_EPM_GLOBAL_ENABLE 86 #define GPIO_EPM_MARKER1 96 #define GPIO_EPM_MARKER2 85 #define EPM_ADC_CONVERSION_TIME_MIN 50000 #define EPM_ADC_CONVERSION_TIME_MAX 51000 /* PSoc Commands */ #define EPM_PSOC_GLOBAL_ENABLE 81 #define EPM_PSOC_VREF_VOLTAGE 2048 #define EPM_PSOC_MAX_ADC_CODE_15_BIT 32767 #define EPM_PSOC_MAX_ADC_CODE_12_BIT 4096 #define EPM_GLOBAL_ENABLE_MIN_DELAY 5000 #define EPM_GLOBAL_ENABLE_MAX_DELAY 5100 struct epm_adc_drv { struct platform_device *pdev; struct device *hwmon; struct spi_device *epm_spi_client; struct mutex conv_lock; uint32_t bus_id; struct miscdevice misc; uint32_t channel_mask; uint32_t epm_global_en_gpio; struct epm_chan_properties epm_psoc_ch_prop[0]; }; static struct epm_adc_drv *epm_adc_drv; static int epm_adc_psoc_gpio_init(struct epm_adc_drv *epm_adc, bool enable) { int rc = 0; if (enable) { rc = gpio_request(epm_adc->epm_global_en_gpio, "EPM_PSOC_GLOBAL_EN"); if (!rc) { gpio_direction_output(epm_adc->epm_global_en_gpio, 1); } else { pr_err("%s: Configure EPM_GLOBAL_EN Failed\n", __func__); return rc; } } else { gpio_direction_output(epm_adc->epm_global_en_gpio, 0); gpio_free(epm_adc->epm_global_en_gpio); } return 0; } static int epm_request_marker1(void) { int rc = 0; rc = gpio_request(GPIO_EPM_MARKER1, "EPM_MARKER1"); if (!rc) { gpio_direction_output(GPIO_EPM_MARKER1, 1); } else { pr_err("%s: Configure MARKER1 GPIO Failed\n", __func__); return rc; } return 0; } static int epm_set_marker1(struct epm_marker_level *marker_init) { gpio_set_value(GPIO_EPM_MARKER1, marker_init->level); return 0; } static int epm_request_marker2(void) { int rc = 0; rc = gpio_request(GPIO_EPM_MARKER2, "EPM_MARKER2"); if (!rc) { gpio_direction_output(GPIO_EPM_MARKER2, 1); } else { pr_err("%s: Configure MARKER2 GPIO Failed\n", __func__); return rc; } return 0; } static int epm_set_marker2(struct epm_marker_level *marker_init) { gpio_set_value(GPIO_EPM_MARKER2, marker_init->level); return 0; } static int epm_marker1_release(void) { gpio_free(GPIO_EPM_MARKER1); return 0; } static int epm_marker2_release(void) { gpio_free(GPIO_EPM_MARKER2); return 0; } static int epm_psoc_generic_request(struct epm_adc_drv *epm_adc, struct epm_generic_request *psoc_get_data) { struct spi_message m; struct spi_transfer t; char tx_buf[64], rx_buf[64]; int rc = 0, data_loop = 0; spi_setup(epm_adc->epm_spi_client); memset(&t, 0, sizeof(t)); memset(tx_buf, 0, sizeof(tx_buf)); memset(rx_buf, 0, sizeof(tx_buf)); t.tx_buf = tx_buf; t.rx_buf = rx_buf; spi_message_init(&m); spi_message_add_tail(&t, &m); for (data_loop = 0; data_loop < 64; data_loop++) tx_buf[data_loop] = psoc_get_data->buf[data_loop]; t.len = sizeof(tx_buf); t.bits_per_word = EPM_ADC_SPI_BITS_PER_WORD; rc = spi_sync(epm_adc->epm_spi_client, &m); if (rc) return rc; for (data_loop = 0; data_loop < 64; data_loop++) psoc_get_data->buf[data_loop] = rx_buf[data_loop]; return rc; } static long epm_adc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct epm_adc_drv *epm_adc = epm_adc_drv; switch (cmd) { case EPM_MARKER1_REQUEST: { uint32_t result; result = epm_request_marker1(); if (copy_to_user((void __user *)arg, &result, sizeof(uint32_t))) return -EFAULT; break; } case EPM_MARKER2_REQUEST: { uint32_t result; result = epm_request_marker2(); if (copy_to_user((void __user *)arg, &result, sizeof(uint32_t))) return -EFAULT; break; } case EPM_MARKER1_SET_LEVEL: { struct epm_marker_level marker_init; uint32_t result; if (copy_from_user(&marker_init, (void __user *)arg, sizeof(struct epm_marker_level))) return -EFAULT; result = epm_set_marker1(&marker_init); if (copy_to_user((void __user *)arg, &result, sizeof(uint32_t))) return -EFAULT; break; } case EPM_MARKER2_SET_LEVEL: { struct epm_marker_level marker_init; uint32_t result; if (copy_from_user(&marker_init, (void __user *)arg, sizeof(struct epm_marker_level))) return -EFAULT; result = epm_set_marker2(&marker_init); if (copy_to_user((void __user *)arg, &result, sizeof(uint32_t))) return -EFAULT; break; } case EPM_MARKER1_RELEASE: { uint32_t result; result = epm_marker1_release(); if (copy_to_user((void __user *)arg, &result, sizeof(uint32_t))) return -EFAULT; break; } case EPM_MARKER2_RELEASE: { uint32_t result; result = epm_marker2_release(); if (copy_to_user((void __user *)arg, &result, sizeof(uint32_t))) return -EFAULT; break; } case EPM_PSOC_ADC_INIT: { int rc; rc = epm_adc_psoc_gpio_init(epm_adc, true); if (rc) pr_err("GPIO init failed with %d\n", rc); if (copy_to_user((void __user *)arg, &rc, sizeof(int))) return -EFAULT; break; } case EPM_PSOC_ADC_DEINIT: { int rc; rc = epm_adc_psoc_gpio_init(epm_adc, false); if (copy_to_user((void __user *)arg, &rc, sizeof(int))) return -EFAULT; break; } case EPM_PSOC_GENERIC_REQUEST: { struct epm_generic_request psoc_get_data; int rc; if (copy_from_user(&psoc_get_data, (void __user *)arg, sizeof(struct epm_generic_request))) return -EFAULT; rc = epm_psoc_generic_request(epm_adc, &psoc_get_data); if (rc) pr_err("Generic request failed\n"); if (copy_to_user((void __user *)arg, &psoc_get_data, sizeof(struct epm_generic_request))) return -EFAULT; break; } default: return -EINVAL; } return 0; } #ifdef CONFIG_COMPAT static long epm_adc_compat_ioctl_process(struct file *filep, unsigned int cmd, unsigned long arg) { arg = (unsigned long)compat_ptr(arg); return epm_adc_ioctl(filep, cmd, arg); } #endif /* CONFIG_COMPAT */ const struct file_operations epm_adc_fops = { .unlocked_ioctl = epm_adc_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = epm_adc_compat_ioctl_process, #endif /* CONFIG_COMPAT */ }; static int get_device_tree_data(struct spi_device *spi) { const struct device_node *node = spi->dev.of_node; struct epm_adc_drv *epm_adc; u32 *epm_ch_gain, *epm_ch_rsense; u32 rc = 0, epm_num_channels, i, channel_mask, epm_gpio_num; if (!node) return -EINVAL; rc = of_property_read_u32(node, "qcom,channels", &epm_num_channels); if (rc) { dev_err(&spi->dev, "missing channel numbers\n"); return -ENODEV; } epm_ch_gain = devm_kzalloc(&spi->dev, epm_num_channels * sizeof(u32), GFP_KERNEL); if (!epm_ch_gain) { dev_err(&spi->dev, "cannot allocate gain\n"); return -ENOMEM; } epm_ch_rsense = devm_kzalloc(&spi->dev, epm_num_channels * sizeof(u32), GFP_KERNEL); if (!epm_ch_rsense) { dev_err(&spi->dev, "cannot allocate rsense\n"); return -ENOMEM; } rc = of_property_read_u32_array(node, "qcom,gain", epm_ch_gain, epm_num_channels); if (rc) { dev_err(&spi->dev, "invalid gain property:%d\n", rc); return rc; } rc = of_property_read_u32_array(node, "qcom,rsense", epm_ch_rsense, epm_num_channels); if (rc) { dev_err(&spi->dev, "invalid rsense property:%d\n", rc); return rc; } rc = of_property_read_u32(node, "qcom,channel-type", &channel_mask); if (rc) { dev_err(&spi->dev, "missing channel mask\n"); return -ENODEV; } epm_gpio_num = of_get_named_gpio(spi->dev.of_node, "qcom,epm-enable-gpio", 0); if (epm_gpio_num < 0) { dev_err(&spi->dev, "missing global en gpio num\n"); return -ENODEV; } epm_adc = devm_kzalloc(&spi->dev, sizeof(struct epm_adc_drv) + (epm_num_channels * sizeof(struct epm_chan_properties)), GFP_KERNEL); if (!epm_adc) { dev_err(&spi->dev, "Unable to allocate memory\n"); return -ENOMEM; } for (i = 0; i < epm_num_channels; i++) { epm_adc->epm_psoc_ch_prop[i].resistorvalue = epm_ch_rsense[i]; epm_adc->epm_psoc_ch_prop[i].gain = epm_ch_gain[i]; } epm_adc->channel_mask = channel_mask; epm_adc->epm_global_en_gpio = epm_gpio_num; epm_adc_drv = epm_adc; return 0; } static int epm_adc_psoc_spi_probe(struct spi_device *spi) { struct epm_adc_drv *epm_adc; struct device_node *node = spi->dev.of_node; int rc = 0; if (node) { rc = get_device_tree_data(spi); if (rc) return rc; } else { epm_adc = epm_adc_drv; epm_adc_drv->epm_spi_client = spi; epm_adc_drv->epm_spi_client->bits_per_word = EPM_ADC_SPI_BITS_PER_WORD; return rc; } epm_adc = epm_adc_drv; epm_adc->misc.name = EPM_ADC_DRIVER_NAME; epm_adc->misc.minor = MISC_DYNAMIC_MINOR; if (node) { epm_adc->misc.fops = &epm_adc_fops; if (misc_register(&epm_adc->misc)) { pr_err("Unable to register misc device!\n"); return -EFAULT; } } epm_adc_drv->epm_spi_client = spi; epm_adc_drv->epm_spi_client->bits_per_word = EPM_ADC_SPI_BITS_PER_WORD; epm_adc->hwmon = hwmon_device_register(&spi->dev); if (IS_ERR(epm_adc->hwmon)) { dev_err(&spi->dev, "hwmon_device_register failed\n"); return rc; } mutex_init(&epm_adc->conv_lock); return rc; } static int epm_adc_psoc_spi_remove(struct spi_device *spi) { epm_adc_drv->epm_spi_client = NULL; return 0; } static const struct of_device_id epm_adc_psoc_match_table[] = { { .compatible = "cy,epm-adc-cy8c5568lti-114", }, {} }; static struct spi_driver epm_spi_driver = { .probe = epm_adc_psoc_spi_probe, .remove = epm_adc_psoc_spi_remove, .driver = { .name = EPM_ADC_DRIVER_NAME, .of_match_table = epm_adc_psoc_match_table, }, }; static int __init epm_adc_init(void) { int ret = 0; ret = spi_register_driver(&epm_spi_driver); if (ret) pr_err("%s: spi register failed: rc=%d\n", __func__, ret); return ret; } static void __exit epm_adc_exit(void) { spi_unregister_driver(&epm_spi_driver); } module_init(epm_adc_init); module_exit(epm_adc_exit); MODULE_DESCRIPTION("EPM ADC Driver"); MODULE_ALIAS("platform:epm_adc"); MODULE_LICENSE("GPL v2");