29
29
#include "pmbus.h"
30
30
31
31
enum chips { zl2004 , zl2005 , zl2006 , zl2008 , zl2105 , zl2106 , zl6100 , zl6105 ,
32
- zl9101m , zl9117m };
32
+ zl9101 , zl9117 };
33
33
34
34
struct zl6100_data {
35
35
int id ;
36
36
ktime_t access ; /* chip access time */
37
+ int delay ; /* Delay between chip accesses in uS */
37
38
struct pmbus_driver_info info ;
38
39
};
39
40
@@ -44,29 +45,64 @@ struct zl6100_data {
44
45
45
46
#define ZL6100_MFR_XTEMP_ENABLE (1 << 7)
46
47
48
+ #define MFR_VMON_OV_FAULT_LIMIT 0xf5
49
+ #define MFR_VMON_UV_FAULT_LIMIT 0xf6
50
+ #define MFR_READ_VMON 0xf7
51
+
52
+ #define VMON_UV_WARNING (1 << 5)
53
+ #define VMON_OV_WARNING (1 << 4)
54
+ #define VMON_UV_FAULT (1 << 1)
55
+ #define VMON_OV_FAULT (1 << 0)
56
+
47
57
#define ZL6100_WAIT_TIME 1000 /* uS */
48
58
59
+ #define ZL6100_VOUT_EXPONENT -13
60
+
49
61
static ushort delay = ZL6100_WAIT_TIME ;
50
62
module_param (delay , ushort , 0644 );
51
63
MODULE_PARM_DESC (delay , "Delay between chip accesses in uS" );
52
64
53
65
/* Some chips need a delay between accesses */
54
66
static inline void zl6100_wait (const struct zl6100_data * data )
55
67
{
56
- if (delay ) {
68
+ if (data -> delay ) {
57
69
s64 delta = ktime_us_delta (ktime_get (), data -> access );
58
- if (delta < delay )
59
- udelay (delay - delta );
70
+ if (delta < data -> delay )
71
+ udelay (data -> delay - delta );
60
72
}
61
73
}
62
74
75
+ static int zl6100_lin11_to_lin16 (int reg )
76
+ {
77
+ int mantissa = ((s16 )((reg & 0x7ff ) << 5 )) >> 5 ;
78
+ int shift = ZL6100_VOUT_EXPONENT - (((s16 )reg ) >> 11 );
79
+
80
+ if (shift < 0 )
81
+ mantissa <<= - shift ;
82
+ else if (shift )
83
+ mantissa >>= shift ;
84
+
85
+ return mantissa & 0xffff ;
86
+ }
87
+
88
+ static u16 zl6100_lin16_to_lin11 (u16 reg )
89
+ {
90
+ int exp = ZL6100_VOUT_EXPONENT ;
91
+
92
+ while (reg > 0x3ff ) {
93
+ exp ++ ;
94
+ reg >>= 1 ;
95
+ }
96
+ return reg | (exp << 11 );
97
+ }
98
+
63
99
static int zl6100_read_word_data (struct i2c_client * client , int page , int reg )
64
100
{
65
101
const struct pmbus_driver_info * info = pmbus_get_driver_info (client );
66
102
struct zl6100_data * data = to_zl6100_data (info );
67
103
int ret ;
68
104
69
- if (page || reg >= PMBUS_VIRT_BASE )
105
+ if (page > info -> pages || reg >= PMBUS_VIRT_BASE )
70
106
return - ENXIO ;
71
107
72
108
if (data -> id == zl2005 ) {
@@ -83,25 +119,71 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, int reg)
83
119
}
84
120
85
121
zl6100_wait (data );
86
- ret = pmbus_read_word_data (client , page , reg );
87
- data -> access = ktime_get ();
88
122
123
+ if (page == 1 ) {
124
+ page = 0 ;
125
+ switch (reg ) {
126
+ case PMBUS_READ_VOUT :
127
+ reg = MFR_READ_VMON ;
128
+ break ;
129
+ case PMBUS_VOUT_OV_FAULT_LIMIT :
130
+ reg = MFR_VMON_OV_FAULT_LIMIT ;
131
+ break ;
132
+ case PMBUS_VOUT_UV_FAULT_LIMIT :
133
+ reg = MFR_VMON_UV_FAULT_LIMIT ;
134
+ break ;
135
+ default :
136
+ /* No other valid registers on page 1 */
137
+ return - ENXIO ;
138
+ }
139
+ ret = pmbus_read_word_data (client , page , reg );
140
+ if (ret > 0 )
141
+ ret = zl6100_lin11_to_lin16 (ret );
142
+ } else {
143
+ ret = pmbus_read_word_data (client , page , reg );
144
+ }
145
+ data -> access = ktime_get ();
89
146
return ret ;
90
147
}
91
148
92
149
static int zl6100_read_byte_data (struct i2c_client * client , int page , int reg )
93
150
{
94
151
const struct pmbus_driver_info * info = pmbus_get_driver_info (client );
95
152
struct zl6100_data * data = to_zl6100_data (info );
96
- int ret ;
153
+ int ret , status ;
97
154
98
- if (page > 0 )
155
+ if (page > info -> pages )
99
156
return - ENXIO ;
100
157
101
158
zl6100_wait (data );
102
- ret = pmbus_read_byte_data (client , page , reg );
103
- data -> access = ktime_get ();
104
159
160
+ if (page == 1 ) {
161
+ switch (reg ) {
162
+ case PMBUS_STATUS_VOUT :
163
+ ret = pmbus_read_byte_data (client , 0 ,
164
+ PMBUS_STATUS_MFR_SPECIFIC );
165
+ if (ret < 0 )
166
+ break ;
167
+
168
+ status = 0 ;
169
+ if (ret & VMON_UV_WARNING )
170
+ status |= PB_VOLTAGE_UV_WARNING ;
171
+ if (ret & VMON_OV_WARNING )
172
+ status |= PB_VOLTAGE_OV_WARNING ;
173
+ if (ret & VMON_UV_FAULT )
174
+ status |= PB_VOLTAGE_UV_FAULT ;
175
+ if (ret & VMON_OV_FAULT )
176
+ status |= PB_VOLTAGE_OV_FAULT ;
177
+ ret = status ;
178
+ break ;
179
+ default :
180
+ /* No other valid registers on page 1 */
181
+ return - ENXIO ;
182
+ }
183
+ } else {
184
+ ret = pmbus_read_byte_data (client , page , reg );
185
+ }
186
+ data -> access = ktime_get ();
105
187
return ret ;
106
188
}
107
189
@@ -112,9 +194,25 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg,
112
194
struct zl6100_data * data = to_zl6100_data (info );
113
195
int ret ;
114
196
115
- if (page || reg >= PMBUS_VIRT_BASE )
197
+ if (page > info -> pages || reg >= PMBUS_VIRT_BASE )
116
198
return - ENXIO ;
117
199
200
+ if (page == 1 ) {
201
+ page = 0 ;
202
+ switch (reg ) {
203
+ case PMBUS_VOUT_OV_FAULT_LIMIT :
204
+ reg = MFR_VMON_OV_FAULT_LIMIT ;
205
+ break ;
206
+ case PMBUS_VOUT_UV_FAULT_LIMIT :
207
+ reg = MFR_VMON_UV_FAULT_LIMIT ;
208
+ break ;
209
+ default :
210
+ /* No other valid registers on page 1 */
211
+ return - ENXIO ;
212
+ }
213
+ word = zl6100_lin16_to_lin11 (word );
214
+ }
215
+
118
216
zl6100_wait (data );
119
217
ret = pmbus_write_word_data (client , page , reg , word );
120
218
data -> access = ktime_get ();
@@ -126,14 +224,16 @@ static int zl6100_write_byte(struct i2c_client *client, int page, u8 value)
126
224
{
127
225
const struct pmbus_driver_info * info = pmbus_get_driver_info (client );
128
226
struct zl6100_data * data = to_zl6100_data (info );
129
- int ret ;
227
+ int ret = 0 ;
130
228
131
- if (page > 0 )
229
+ if (page > info -> pages )
132
230
return - ENXIO ;
133
231
134
- zl6100_wait (data );
135
- ret = pmbus_write_byte (client , page , value );
136
- data -> access = ktime_get ();
232
+ if (page <= 0 ) {
233
+ zl6100_wait (data );
234
+ ret = pmbus_write_byte (client , page , value );
235
+ data -> access = ktime_get ();
236
+ }
137
237
138
238
return ret ;
139
239
}
@@ -152,8 +252,8 @@ static const struct i2c_device_id zl6100_id[] = {
152
252
{"zl2106" , zl2106 },
153
253
{"zl6100" , zl6100 },
154
254
{"zl6105" , zl6105 },
155
- {"zl9101m " , zl9101m },
156
- {"zl9117m " , zl9117m },
255
+ {"zl9101 " , zl9101 },
256
+ {"zl9117 " , zl9117 },
157
257
{ }
158
258
};
159
259
MODULE_DEVICE_TABLE (i2c , zl6100_id );
@@ -203,16 +303,17 @@ static int zl6100_probe(struct i2c_client *client,
203
303
data -> id = mid -> driver_data ;
204
304
205
305
/*
206
- * ZL2005, ZL2008, ZL2105, and ZL6100 are known to require a wait time
207
- * between I2C accesses. ZL2004 and ZL6105 are known to be safe.
208
- * Other chips have not yet been tested.
306
+ * ZL2005, ZL2008, ZL2105, ZL6100, ZL9101M, and ZL9117M are known to
307
+ * require a wait time between I2C accesses. ZL2004 and ZL6105 are known
308
+ * to be safe. Other chips have not yet been tested.
209
309
*
210
310
* Only clear the wait time for chips known to be safe. The wait time
211
311
* can be cleared later for additional chips if tests show that it
212
312
* is not needed (in other words, better be safe than sorry).
213
313
*/
314
+ data -> delay = delay ;
214
315
if (data -> id == zl2004 || data -> id == zl6105 )
215
- delay = 0 ;
316
+ data -> delay = 0 ;
216
317
217
318
/*
218
319
* Since there was a direct I2C device access above, wait before
@@ -229,6 +330,16 @@ static int zl6100_probe(struct i2c_client *client,
229
330
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
230
331
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP ;
231
332
333
+ /*
334
+ * ZL2004, ZL9101M, and ZL9117M support monitoring an extra voltage
335
+ * (VMON for ZL2004, VDRV for ZL9101M and ZL9117M). Report it as output
336
+ * voltage on the second page (vout2).
337
+ */
338
+ if (data -> id == zl2004 || data -> id == zl9101 || data -> id == zl9117 ) {
339
+ info -> pages = 2 ;
340
+ info -> func [1 ] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT ;
341
+ }
342
+
232
343
ret = i2c_smbus_read_word_data (client , ZL6100_MFR_CONFIG );
233
344
if (ret < 0 )
234
345
return ret ;
0 commit comments