summaryrefslogtreecommitdiff
path: root/drivers/iio/dac/mcf54415_dac.c
blob: e2c12241a53473f2ab81c90c2700f47fe9650444 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * NXP mcf54415 DAC driver
 *
 * Copyright 2026 BayLibre - adureghello@baylibre.com
 */

#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/types.h>

#include <linux/iio/iio.h>

#define MCF54415_DAC_CR			0x00
#define MCF54415_DAC_CR_PDN		BIT(0)
#define MCF54415_DAC_CR_HSLS		BIT(6)
#define MCF54415_DAC_CR_WMLVL		GENMASK(9, 8)
#define MCF54415_DAC_CR_FILT		BIT(12)

#define MCF54415_DAC_DATA		0x02

struct mcf54415_dac {
	struct regmap *map;
	struct clk *clk;
};

static const struct regmap_config mcf54415_dac_regmap_config = {
	.reg_bits = 16,
	.reg_stride = 2,
	.val_bits = 16,
	.max_register = 0x0c, /* DACX_FILTCNT,  R.M. Table 30-2 */
	.val_format_endian = REGMAP_ENDIAN_BIG,
	.reg_format_endian = REGMAP_ENDIAN_BIG,
};

static int mcf54415_dac_init(struct mcf54415_dac *info)
{
	u16 val = MCF54415_DAC_CR_FILT | FIELD_PREP(MCF54415_DAC_CR_WMLVL, 1);
	int ret;

	/* Fixed defaults and enable DAC (bit 0 set to 0) */
	ret = regmap_write(info->map, MCF54415_DAC_CR, val);
	if (ret)
		return ret;

	/* DAC is ready after 12us, from RM table 40-3  */
	fsleep(12);

	return 0;
}

static void mcf54415_dac_exit(void *data)
{
	struct mcf54415_dac *info = data;

	regmap_set_bits(info->map, MCF54415_DAC_CR, MCF54415_DAC_CR_PDN);
}

static const struct iio_chan_spec mcf54415_dac_iio_channel = {
	.type = IIO_VOLTAGE,
	.output = 1,
	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
};

static int mcf54415_read_raw(struct iio_dev *indio_dev,
			     struct iio_chan_spec const *chan,
			     int *val, int *val2, long mask)
{
	struct mcf54415_dac *info = iio_priv(indio_dev);
	unsigned int reg;
	int ret;

	switch (mask) {
	case IIO_CHAN_INFO_RAW:
		ret = regmap_read(info->map, MCF54415_DAC_DATA, &reg);
		if (ret)
			return ret;
		*val = reg & GENMASK(11, 0);
		return IIO_VAL_INT;
	case IIO_CHAN_INFO_SCALE:
		/* Reference voltage as per ColdFire datasheet is 3.3V */
		*val = 3300 /* mV */;
		*val2 = 12;
		return IIO_VAL_FRACTIONAL_LOG2;
	default:
		return -EINVAL;
	}
}

static int mcf54415_write_raw(struct iio_dev *indio_dev,
			      struct iio_chan_spec const *chan,
			      int val, int val2, long mask)
{
	struct mcf54415_dac *info = iio_priv(indio_dev);

	switch (mask) {
	case IIO_CHAN_INFO_RAW:
		/* Check based on RM 30.3.2 (DACn_DATA) reg. resolution */
		if (val < 0 || val > 4095)
			return -EINVAL;
		return regmap_write(info->map, MCF54415_DAC_DATA, val);
	default:
		return -EINVAL;
	}
}

static const struct iio_info mcf54415_dac_iio_info = {
	.read_raw = &mcf54415_read_raw,
	.write_raw = &mcf54415_write_raw,
};

static int mcf54415_dac_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct iio_dev *indio_dev;
	struct mcf54415_dac *info;
	void __iomem *regs;
	int ret;

	indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
	if (!indio_dev)
		return -ENOMEM;

	info = iio_priv(indio_dev);

	regs = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(regs))
		return dev_err_probe(dev, PTR_ERR(regs), "failed to get io regs\n");

	info->map = devm_regmap_init_mmio(dev, regs, &mcf54415_dac_regmap_config);
	if (IS_ERR(info->map))
		return PTR_ERR(info->map);

	info->clk = devm_clk_get_enabled(dev, "dac");
	if (IS_ERR(info->clk))
		return dev_err_probe(dev, PTR_ERR(info->clk), "failed getting clock\n");

	indio_dev->name = "mcf54415";
	indio_dev->info = &mcf54415_dac_iio_info;
	indio_dev->modes = INDIO_DIRECT_MODE;
	indio_dev->channels = &mcf54415_dac_iio_channel;
	indio_dev->num_channels = 1;

	ret = mcf54415_dac_init(info);
	if (ret)
		return ret;

	ret = devm_add_action_or_reset(dev, mcf54415_dac_exit, info);
	if (ret)
		return ret;

	return devm_iio_device_register(dev, indio_dev);
}

static const struct platform_device_id mcf54415_dac_ids[] = {
	{ .name = "mcfdac" },
	{ }
};
MODULE_DEVICE_TABLE(platform, mcf54415_dac_ids);

static struct platform_driver mcf54415_dac_driver = {
	.driver = {
		.name = "mcf54415_dac",
	},
	.probe = mcf54415_dac_probe,
	.id_table = mcf54415_dac_ids,
};
module_platform_driver(mcf54415_dac_driver);

MODULE_AUTHOR("Angelo Dureghello <angelo@kernel-space.org>");
MODULE_DESCRIPTION("NXP MCF54415 DAC driver");
MODULE_LICENSE("GPL");