Skip to content

Commit 8efb11a

Browse files
author
Wolfram Sang
committed
Merge branch 'i2c-mux/for-next' of https://github.com/peda-r/i2c-mux into i2c/for-4.17
"These patches verify the device id of the PCA984x mux chips using standardized (but rarely implemented) i2c device identification."
2 parents af50371 + 2d74187 commit 8efb11a

File tree

3 files changed

+112
-6
lines changed

3 files changed

+112
-6
lines changed

drivers/i2c/i2c-core-base.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
#define I2C_ADDR_7BITS_MAX 0x77
5959
#define I2C_ADDR_7BITS_COUNT (I2C_ADDR_7BITS_MAX + 1)
6060

61+
#define I2C_ADDR_DEVICE_ID 0x7c
62+
6163
/*
6264
* core_lock protects i2c_adapter_idr, and guarantees that device detection,
6365
* deletion of detected devices, and attach_adapter calls are serialized
@@ -1976,6 +1978,37 @@ int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf,
19761978
}
19771979
EXPORT_SYMBOL(i2c_transfer_buffer_flags);
19781980

1981+
/**
1982+
* i2c_get_device_id - get manufacturer, part id and die revision of a device
1983+
* @client: The device to query
1984+
* @id: The queried information
1985+
*
1986+
* Returns negative errno on error, zero on success.
1987+
*/
1988+
int i2c_get_device_id(const struct i2c_client *client,
1989+
struct i2c_device_identity *id)
1990+
{
1991+
struct i2c_adapter *adap = client->adapter;
1992+
union i2c_smbus_data raw_id;
1993+
int ret;
1994+
1995+
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_I2C_BLOCK))
1996+
return -EOPNOTSUPP;
1997+
1998+
raw_id.block[0] = 3;
1999+
ret = i2c_smbus_xfer(adap, I2C_ADDR_DEVICE_ID, 0,
2000+
I2C_SMBUS_READ, client->addr << 1,
2001+
I2C_SMBUS_I2C_BLOCK_DATA, &raw_id);
2002+
if (ret)
2003+
return ret;
2004+
2005+
id->manufacturer_id = (raw_id.block[1] << 4) | (raw_id.block[2] >> 4);
2006+
id->part_id = ((raw_id.block[2] & 0xf) << 5) | (raw_id.block[3] >> 3);
2007+
id->die_revision = raw_id.block[3] & 0x7;
2008+
return 0;
2009+
}
2010+
EXPORT_SYMBOL_GPL(i2c_get_device_id);
2011+
19792012
/* ----------------------------------------------------
19802013
* the i2c address scanning function
19812014
* Will not work for 10-bit addresses!

drivers/i2c/muxes/i2c-mux-pca954x.c

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ struct chip_desc {
7777
pca954x_ismux = 0,
7878
pca954x_isswi
7979
} muxtype;
80+
struct i2c_device_identity id;
8081
};
8182

8283
struct pca954x {
@@ -97,59 +98,83 @@ static const struct chip_desc chips[] = {
9798
.nchans = 2,
9899
.enable = 0x4,
99100
.muxtype = pca954x_ismux,
101+
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
100102
},
101103
[pca_9542] = {
102104
.nchans = 2,
103105
.enable = 0x4,
104106
.has_irq = 1,
105107
.muxtype = pca954x_ismux,
108+
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
106109
},
107110
[pca_9543] = {
108111
.nchans = 2,
109112
.has_irq = 1,
110113
.muxtype = pca954x_isswi,
114+
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
111115
},
112116
[pca_9544] = {
113117
.nchans = 4,
114118
.enable = 0x4,
115119
.has_irq = 1,
116120
.muxtype = pca954x_ismux,
121+
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
117122
},
118123
[pca_9545] = {
119124
.nchans = 4,
120125
.has_irq = 1,
121126
.muxtype = pca954x_isswi,
127+
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
122128
},
123129
[pca_9546] = {
124130
.nchans = 4,
125131
.muxtype = pca954x_isswi,
132+
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
126133
},
127134
[pca_9547] = {
128135
.nchans = 8,
129136
.enable = 0x8,
130137
.muxtype = pca954x_ismux,
138+
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
131139
},
132140
[pca_9548] = {
133141
.nchans = 8,
134142
.muxtype = pca954x_isswi,
143+
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
135144
},
136145
[pca_9846] = {
137146
.nchans = 4,
138147
.muxtype = pca954x_isswi,
148+
.id = {
149+
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
150+
.part_id = 0x10b,
151+
},
139152
},
140153
[pca_9847] = {
141154
.nchans = 8,
142155
.enable = 0x8,
143156
.muxtype = pca954x_ismux,
157+
.id = {
158+
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
159+
.part_id = 0x108,
160+
},
144161
},
145162
[pca_9848] = {
146163
.nchans = 8,
147164
.muxtype = pca954x_isswi,
165+
.id = {
166+
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
167+
.part_id = 0x10a,
168+
},
148169
},
149170
[pca_9849] = {
150171
.nchans = 4,
151172
.enable = 0x4,
152173
.muxtype = pca954x_ismux,
174+
.id = {
175+
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
176+
.part_id = 0x109,
177+
},
153178
},
154179
};
155180

@@ -369,6 +394,30 @@ static int pca954x_probe(struct i2c_client *client,
369394
if (IS_ERR(gpio))
370395
return PTR_ERR(gpio);
371396

397+
match = of_match_device(of_match_ptr(pca954x_of_match), &client->dev);
398+
if (match)
399+
data->chip = of_device_get_match_data(&client->dev);
400+
else
401+
data->chip = &chips[id->driver_data];
402+
403+
if (data->chip->id.manufacturer_id != I2C_DEVICE_ID_NONE) {
404+
struct i2c_device_identity id;
405+
406+
ret = i2c_get_device_id(client, &id);
407+
if (ret && ret != -EOPNOTSUPP)
408+
return ret;
409+
410+
if (!ret &&
411+
(id.manufacturer_id != data->chip->id.manufacturer_id ||
412+
id.part_id != data->chip->id.part_id)) {
413+
dev_warn(&client->dev,
414+
"unexpected device id %03x-%03x-%x\n",
415+
id.manufacturer_id, id.part_id,
416+
id.die_revision);
417+
return -ENODEV;
418+
}
419+
}
420+
372421
/* Write the mux register at addr to verify
373422
* that the mux is in fact present. This also
374423
* initializes the mux to disconnected state.
@@ -378,12 +427,6 @@ static int pca954x_probe(struct i2c_client *client,
378427
return -ENODEV;
379428
}
380429

381-
match = of_match_device(of_match_ptr(pca954x_of_match), &client->dev);
382-
if (match)
383-
data->chip = of_device_get_match_data(&client->dev);
384-
else
385-
data->chip = &chips[id->driver_data];
386-
387430
data->last_chan = 0; /* force the first selection */
388431

