1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver (I2C Bus)
4 *
5 * Copyright 2010 Analog Devices Inc.
6 */
7
8 #include <linux/device.h>
9 #include <linux/kernel.h>
10 #include <linux/i2c.h>
11 #include <linux/slab.h>
12 #include <linux/module.h>
13
14 #include <linux/iio/iio.h>
15 #include "ade7854.h"
16
ade7854_i2c_write_reg(struct device * dev,u16 reg_address,u32 val,int bits)17 static int ade7854_i2c_write_reg(struct device *dev,
18 u16 reg_address,
19 u32 val,
20 int bits)
21 {
22 int ret;
23 int count;
24 struct iio_dev *indio_dev = dev_to_iio_dev(dev);
25 struct ade7854_state *st = iio_priv(indio_dev);
26
27 mutex_lock(&st->buf_lock);
28 st->tx[0] = (reg_address >> 8) & 0xFF;
29 st->tx[1] = reg_address & 0xFF;
30
31 switch (bits) {
32 case 8:
33 st->tx[2] = val & 0xFF;
34 count = 3;
35 break;
36 case 16:
37 st->tx[2] = (val >> 8) & 0xFF;
38 st->tx[3] = val & 0xFF;
39 count = 4;
40 break;
41 case 24:
42 st->tx[2] = (val >> 16) & 0xFF;
43 st->tx[3] = (val >> 8) & 0xFF;
44 st->tx[4] = val & 0xFF;
45 count = 5;
46 break;
47 case 32:
48 st->tx[2] = (val >> 24) & 0xFF;
49 st->tx[3] = (val >> 16) & 0xFF;
50 st->tx[4] = (val >> 8) & 0xFF;
51 st->tx[5] = val & 0xFF;
52 count = 6;
53 break;
54 default:
55 ret = -EINVAL;
56 goto unlock;
57 }
58
59 ret = i2c_master_send(st->i2c, st->tx, count);
60
61 unlock:
62 mutex_unlock(&st->buf_lock);
63
64 return ret < 0 ? ret : 0;
65 }
66
ade7854_i2c_read_reg(struct device * dev,u16 reg_address,u32 * val,int bits)67 static int ade7854_i2c_read_reg(struct device *dev,
68 u16 reg_address,
69 u32 *val,
70 int bits)
71 {
72 struct iio_dev *indio_dev = dev_to_iio_dev(dev);
73 struct ade7854_state *st = iio_priv(indio_dev);
74 int ret;
75
76 mutex_lock(&st->buf_lock);
77 st->tx[0] = (reg_address >> 8) & 0xFF;
78 st->tx[1] = reg_address & 0xFF;
79
80 ret = i2c_master_send(st->i2c, st->tx, 2);
81 if (ret < 0)
82 goto unlock;
83
84 ret = i2c_master_recv(st->i2c, st->rx, bits);
85 if (ret < 0)
86 goto unlock;
87
88 switch (bits) {
89 case 8:
90 *val = st->rx[0];
91 break;
92 case 16:
93 *val = (st->rx[0] << 8) | st->rx[1];
94 break;
95 case 24:
96 *val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2];
97 break;
98 case 32:
99 *val = (st->rx[0] << 24) | (st->rx[1] << 16) |
100 (st->rx[2] << 8) | st->rx[3];
101 break;
102 default:
103 ret = -EINVAL;
104 goto unlock;
105 }
106
107 unlock:
108 mutex_unlock(&st->buf_lock);
109 return ret;
110 }
111
ade7854_i2c_probe(struct i2c_client * client,const struct i2c_device_id * id)112 static int ade7854_i2c_probe(struct i2c_client *client,
113 const struct i2c_device_id *id)
114 {
115 struct ade7854_state *st;
116 struct iio_dev *indio_dev;
117
118 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
119 if (!indio_dev)
120 return -ENOMEM;
121 st = iio_priv(indio_dev);
122 i2c_set_clientdata(client, indio_dev);
123 st->read_reg = ade7854_i2c_read_reg;
124 st->write_reg = ade7854_i2c_write_reg;
125 st->i2c = client;
126 st->irq = client->irq;
127
128 return ade7854_probe(indio_dev, &client->dev);
129 }
130
131 static const struct i2c_device_id ade7854_id[] = {
132 { "ade7854", 0 },
133 { "ade7858", 0 },
134 { "ade7868", 0 },
135 { "ade7878", 0 },
136 { }
137 };
138 MODULE_DEVICE_TABLE(i2c, ade7854_id);
139
140 static struct i2c_driver ade7854_i2c_driver = {
141 .driver = {
142 .name = "ade7854",
143 },
144 .probe = ade7854_i2c_probe,
145 .id_table = ade7854_id,
146 };
147 module_i2c_driver(ade7854_i2c_driver);
148
149 MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
150 MODULE_DESCRIPTION("Analog Devices ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC I2C Driver");
151 MODULE_LICENSE("GPL v2");
152