Lynx framebuffers multidomain implementation.
[linux/elbrus.git] / drivers / misc / ucd9080.c
blob9bb0139e8563c59e4a653db35a42c50974b5e910
1 /*
2 * ucd9080.c - Linux kernel module for
3 * Texas Instruments 8-channel power-supply sequencer and monitor.
5 * Copyright (c) 2012-2013 Andrey Kuyan <kuyan_a@mcst.ru>
6 * 2012-2013 Evgeny Kravtsunov <kravtsunov_e@mcst.ru>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <linux/module.h>
24 #include <linux/slab.h>
25 #include <linux/init.h>
26 #include <linux/i2c.h>
27 #include <linux/mutex.h>
29 #define UCD9080_DRV_NAME "ucd9080"
30 #define DRIVER_VERSION "0.01"
32 /* RAIL registers: |--RAIL1H--|--RAIL1L--|...|--RAIL8L--|
33 * adjustment (+1) when reading RAIL1H - RAIL8H,
34 * or (-15) when reading RAIL8L */
35 #define UCD9080_RAIL_START_R_REG 0x00
36 #define UCD9080_RAILS_TOTAL 16
37 /* ERROR registers: |--ERROR1--|--ERROR2--|...|--ERROR6--| */
38 #define UCD9080_ERRORSTART_R_REG 0x20
39 /* Status: */
40 #define UCD9080_STATUS_R_REG 0x26
41 /* Version: */
42 #define UCD9080_VERSION_R_REG 0x27
43 /* Railstatus: |--RAILSTATUS2--|--RAILSTATUS1--| */
44 #define UCD9080_RAILSTATUS_START_R_REG 0x29
45 /* Flashlock (cachable): */
46 #define UCD9080_FLASHLOCK_RW_REG 0x2E
47 /* Restart: */
48 #define UCD9080_RESTART_W_REG 0x2F
49 /* Waddr (cachable): |--WADDR2--|--WADDR1--| */
50 #define UCD9080_WADDR_START_RW_REG 0x31
51 /* Wdata (cachable): |--WDATA2--|--WDATA1--| */
52 #define UCD9080_WDATA_START_RW_REG 0x33
54 /* Common */
55 /* Flashstates: */
56 #define UCD9080_FLASHSTATE_LOCK 0x00
57 #define UCD9080_FLASHSTATE_UNLOCK 0x02
59 /* Restart value: */
60 #define UCD9080_RESTART_VALUE 0x00
62 /* Configuration values */
63 #define UCD9080_CONF_WADDR_START 0xE000
64 #define UCD9080_CONF_WDATA_UPDATE 0xBADC
66 /* I2C Write block size (in bytes) */
67 #define UCD9080_I2C_WRITE_BLOCK_SIZE 32
68 #define UCD9080_I2C_READ_BLOCK_SIZE UCD9080_I2C_WRITE_BLOCK_SIZE
70 /* Total conf buffer size */
71 #define UCD9080_CONF_BUFFER_SIZE 512
72 #define UCD9080_CONF_ITERATIONS (UCD9080_CONF_BUFFER_SIZE / \
73 UCD9080_I2C_WRITE_BLOCK_SIZE)
75 /* Init sequence: ranges 0xE080-0xE0A8 and 0xE100-0xE188 are tunable */
76 static const unsigned char ucd9080_initseq[UCD9080_CONF_BUFFER_SIZE] = {
77 /* 0xE000 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
78 /* 0xE008 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
79 /* 0xE010 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
80 /* 0xE018 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
81 /* 0xE020 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
82 /* 0xE028 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
83 /* 0xE030 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
84 /* 0xE038 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
85 /* 0xE040 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
86 /* 0xE048 */ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
87 /* 0xE050 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
88 /* 0xE058 */ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x02,
89 /* 0xE060 */ 0x00, 0x00, 0x00, 0x0f, 0x00, 0x02, 0x00, 0x02,
90 /* 0xE068 */ 0xff, 0x0f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00,
91 /* 0xE070 */ 0x00, 0x00, 0xc0, 0x20, 0x00, 0x00, 0x00, 0x00,
92 /* 0xE078 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0xdc, 0xba,
93 /* 0xE080 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
94 /* 0xE088 */ 0x00, 0x49, 0x4a, 0x4b, 0x01, 0x00, 0x01, 0x04,
95 /* 0xE090 */ 0x01, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00,
96 /* 0xE098 */ 0x05, 0xe0, 0x05, 0xa0, 0x32, 0xe0, 0x33, 0xe0,
97 /* 0xE0A0 */ 0x33, 0xe0, 0x35, 0xe0, 0x35, 0xe0, 0x00, 0x00,
98 /* 0xE0A8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99 /* 0xE0B0 */ 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f,
100 /* 0xE0B8 */ 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f,
101 /* 0xE0C0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102 /* 0xE0C8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103 /* 0xE0D0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104 /* 0xE0D8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105 /* 0xE0E0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106 /* 0xE0E8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107 /* 0xE0F0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108 /* 0xE0F8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109 /* 0xE100 */ 0x7f, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00,
110 /* 0xE108 */ 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00,
111 /* 0xE110 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112 /* 0xE118 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113 /* 0xE120 */ 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04,
114 /* 0xE128 */ 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04,
115 /* 0xE130 */ 0xa0, 0x0f, 0xa0, 0x0f, 0xa0, 0x0f, 0xa0, 0x0f,
116 /* 0xE138 */ 0xa0, 0x0f, 0xa0, 0x0f, 0xa0, 0x0f, 0xa0, 0x0f,
117 /* 0xE140 */ 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00,
118 /* 0xE148 */ 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00,
119 /* 0xE150 */ 0xff, 0xc0, 0xff, 0xc1, 0xff, 0xc2, 0xff, 0xc3,
120 /* 0xE158 */ 0xff, 0xc4, 0xff, 0xc5, 0xff, 0xc6, 0xff, 0xc7,
121 /* 0xE160 */ 0x00, 0x00, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0,
122 /* 0xE168 */ 0x04, 0x20, 0x08, 0x20, 0x04, 0x18, 0x02, 0x18,
123 /* 0xE170 */ 0x08, 0x18, 0x10, 0x18, 0x20, 0x18, 0x10, 0x20,
124 /* 0xE178 */ 0x00, 0x20, 0x20, 0x20, 0x40, 0x20, 0x80, 0x20,
125 /* 0xE180 */ 0x00, 0x00, 0x00, 0x04, 0x94, 0x02, 0xf2, 0x08,
126 /* 0xE188 */ 0x10, 0x03, 0x05, 0xc0, 0x40, 0x00, 0xff, 0x08,
127 /* 0xE190 */ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
128 /* 0xE198 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129 /* 0xE1A0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
130 /* 0xE1A8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 /* 0xE1B0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132 /* 0xE1B8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133 /* 0xE1C0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134 /* 0xE1C8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135 /* 0xE1D0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
136 /* 0xE1D8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
137 /* 0xE1E0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
138 /* 0xE1E8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
139 /* 0xE1F0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
140 /* 0xE1F8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
143 struct ucd9080_data {
144 struct i2c_client *client;
145 struct mutex lock;
146 int adapter_supports_blocks;
150 * sysfs layer
153 /* rail values */
154 static ssize_t ucd9080_show_railvalues(struct device *dev,
155 struct device_attribute *attr, char *buf)
157 struct i2c_client *client = to_i2c_client(dev);
158 int len;
159 int value;
160 unsigned char railvalues[UCD9080_I2C_READ_BLOCK_SIZE];
161 struct ucd9080_data *data;
162 int i;
164 data = i2c_get_clientdata(client);
165 if (data->adapter_supports_blocks) {
166 len = i2c_smbus_read_block_data(client,
167 UCD9080_RAIL_START_R_REG,
168 railvalues);
169 if (len < 0)
170 return sprintf(buf, "Error\n");
171 } else { /* adapter does not support block reads */
172 len = 0;
173 for (i = 0; i < (UCD9080_RAILS_TOTAL>>1); i++) {
174 value = i2c_smbus_read_word_data(client,
175 UCD9080_RAIL_START_R_REG);
176 if (value < 0)
177 return sprintf(buf, "Error\n");
179 ((unsigned short *)railvalues)[i] =
180 (unsigned short) value;
181 len += 2;
185 if (len != UCD9080_RAILS_TOTAL)
186 return sprintf(buf, "Error\n");
188 return sprintf(buf, "%i %i %i %i %i %i %i %i\n",
189 ((unsigned short *)railvalues)[0],
190 ((unsigned short *)railvalues)[1],
191 ((unsigned short *)railvalues)[2],
192 ((unsigned short *)railvalues)[3],
193 ((unsigned short *)railvalues)[4],
194 ((unsigned short *)railvalues)[5],
195 ((unsigned short *)railvalues)[6],
196 ((unsigned short *)railvalues)[7]);
198 static DEVICE_ATTR(railvalues, S_IRUSR, ucd9080_show_railvalues, NULL);
200 /* TODO: reading out errors FIFO buffer and parsing them */
202 /* status */
203 static ssize_t ucd9080_show_status(struct device *dev,
204 struct device_attribute *attr, char *buf)
206 struct i2c_client *client = to_i2c_client(dev);
207 int status;
209 status = i2c_smbus_read_byte_data(client, UCD9080_STATUS_R_REG);
210 if (status < 0)
211 return sprintf(buf, "Error\n");
213 return sprintf(buf, "%i\n", status);
215 static DEVICE_ATTR(status, S_IRUSR, ucd9080_show_status, NULL);
217 /* version */
218 static ssize_t ucd9080_show_version(struct device *dev,
219 struct device_attribute *attr, char *buf)
221 struct i2c_client *client = to_i2c_client(dev);
222 int version;
224 version = i2c_smbus_read_byte_data(client, UCD9080_VERSION_R_REG);
225 if (version < 0)
226 return sprintf(buf, "Error\n");
228 return sprintf(buf, "%i\n", version);
230 static DEVICE_ATTR(version, S_IRUSR, ucd9080_show_version, NULL);
232 /* railstatus */
233 static ssize_t ucd9080_show_railstatus(struct device *dev,
234 struct device_attribute *attr, char *buf)
236 struct i2c_client *client = to_i2c_client(dev);
237 int railstatus;
239 railstatus = i2c_smbus_read_word_data(client,
240 UCD9080_RAILSTATUS_START_R_REG);
241 if (railstatus < 0)
242 return sprintf(buf, "Error\n");
244 return sprintf(buf, "%i\n", railstatus);
246 static DEVICE_ATTR(railstatus, S_IRUSR, ucd9080_show_railstatus, NULL);
248 /* restart */
249 static ssize_t ucd9080_store_restart(struct device *dev,
250 struct device_attribute *attr, const char *buf, size_t count)
252 struct i2c_client *client = to_i2c_client(dev);
253 unsigned long val;
254 int ret;
256 if ((strict_strtoul(buf, 10, &val) < 0) || (val > 1))
257 return -EINVAL;
259 ret = i2c_smbus_write_byte_data(client, UCD9080_RESTART_W_REG,
260 UCD9080_RESTART_VALUE);
261 if (ret < 0)
262 return ret;
264 return count;
266 static DEVICE_ATTR(restart, S_IWUSR, NULL, ucd9080_store_restart);
268 static struct attribute *ucd9080_attributes[] = {
269 &dev_attr_railvalues.attr,
270 &dev_attr_status.attr,
271 &dev_attr_version.attr,
272 &dev_attr_railstatus.attr,
273 &dev_attr_restart.attr,
274 NULL
277 static const struct attribute_group ucd9080_attr_group = {
278 .attrs = ucd9080_attributes,
281 static int ucd9080_init_chip(struct i2c_client *client)
283 struct ucd9080_data *data = i2c_get_clientdata(client);
284 int i, ii;
285 unsigned short waddr = UCD9080_WADDR_START_RW_REG;
286 unsigned char write_block[I2C_SMBUS_BLOCK_MAX + 2];
287 int err = 0;
289 /* Flashstate unlock */
290 err = i2c_smbus_write_byte_data(client, UCD9080_FLASHLOCK_RW_REG,
291 UCD9080_FLASHSTATE_UNLOCK);
292 if (err)
293 return err;
294 /* Write WADDR start */
295 err = i2c_smbus_write_word_data(client, UCD9080_WADDR_START_RW_REG,
296 UCD9080_CONF_WADDR_START);
297 if (err)
298 return err;
299 /* Write WDATA memory update constant */
300 err = i2c_smbus_write_word_data(client, UCD9080_WDATA_START_RW_REG,
301 UCD9080_CONF_WDATA_UPDATE);
302 if (err)
303 return err;
305 for (i = 0; i < UCD9080_CONF_ITERATIONS; i++) {
306 /* WADDR */
307 err = i2c_smbus_write_word_data(client,
308 UCD9080_WADDR_START_RW_REG,
309 waddr);
310 if (err)
311 return err;
313 if (data->adapter_supports_blocks) {
314 /* WDATA (block) */
315 write_block[0] = UCD9080_I2C_WRITE_BLOCK_SIZE;
316 memcpy(&write_block[1],
317 (ucd9080_initseq +
318 (i*UCD9080_I2C_WRITE_BLOCK_SIZE)),
319 UCD9080_I2C_WRITE_BLOCK_SIZE);
320 err = i2c_smbus_write_block_data(client,
321 UCD9080_WDATA_START_RW_REG,
322 UCD9080_I2C_WRITE_BLOCK_SIZE,
323 write_block);
324 if (err)
325 return err;
326 } else { /* adapter supports word only */
327 for (ii = 0; ii < (UCD9080_I2C_WRITE_BLOCK_SIZE>>1);
328 ii += 2) {
329 err = i2c_smbus_write_word_data(client,
330 UCD9080_WDATA_START_RW_REG,
331 ((unsigned short *)ucd9080_initseq)[ii]);
332 if (err)
333 return err;
337 waddr += UCD9080_I2C_WRITE_BLOCK_SIZE;
339 /* Flashstate lock */
340 err = i2c_smbus_write_byte_data(client, UCD9080_FLASHLOCK_RW_REG,
341 UCD9080_FLASHSTATE_LOCK);
342 return err;
346 * I2C layer
349 static int ucd9080_probe(struct i2c_client *client,
350 const struct i2c_device_id *id)
352 struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
353 struct ucd9080_data *data;
354 int err = 0;
355 int adapter_supports_blocks = 1;
357 if (!i2c_check_functionality(adapter, (I2C_FUNC_SMBUS_BYTE |
358 I2C_FUNC_SMBUS_BLOCK_DATA))) {
359 adapter_supports_blocks = 0;
360 if (!i2c_check_functionality(adapter, (I2C_FUNC_SMBUS_BYTE |
361 I2C_FUNC_SMBUS_WORD_DATA))) {
362 return -EIO;
366 data = kzalloc(sizeof(struct ucd9080_data), GFP_KERNEL);
367 if (!data)
368 return -ENOMEM;
370 data->client = client;
371 data->adapter_supports_blocks = adapter_supports_blocks;
372 i2c_set_clientdata(client, data);
373 mutex_init(&data->lock);
375 /* initialization of UCD9080 chip */
376 err = ucd9080_init_chip(client);
377 if (err)
378 goto exit_kfree;
380 /* register sysfs hooks */
381 err = sysfs_create_group(&client->dev.kobj, &ucd9080_attr_group);
382 if (err)
383 goto exit_kfree;
385 dev_info(&client->dev, "driver version %s enable\n", DRIVER_VERSION);
386 return 0;
388 exit_kfree:
389 kfree(data);
390 return err;
393 static int ucd9080_remove(struct i2c_client *client)
395 struct ucd9080_data *data;
397 data = i2c_get_clientdata(client);
398 if (data) {
399 sysfs_remove_group(&client->dev.kobj, &ucd9080_attr_group);
400 kfree(data);
402 return 0;
405 #define ucd9080_suspend NULL
406 #define ucd9080_resume NULL
408 static const struct i2c_device_id ucd9080_id[] = {
409 { "ucd9080", 0},
412 MODULE_DEVICE_TABLE(i2c, ucd9080_id);
414 static struct i2c_driver ucd9080_driver = {
415 .driver = {
416 .name = UCD9080_DRV_NAME,
417 .owner = THIS_MODULE,
419 .suspend = ucd9080_suspend,
420 .resume = ucd9080_resume,
421 .probe = ucd9080_probe,
422 .remove = ucd9080_remove,
423 .id_table = ucd9080_id,
426 static int __init ucd9080_init(void)
428 return i2c_add_driver(&ucd9080_driver);
431 static void __exit ucd9080_exit(void)
433 i2c_del_driver(&ucd9080_driver);
436 MODULE_AUTHOR("Andrey Kuyan <kuyan_a@mcst.ru>");
437 MODULE_DESCRIPTION("TI 8-channel power-supply sequencer and monitor");
438 MODULE_LICENSE("GPL v2");
439 MODULE_VERSION(DRIVER_VERSION);
441 module_init(ucd9080_init);
442 module_exit(ucd9080_exit);