CHROMIUM: i2c: wire the smart battery passthrough
[cascardo/linux.git] / drivers / i2c / busses / i2c-chromeos_ec.c
1 /*
2  *  Copyright (C) 2012 Google, Inc
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  *
19  * Expose an I2C passthrough to the ChromeOS EC.
20  */
21
22 #include <linux/module.h>
23 #include <linux/i2c.h>
24 #include <linux/mfd/chromeos_ec.h>
25 #include <linux/mfd/chromeos_ec_commands.h>
26 #include <linux/of_i2c.h>
27 #include <linux/platform_device.h>
28 #include <linux/slab.h>
29
30 struct ec_i2c_device {
31         struct device *dev;
32         struct i2c_adapter adap;
33         struct chromeos_ec_device *ec;
34 };
35
36 #define CHECK_I2C_WR(num, length) \
37         (((msgs[num].flags & I2C_M_RD) == 0) && (msgs[num].len == length))
38
39 #define CHECK_I2C_RD(num, length) \
40         ((msgs[num].flags & I2C_M_RD) && (msgs[num].len == length))
41
42 /* Standard I2C address for smart batteries */
43 #define SBS_I2C_ADDR 0xB
44
45 static int ec_forward_to_sbs(struct i2c_adapter *adap, struct i2c_msg msgs[],
46                              int num)
47 {
48         struct ec_i2c_device *bus = adap->algo_data;
49
50         /* Battery device probing */
51         if ((num == 1) && (msgs[0].len == 0))
52                 return bus->ec->command_sendrecv(bus->ec, EC_CMD_SB_READ_WORD,
53                         msgs[0].buf, msgs[0].len, msgs[1].buf, msgs[1].len);
54         /* Read a word-sized register */
55         if ((num == 1) && CHECK_I2C_WR(0, 3))
56                 return bus->ec->command_send(bus->ec, EC_CMD_SB_WRITE_WORD,
57                          msgs[0].buf, msgs[0].len);
58         /* Write a word-sized register */
59         if ((num == 2) && CHECK_I2C_WR(0, 1) && CHECK_I2C_RD(1, 2))
60                 return bus->ec->command_sendrecv(bus->ec, EC_CMD_SB_READ_WORD,
61                         msgs[0].buf, msgs[0].len, msgs[1].buf, msgs[1].len);
62         /* Retrieve string data length */
63         if ((num == 2) && CHECK_I2C_WR(0, 1) && CHECK_I2C_RD(1, 1)) {
64                 msgs[1].buf[0] = I2C_SMBUS_BLOCK_MAX;
65                 return 0;
66         }
67         /* Read string data */
68         if ((num == 2) && CHECK_I2C_WR(0, 1) &&
69                           CHECK_I2C_RD(1, I2C_SMBUS_BLOCK_MAX)) {
70                 char tmpblock[I2C_SMBUS_BLOCK_MAX + 1];
71                 int ret = bus->ec->command_sendrecv(bus->ec,
72                         EC_CMD_SB_READ_BLOCK, msgs[0].buf, msgs[0].len,
73                         tmpblock, I2C_SMBUS_BLOCK_MAX);
74                 tmpblock[I2C_SMBUS_BLOCK_MAX] = 0;
75                 /* real string length */
76                 msgs[1].buf[0] = strlen(tmpblock);
77                 strlcpy(&msgs[1].buf[1], tmpblock, msgs[1].len);
78                 return ret;
79         }
80
81         return -EIO;
82 }
83
84 static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[],
85                        int num)
86 {
87         struct ec_i2c_device *bus = adap->algo_data;
88
89         if (num && (i2c_msgs[0].addr == SBS_I2C_ADDR))
90                 return ec_forward_to_sbs(adap, i2c_msgs, num);
91
92         return bus->ec->command_i2c(bus->ec, i2c_msgs, num);
93 }
94
95 static u32 ec_i2c_functionality(struct i2c_adapter *adap)
96 {
97         return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
98 }
99
100 static const struct i2c_algorithm ec_i2c_algorithm = {
101         .master_xfer    = ec_i2c_xfer,
102         .functionality  = ec_i2c_functionality,
103 };
104
105 static int __devinit ec_i2c_probe(struct platform_device *pdev)
106 {
107         struct chromeos_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
108         struct device *dev = ec->dev;
109         struct ec_i2c_device *bus = NULL;
110         int err;
111
112         dev_dbg(dev, "EC I2C pass-through probing\n");
113
114         bus = kzalloc(sizeof(*bus), GFP_KERNEL);
115         if (bus == NULL) {
116                 err = -ENOMEM;
117                 dev_err(dev, "cannot allocate bus device\n");
118                 goto fail;
119         }
120
121         bus->ec = ec;
122         bus->dev = dev;
123
124         bus->adap.owner   = THIS_MODULE;
125         bus->adap.retries = 3;
126         bus->adap.nr = 0;
127         strlcpy(bus->adap.name, "cros_ec_i2c", sizeof(bus->adap.name));
128         bus->adap.algo = &ec_i2c_algorithm;
129         bus->adap.algo_data = bus;
130         bus->adap.dev.parent = &pdev->dev;
131         bus->adap.dev.of_node = of_find_compatible_node(dev->of_node, NULL,
132                                                         "google,cros_ec-i2c");
133         err = i2c_add_adapter(&bus->adap);
134         if (err) {
135                 dev_err(dev, "cannot register i2c adapter\n");
136                 goto fail_reg;
137         }
138         platform_set_drvdata(pdev, bus);
139
140         dev_info(&pdev->dev, "%s: Chrome EC I2C pass-through adapter\n",
141                  dev_name(bus->dev));
142
143         of_i2c_register_devices(&bus->adap);
144
145         return 0;
146 fail_reg:
147         kfree(bus);
148 fail:
149         return err;
150 }
151
152 static int __exit ec_i2c_remove(struct platform_device *dev)
153 {
154         struct ec_i2c_device *bus = platform_get_drvdata(dev);
155
156         platform_set_drvdata(dev, NULL);
157
158         i2c_del_adapter(&bus->adap);
159         kfree(bus);
160
161         return 0;
162 }
163
164 static struct platform_driver ec_i2c_driver = {
165         .probe = ec_i2c_probe,
166         .remove = __exit_p(ec_i2c_remove),
167         .driver = {
168                 .name = "cros_ec-i2c",
169         },
170 };
171
172
173 module_platform_driver(ec_i2c_driver);
174
175 MODULE_LICENSE("GPL");
176 MODULE_DESCRIPTION("EC I2C pass-through driver");
177 MODULE_ALIAS("platform:cros_ec-i2c");