Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iio: dac: vf610_dac: Add IIO DAC driver for Vybrid SoC

Add driver support for DAC peripheral on Vybrid SoC.

Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>

authored by

Sanchayan Maity and committed by
Jonathan Cameron
1b983bf4 1961bce7

+338
+9
Documentation/ABI/testing/sysfs-bus-iio-vf610
··· 5 5 Specifies the hardware conversion mode used. The three 6 6 available modes are "normal", "high-speed" and "low-power", 7 7 where the last is the default mode. 8 + 9 + 10 + What: /sys/bus/iio/devices/iio:deviceX/out_conversion_mode 11 + KernelVersion: 4.6 12 + Contact: linux-iio@vger.kernel.org 13 + Description: 14 + Specifies the hardware conversion mode used within DAC. 15 + The two available modes are "high-power" and "low-power", 16 + where "low-power" mode is the default mode.
+20
Documentation/devicetree/bindings/iio/dac/vf610-dac.txt
··· 1 + Freescale vf610 Digital to Analog Converter bindings 2 + 3 + The devicetree bindings are for the new DAC driver written for 4 + vf610 SoCs from Freescale. 5 + 6 + Required properties: 7 + - compatible: Should contain "fsl,vf610-dac" 8 + - reg: Offset and length of the register set for the device 9 + - interrupts: Should contain the interrupt for the device 10 + - clocks: The clock is needed by the DAC controller 11 + - clock-names: Must contain "dac" matching entry in the clocks property. 12 + 13 + Example: 14 + dac0: dac@400cc000 { 15 + compatible = "fsl,vf610-dac"; 16 + reg = <0x400cc000 0x1000>; 17 + interrupts = <55 IRQ_TYPE_LEVEL_HIGH>; 18 + clock-names = "dac"; 19 + clocks = <&clks VF610_CLK_DAC0>; 20 + };
+10
drivers/iio/dac/Kconfig
··· 217 217 addresses for the devices may be configured via the "base" module 218 218 parameter array. 219 219 220 + config VF610_DAC 221 + tristate "Vybrid vf610 DAC driver" 222 + depends on OF 223 + depends on HAS_IOMEM 224 + help 225 + Say yes here to support Vybrid board digital-to-analog converter. 226 + 227 + This driver can also be built as a module. If so, the module will 228 + be called vf610_dac. 229 + 220 230 endmenu
+1
drivers/iio/dac/Makefile
··· 23 23 obj-$(CONFIG_MCP4725) += mcp4725.o 24 24 obj-$(CONFIG_MCP4922) += mcp4922.o 25 25 obj-$(CONFIG_STX104) += stx104.o 26 + obj-$(CONFIG_VF610_DAC) += vf610_dac.o
+298
drivers/iio/dac/vf610_dac.c
··· 1 + /* 2 + * Freescale Vybrid vf610 DAC driver 3 + * 4 + * Copyright 2016 Toradex AG 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + */ 16 + 17 + #include <linux/clk.h> 18 + #include <linux/err.h> 19 + #include <linux/interrupt.h> 20 + #include <linux/io.h> 21 + #include <linux/kernel.h> 22 + #include <linux/module.h> 23 + #include <linux/platform_device.h> 24 + #include <linux/regulator/consumer.h> 25 + #include <linux/slab.h> 26 + 27 + #include <linux/iio/iio.h> 28 + #include <linux/iio/sysfs.h> 29 + 30 + #define VF610_DACx_STATCTRL 0x20 31 + 32 + #define VF610_DAC_DACEN BIT(15) 33 + #define VF610_DAC_DACRFS BIT(14) 34 + #define VF610_DAC_LPEN BIT(11) 35 + 36 + #define VF610_DAC_DAT0(x) ((x) & 0xFFF) 37 + 38 + enum vf610_conversion_mode_sel { 39 + VF610_DAC_CONV_HIGH_POWER, 40 + VF610_DAC_CONV_LOW_POWER, 41 + }; 42 + 43 + struct vf610_dac { 44 + struct clk *clk; 45 + struct device *dev; 46 + enum vf610_conversion_mode_sel conv_mode; 47 + void __iomem *regs; 48 + }; 49 + 50 + static void vf610_dac_init(struct vf610_dac *info) 51 + { 52 + int val; 53 + 54 + info->conv_mode = VF610_DAC_CONV_LOW_POWER; 55 + val = VF610_DAC_DACEN | VF610_DAC_DACRFS | 56 + VF610_DAC_LPEN; 57 + writel(val, info->regs + VF610_DACx_STATCTRL); 58 + } 59 + 60 + static void vf610_dac_exit(struct vf610_dac *info) 61 + { 62 + int val; 63 + 64 + val = readl(info->regs + VF610_DACx_STATCTRL); 65 + val &= ~VF610_DAC_DACEN; 66 + writel(val, info->regs + VF610_DACx_STATCTRL); 67 + } 68 + 69 + static int vf610_set_conversion_mode(struct iio_dev *indio_dev, 70 + const struct iio_chan_spec *chan, 71 + unsigned int mode) 72 + { 73 + struct vf610_dac *info = iio_priv(indio_dev); 74 + int val; 75 + 76 + mutex_lock(&indio_dev->mlock); 77 + info->conv_mode = mode; 78 + val = readl(info->regs + VF610_DACx_STATCTRL); 79 + if (mode) 80 + val |= VF610_DAC_LPEN; 81 + else 82 + val &= ~VF610_DAC_LPEN; 83 + writel(val, info->regs + VF610_DACx_STATCTRL); 84 + mutex_unlock(&indio_dev->mlock); 85 + 86 + return 0; 87 + } 88 + 89 + static int vf610_get_conversion_mode(struct iio_dev *indio_dev, 90 + const struct iio_chan_spec *chan) 91 + { 92 + struct vf610_dac *info = iio_priv(indio_dev); 93 + 94 + return info->conv_mode; 95 + } 96 + 97 + static const char * const vf610_conv_modes[] = { "high-power", "low-power" }; 98 + 99 + static const struct iio_enum vf610_conversion_mode = { 100 + .items = vf610_conv_modes, 101 + .num_items = ARRAY_SIZE(vf610_conv_modes), 102 + .get = vf610_get_conversion_mode, 103 + .set = vf610_set_conversion_mode, 104 + }; 105 + 106 + static const struct iio_chan_spec_ext_info vf610_ext_info[] = { 107 + IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR, 108 + &vf610_conversion_mode), 109 + {}, 110 + }; 111 + 112 + #define VF610_DAC_CHAN(_chan_type) { \ 113 + .type = (_chan_type), \ 114 + .output = 1, \ 115 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 116 + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ 117 + .ext_info = vf610_ext_info, \ 118 + } 119 + 120 + static const struct iio_chan_spec vf610_dac_iio_channels[] = { 121 + VF610_DAC_CHAN(IIO_VOLTAGE), 122 + }; 123 + 124 + static int vf610_read_raw(struct iio_dev *indio_dev, 125 + struct iio_chan_spec const *chan, 126 + int *val, int *val2, 127 + long mask) 128 + { 129 + struct vf610_dac *info = iio_priv(indio_dev); 130 + 131 + switch (mask) { 132 + case IIO_CHAN_INFO_RAW: 133 + *val = VF610_DAC_DAT0(readl(info->regs)); 134 + return IIO_VAL_INT; 135 + case IIO_CHAN_INFO_SCALE: 136 + /* 137 + * DACRFS is always 1 for valid reference and typical 138 + * reference voltage as per Vybrid datasheet is 3.3V 139 + * from section 9.1.2.1 of Vybrid datasheet 140 + */ 141 + *val = 3300 /* mV */; 142 + *val2 = 12; 143 + return IIO_VAL_FRACTIONAL_LOG2; 144 + 145 + default: 146 + return -EINVAL; 147 + } 148 + } 149 + 150 + static int vf610_write_raw(struct iio_dev *indio_dev, 151 + struct iio_chan_spec const *chan, 152 + int val, int val2, 153 + long mask) 154 + { 155 + struct vf610_dac *info = iio_priv(indio_dev); 156 + 157 + switch (mask) { 158 + case IIO_CHAN_INFO_RAW: 159 + mutex_lock(&indio_dev->mlock); 160 + writel(VF610_DAC_DAT0(val), info->regs); 161 + mutex_unlock(&indio_dev->mlock); 162 + return 0; 163 + 164 + default: 165 + return -EINVAL; 166 + } 167 + } 168 + 169 + static const struct iio_info vf610_dac_iio_info = { 170 + .driver_module = THIS_MODULE, 171 + .read_raw = &vf610_read_raw, 172 + .write_raw = &vf610_write_raw, 173 + }; 174 + 175 + static const struct of_device_id vf610_dac_match[] = { 176 + { .compatible = "fsl,vf610-dac", }, 177 + { /* sentinel */ } 178 + }; 179 + MODULE_DEVICE_TABLE(of, vf610_dac_match); 180 + 181 + static int vf610_dac_probe(struct platform_device *pdev) 182 + { 183 + struct iio_dev *indio_dev; 184 + struct vf610_dac *info; 185 + struct resource *mem; 186 + int ret; 187 + 188 + indio_dev = devm_iio_device_alloc(&pdev->dev, 189 + sizeof(struct vf610_dac)); 190 + if (!indio_dev) { 191 + dev_err(&pdev->dev, "Failed allocating iio device\n"); 192 + return -ENOMEM; 193 + } 194 + 195 + info = iio_priv(indio_dev); 196 + info->dev = &pdev->dev; 197 + 198 + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 199 + info->regs = devm_ioremap_resource(&pdev->dev, mem); 200 + if (IS_ERR(info->regs)) 201 + return PTR_ERR(info->regs); 202 + 203 + info->clk = devm_clk_get(&pdev->dev, "dac"); 204 + if (IS_ERR(info->clk)) { 205 + dev_err(&pdev->dev, "Failed getting clock, err = %ld\n", 206 + PTR_ERR(info->clk)); 207 + return PTR_ERR(info->clk); 208 + } 209 + 210 + platform_set_drvdata(pdev, indio_dev); 211 + 212 + indio_dev->name = dev_name(&pdev->dev); 213 + indio_dev->dev.parent = &pdev->dev; 214 + indio_dev->dev.of_node = pdev->dev.of_node; 215 + indio_dev->info = &vf610_dac_iio_info; 216 + indio_dev->modes = INDIO_DIRECT_MODE; 217 + indio_dev->channels = vf610_dac_iio_channels; 218 + indio_dev->num_channels = ARRAY_SIZE(vf610_dac_iio_channels); 219 + 220 + ret = clk_prepare_enable(info->clk); 221 + if (ret) { 222 + dev_err(&pdev->dev, 223 + "Could not prepare or enable the clock\n"); 224 + return ret; 225 + } 226 + 227 + vf610_dac_init(info); 228 + 229 + ret = iio_device_register(indio_dev); 230 + if (ret) { 231 + dev_err(&pdev->dev, "Couldn't register the device\n"); 232 + goto error_iio_device_register; 233 + } 234 + 235 + return 0; 236 + 237 + error_iio_device_register: 238 + clk_disable_unprepare(info->clk); 239 + 240 + return ret; 241 + } 242 + 243 + static int vf610_dac_remove(struct platform_device *pdev) 244 + { 245 + struct iio_dev *indio_dev = platform_get_drvdata(pdev); 246 + struct vf610_dac *info = iio_priv(indio_dev); 247 + 248 + iio_device_unregister(indio_dev); 249 + vf610_dac_exit(info); 250 + clk_disable_unprepare(info->clk); 251 + 252 + return 0; 253 + } 254 + 255 + #ifdef CONFIG_PM_SLEEP 256 + static int vf610_dac_suspend(struct device *dev) 257 + { 258 + struct iio_dev *indio_dev = dev_get_drvdata(dev); 259 + struct vf610_dac *info = iio_priv(indio_dev); 260 + 261 + vf610_dac_exit(info); 262 + clk_disable_unprepare(info->clk); 263 + 264 + return 0; 265 + } 266 + 267 + static int vf610_dac_resume(struct device *dev) 268 + { 269 + struct iio_dev *indio_dev = dev_get_drvdata(dev); 270 + struct vf610_dac *info = iio_priv(indio_dev); 271 + int ret; 272 + 273 + ret = clk_prepare_enable(info->clk); 274 + if (ret) 275 + return ret; 276 + 277 + vf610_dac_init(info); 278 + 279 + return 0; 280 + } 281 + #endif 282 + 283 + static SIMPLE_DEV_PM_OPS(vf610_dac_pm_ops, vf610_dac_suspend, vf610_dac_resume); 284 + 285 + static struct platform_driver vf610_dac_driver = { 286 + .probe = vf610_dac_probe, 287 + .remove = vf610_dac_remove, 288 + .driver = { 289 + .name = "vf610-dac", 290 + .of_match_table = vf610_dac_match, 291 + .pm = &vf610_dac_pm_ops, 292 + }, 293 + }; 294 + module_platform_driver(vf610_dac_driver); 295 + 296 + MODULE_AUTHOR("Sanchayan Maity <sanchayan.maity@toradex.com>"); 297 + MODULE_DESCRIPTION("Freescale VF610 DAC driver"); 298 + MODULE_LICENSE("GPL v2");