389432
idle_disconnect_dt = of_node &&

include/linux/i2c.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ struct i2c_algorithm;
4747
struct i2c_adapter;
4848
struct i2c_client;
4949
struct i2c_driver;
50+
struct i2c_device_identity;
5051
union i2c_smbus_data;
5152
struct i2c_board_info;
5253
enum i2c_slave_event;
@@ -186,8 +187,37 @@ extern s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client,
186187
extern s32
187188
i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client,
188189
u8 command, u8 length, u8 *values);
190+
int i2c_get_device_id(const struct i2c_client *client,
191+
struct i2c_device_identity *id);
189192
#endif /* I2C */
190193

194+
/**
195+
* struct i2c_device_identity - i2c client device identification
196+
* @manufacturer_id: 0 - 4095, database maintained by NXP
197+
* @part_id: 0 - 511, according to manufacturer
198+
* @die_revision: 0 - 7, according to manufacturer
199+
*/
200+
struct i2c_device_identity {
201+
u16 manufacturer_id;
202+
#define I2C_DEVICE_ID_NXP_SEMICONDUCTORS 0
203+
#define I2C_DEVICE_ID_NXP_SEMICONDUCTORS_1 1
204+
#define I2C_DEVICE_ID_NXP_SEMICONDUCTORS_2 2
205+
#define I2C_DEVICE_ID_NXP_SEMICONDUCTORS_3 3
206+
#define I2C_DEVICE_ID_RAMTRON_INTERNATIONAL 4
207+
#define I2C_DEVICE_ID_ANALOG_DEVICES 5
208+
#define I2C_DEVICE_ID_STMICROELECTRONICS 6
209+
#define I2C_DEVICE_ID_ON_SEMICONDUCTOR 7
210+
#define I2C_DEVICE_ID_SPRINTEK_CORPORATION 8
211+
#define I2C_DEVICE_ID_ESPROS_PHOTONICS_AG 9
212+
#define I2C_DEVICE_ID_FUJITSU_SEMICONDUCTOR 10
213+
#define I2C_DEVICE_ID_FLIR 11
214+
#define I2C_DEVICE_ID_O2MICRO 12
215+
#define I2C_DEVICE_ID_ATMEL 13
216+
#define I2C_DEVICE_ID_NONE 0xffff
217+
u16 part_id;
218+
u8 die_revision;
219+
};
220+
191221
enum i2c_alert_protocol {
192222
I2C_PROTOCOL_SMBUS_ALERT,
193223
I2C_PROTOCOL_SMBUS_HOST_NOTIFY,

0 commit comments

Comments
 (0)