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