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
40 #define UCD9080_STATUS_R_REG 0x26
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
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
56 #define UCD9080_FLASHSTATE_LOCK 0x00
57 #define UCD9080_FLASHSTATE_UNLOCK 0x02
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
;
146 int adapter_supports_blocks
;
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
);
160 unsigned char railvalues
[UCD9080_I2C_READ_BLOCK_SIZE
];
161 struct ucd9080_data
*data
;
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
,
170 return sprintf(buf
, "Error\n");
171 } else { /* adapter does not support block reads */
173 for (i
= 0; i
< (UCD9080_RAILS_TOTAL
>>1); i
++) {
174 value
= i2c_smbus_read_word_data(client
,
175 UCD9080_RAIL_START_R_REG
);
177 return sprintf(buf
, "Error\n");
179 ((unsigned short *)railvalues
)[i
] =
180 (unsigned short) value
;
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 */
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
);
209 status
= i2c_smbus_read_byte_data(client
, UCD9080_STATUS_R_REG
);
211 return sprintf(buf
, "Error\n");
213 return sprintf(buf
, "%i\n", status
);
215 static DEVICE_ATTR(status
, S_IRUSR
, ucd9080_show_status
, NULL
);
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
);
224 version
= i2c_smbus_read_byte_data(client
, UCD9080_VERSION_R_REG
);
226 return sprintf(buf
, "Error\n");
228 return sprintf(buf
, "%i\n", version
);
230 static DEVICE_ATTR(version
, S_IRUSR
, ucd9080_show_version
, NULL
);
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
);
239 railstatus
= i2c_smbus_read_word_data(client
,
240 UCD9080_RAILSTATUS_START_R_REG
);
242 return sprintf(buf
, "Error\n");
244 return sprintf(buf
, "%i\n", railstatus
);
246 static DEVICE_ATTR(railstatus
, S_IRUSR
, ucd9080_show_railstatus
, NULL
);
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
);
256 if ((strict_strtoul(buf
, 10, &val
) < 0) || (val
> 1))
259 ret
= i2c_smbus_write_byte_data(client
, UCD9080_RESTART_W_REG
,
260 UCD9080_RESTART_VALUE
);
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
,
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
);
285 unsigned short waddr
= UCD9080_WADDR_START_RW_REG
;
286 unsigned char write_block
[I2C_SMBUS_BLOCK_MAX
+ 2];
289 /* Flashstate unlock */
290 err
= i2c_smbus_write_byte_data(client
, UCD9080_FLASHLOCK_RW_REG
,
291 UCD9080_FLASHSTATE_UNLOCK
);
294 /* Write WADDR start */
295 err
= i2c_smbus_write_word_data(client
, UCD9080_WADDR_START_RW_REG
,
296 UCD9080_CONF_WADDR_START
);
299 /* Write WDATA memory update constant */
300 err
= i2c_smbus_write_word_data(client
, UCD9080_WDATA_START_RW_REG
,
301 UCD9080_CONF_WDATA_UPDATE
);
305 for (i
= 0; i
< UCD9080_CONF_ITERATIONS
; i
++) {
307 err
= i2c_smbus_write_word_data(client
,
308 UCD9080_WADDR_START_RW_REG
,
313 if (data
->adapter_supports_blocks
) {
315 write_block
[0] = UCD9080_I2C_WRITE_BLOCK_SIZE
;
316 memcpy(&write_block
[1],
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
,
326 } else { /* adapter supports word only */
327 for (ii
= 0; ii
< (UCD9080_I2C_WRITE_BLOCK_SIZE
>>1);
329 err
= i2c_smbus_write_word_data(client
,
330 UCD9080_WDATA_START_RW_REG
,
331 ((unsigned short *)ucd9080_initseq
)[ii
]);
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
);
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
;
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
))) {
366 data
= kzalloc(sizeof(struct ucd9080_data
), GFP_KERNEL
);
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
);
380 /* register sysfs hooks */
381 err
= sysfs_create_group(&client
->dev
.kobj
, &ucd9080_attr_group
);
385 dev_info(&client
->dev
, "driver version %s enable\n", DRIVER_VERSION
);
393 static int ucd9080_remove(struct i2c_client
*client
)
395 struct ucd9080_data
*data
;
397 data
= i2c_get_clientdata(client
);
399 sysfs_remove_group(&client
->dev
.kobj
, &ucd9080_attr_group
);
405 #define ucd9080_suspend NULL
406 #define ucd9080_resume NULL
408 static const struct i2c_device_id ucd9080_id
[] = {
412 MODULE_DEVICE_TABLE(i2c
, ucd9080_id
);
414 static struct i2c_driver ucd9080_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
);