18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Freescale Vybrid vf610 DAC driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2016 Toradex AG 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 198c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define VF610_DACx_STATCTRL 0x20 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define VF610_DAC_DACEN BIT(15) 248c2ecf20Sopenharmony_ci#define VF610_DAC_DACRFS BIT(14) 258c2ecf20Sopenharmony_ci#define VF610_DAC_LPEN BIT(11) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define VF610_DAC_DAT0(x) ((x) & 0xFFF) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cienum vf610_conversion_mode_sel { 308c2ecf20Sopenharmony_ci VF610_DAC_CONV_HIGH_POWER, 318c2ecf20Sopenharmony_ci VF610_DAC_CONV_LOW_POWER, 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct vf610_dac { 358c2ecf20Sopenharmony_ci struct clk *clk; 368c2ecf20Sopenharmony_ci struct device *dev; 378c2ecf20Sopenharmony_ci enum vf610_conversion_mode_sel conv_mode; 388c2ecf20Sopenharmony_ci void __iomem *regs; 398c2ecf20Sopenharmony_ci struct mutex lock; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void vf610_dac_init(struct vf610_dac *info) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci int val; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci info->conv_mode = VF610_DAC_CONV_LOW_POWER; 478c2ecf20Sopenharmony_ci val = VF610_DAC_DACEN | VF610_DAC_DACRFS | 488c2ecf20Sopenharmony_ci VF610_DAC_LPEN; 498c2ecf20Sopenharmony_ci writel(val, info->regs + VF610_DACx_STATCTRL); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void vf610_dac_exit(struct vf610_dac *info) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci int val; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci val = readl(info->regs + VF610_DACx_STATCTRL); 578c2ecf20Sopenharmony_ci val &= ~VF610_DAC_DACEN; 588c2ecf20Sopenharmony_ci writel(val, info->regs + VF610_DACx_STATCTRL); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic int vf610_set_conversion_mode(struct iio_dev *indio_dev, 628c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 638c2ecf20Sopenharmony_ci unsigned int mode) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct vf610_dac *info = iio_priv(indio_dev); 668c2ecf20Sopenharmony_ci int val; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 698c2ecf20Sopenharmony_ci info->conv_mode = mode; 708c2ecf20Sopenharmony_ci val = readl(info->regs + VF610_DACx_STATCTRL); 718c2ecf20Sopenharmony_ci if (mode) 728c2ecf20Sopenharmony_ci val |= VF610_DAC_LPEN; 738c2ecf20Sopenharmony_ci else 748c2ecf20Sopenharmony_ci val &= ~VF610_DAC_LPEN; 758c2ecf20Sopenharmony_ci writel(val, info->regs + VF610_DACx_STATCTRL); 768c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return 0; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int vf610_get_conversion_mode(struct iio_dev *indio_dev, 828c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct vf610_dac *info = iio_priv(indio_dev); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return info->conv_mode; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic const char * const vf610_conv_modes[] = { "high-power", "low-power" }; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic const struct iio_enum vf610_conversion_mode = { 928c2ecf20Sopenharmony_ci .items = vf610_conv_modes, 938c2ecf20Sopenharmony_ci .num_items = ARRAY_SIZE(vf610_conv_modes), 948c2ecf20Sopenharmony_ci .get = vf610_get_conversion_mode, 958c2ecf20Sopenharmony_ci .set = vf610_set_conversion_mode, 968c2ecf20Sopenharmony_ci}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic const struct iio_chan_spec_ext_info vf610_ext_info[] = { 998c2ecf20Sopenharmony_ci IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR, 1008c2ecf20Sopenharmony_ci &vf610_conversion_mode), 1018c2ecf20Sopenharmony_ci {}, 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define VF610_DAC_CHAN(_chan_type) { \ 1058c2ecf20Sopenharmony_ci .type = (_chan_type), \ 1068c2ecf20Sopenharmony_ci .output = 1, \ 1078c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 1088c2ecf20Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ 1098c2ecf20Sopenharmony_ci .ext_info = vf610_ext_info, \ 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic const struct iio_chan_spec vf610_dac_iio_channels[] = { 1138c2ecf20Sopenharmony_ci VF610_DAC_CHAN(IIO_VOLTAGE), 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int vf610_read_raw(struct iio_dev *indio_dev, 1178c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 1188c2ecf20Sopenharmony_ci int *val, int *val2, 1198c2ecf20Sopenharmony_ci long mask) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct vf610_dac *info = iio_priv(indio_dev); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci switch (mask) { 1248c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 1258c2ecf20Sopenharmony_ci *val = VF610_DAC_DAT0(readl(info->regs)); 1268c2ecf20Sopenharmony_ci return IIO_VAL_INT; 1278c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 1288c2ecf20Sopenharmony_ci /* 1298c2ecf20Sopenharmony_ci * DACRFS is always 1 for valid reference and typical 1308c2ecf20Sopenharmony_ci * reference voltage as per Vybrid datasheet is 3.3V 1318c2ecf20Sopenharmony_ci * from section 9.1.2.1 of Vybrid datasheet 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci *val = 3300 /* mV */; 1348c2ecf20Sopenharmony_ci *val2 = 12; 1358c2ecf20Sopenharmony_ci return IIO_VAL_FRACTIONAL_LOG2; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci default: 1388c2ecf20Sopenharmony_ci return -EINVAL; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int vf610_write_raw(struct iio_dev *indio_dev, 1438c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 1448c2ecf20Sopenharmony_ci int val, int val2, 1458c2ecf20Sopenharmony_ci long mask) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct vf610_dac *info = iio_priv(indio_dev); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci switch (mask) { 1508c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 1518c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 1528c2ecf20Sopenharmony_ci writel(VF610_DAC_DAT0(val), info->regs); 1538c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci default: 1578c2ecf20Sopenharmony_ci return -EINVAL; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic const struct iio_info vf610_dac_iio_info = { 1628c2ecf20Sopenharmony_ci .read_raw = &vf610_read_raw, 1638c2ecf20Sopenharmony_ci .write_raw = &vf610_write_raw, 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic const struct of_device_id vf610_dac_match[] = { 1678c2ecf20Sopenharmony_ci { .compatible = "fsl,vf610-dac", }, 1688c2ecf20Sopenharmony_ci { /* sentinel */ } 1698c2ecf20Sopenharmony_ci}; 1708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, vf610_dac_match); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int vf610_dac_probe(struct platform_device *pdev) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 1758c2ecf20Sopenharmony_ci struct vf610_dac *info; 1768c2ecf20Sopenharmony_ci int ret; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&pdev->dev, 1798c2ecf20Sopenharmony_ci sizeof(struct vf610_dac)); 1808c2ecf20Sopenharmony_ci if (!indio_dev) { 1818c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed allocating iio device\n"); 1828c2ecf20Sopenharmony_ci return -ENOMEM; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci info = iio_priv(indio_dev); 1868c2ecf20Sopenharmony_ci info->dev = &pdev->dev; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci info->regs = devm_platform_ioremap_resource(pdev, 0); 1898c2ecf20Sopenharmony_ci if (IS_ERR(info->regs)) 1908c2ecf20Sopenharmony_ci return PTR_ERR(info->regs); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci info->clk = devm_clk_get(&pdev->dev, "dac"); 1938c2ecf20Sopenharmony_ci if (IS_ERR(info->clk)) { 1948c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed getting clock, err = %ld\n", 1958c2ecf20Sopenharmony_ci PTR_ERR(info->clk)); 1968c2ecf20Sopenharmony_ci return PTR_ERR(info->clk); 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, indio_dev); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci indio_dev->name = dev_name(&pdev->dev); 2028c2ecf20Sopenharmony_ci indio_dev->info = &vf610_dac_iio_info; 2038c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 2048c2ecf20Sopenharmony_ci indio_dev->channels = vf610_dac_iio_channels; 2058c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(vf610_dac_iio_channels); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci mutex_init(&info->lock); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci ret = clk_prepare_enable(info->clk); 2108c2ecf20Sopenharmony_ci if (ret) { 2118c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 2128c2ecf20Sopenharmony_ci "Could not prepare or enable the clock\n"); 2138c2ecf20Sopenharmony_ci return ret; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci vf610_dac_init(info); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci ret = iio_device_register(indio_dev); 2198c2ecf20Sopenharmony_ci if (ret) { 2208c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Couldn't register the device\n"); 2218c2ecf20Sopenharmony_ci goto error_iio_device_register; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return 0; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cierror_iio_device_register: 2278c2ecf20Sopenharmony_ci vf610_dac_exit(info); 2288c2ecf20Sopenharmony_ci clk_disable_unprepare(info->clk); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return ret; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int vf610_dac_remove(struct platform_device *pdev) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = platform_get_drvdata(pdev); 2368c2ecf20Sopenharmony_ci struct vf610_dac *info = iio_priv(indio_dev); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci iio_device_unregister(indio_dev); 2398c2ecf20Sopenharmony_ci vf610_dac_exit(info); 2408c2ecf20Sopenharmony_ci clk_disable_unprepare(info->clk); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 2468c2ecf20Sopenharmony_cistatic int vf610_dac_suspend(struct device *dev) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 2498c2ecf20Sopenharmony_ci struct vf610_dac *info = iio_priv(indio_dev); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci vf610_dac_exit(info); 2528c2ecf20Sopenharmony_ci clk_disable_unprepare(info->clk); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return 0; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int vf610_dac_resume(struct device *dev) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 2608c2ecf20Sopenharmony_ci struct vf610_dac *info = iio_priv(indio_dev); 2618c2ecf20Sopenharmony_ci int ret; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci ret = clk_prepare_enable(info->clk); 2648c2ecf20Sopenharmony_ci if (ret) 2658c2ecf20Sopenharmony_ci return ret; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci vf610_dac_init(info); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci#endif 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(vf610_dac_pm_ops, vf610_dac_suspend, vf610_dac_resume); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic struct platform_driver vf610_dac_driver = { 2768c2ecf20Sopenharmony_ci .probe = vf610_dac_probe, 2778c2ecf20Sopenharmony_ci .remove = vf610_dac_remove, 2788c2ecf20Sopenharmony_ci .driver = { 2798c2ecf20Sopenharmony_ci .name = "vf610-dac", 2808c2ecf20Sopenharmony_ci .of_match_table = vf610_dac_match, 2818c2ecf20Sopenharmony_ci .pm = &vf610_dac_pm_ops, 2828c2ecf20Sopenharmony_ci }, 2838c2ecf20Sopenharmony_ci}; 2848c2ecf20Sopenharmony_cimodule_platform_driver(vf610_dac_driver); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sanchayan Maity <sanchayan.maity@toradex.com>"); 2878c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale VF610 DAC driver"); 2888c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 289