|
15 | 15 | #include <linux/workqueue.h>
|
16 | 16 | #include "dell-smo8800-ids.h"
|
17 | 17 |
|
| 18 | +#define LIS3_WHO_AM_I 0x0f |
| 19 | + |
18 | 20 | #define DELL_LIS3LV02D_DMI_ENTRY(product_name, i2c_addr) \
|
19 | 21 | { \
|
20 | 22 | .matches = { \
|
@@ -57,6 +59,42 @@ static u8 i2c_addr;
|
57 | 59 | static struct i2c_client *i2c_dev;
|
58 | 60 | static bool notifier_registered;
|
59 | 61 |
|
| 62 | +static bool probe_i2c_addr; |
| 63 | +module_param(probe_i2c_addr, bool, 0444); |
| 64 | +MODULE_PARM_DESC(probe_i2c_addr, "Probe the i801 I2C bus for the accelerometer on models where the address is unknown, this may be dangerous."); |
| 65 | + |
| 66 | +static int detect_lis3lv02d(struct i2c_adapter *adap, unsigned short addr) |
| 67 | +{ |
| 68 | + union i2c_smbus_data smbus_data; |
| 69 | + int err; |
| 70 | + |
| 71 | + dev_info(&adap->dev, "Probing for lis3lv02d on address 0x%02x\n", addr); |
| 72 | + |
| 73 | + err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, LIS3_WHO_AM_I, |
| 74 | + I2C_SMBUS_BYTE_DATA, &smbus_data); |
| 75 | + if (err < 0) |
| 76 | + return 0; /* Not found */ |
| 77 | + |
| 78 | + /* valid who-am-i values are from drivers/misc/lis3lv02d/lis3lv02d.c */ |
| 79 | + switch (smbus_data.byte) { |
| 80 | + case 0x32: |
| 81 | + case 0x33: |
| 82 | + case 0x3a: |
| 83 | + case 0x3b: |
| 84 | + break; |
| 85 | + default: |
| 86 | + dev_warn(&adap->dev, "Unknown who-am-i register value 0x%02x\n", |
| 87 | + smbus_data.byte); |
| 88 | + return 0; /* Not found */ |
| 89 | + } |
| 90 | + |
| 91 | + dev_info(&adap->dev, |
| 92 | + "Detected lis3lv02d on address 0x%02x, please report this upstream to [email protected] so that a quirk can be added\n", |
| 93 | + addr); |
| 94 | + |
| 95 | + return 1; /* Found */ |
| 96 | +} |
| 97 | + |
60 | 98 | static bool i2c_adapter_is_main_i801(struct i2c_adapter *adap)
|
61 | 99 | {
|
62 | 100 | /*
|
@@ -97,10 +135,18 @@ static void instantiate_i2c_client(struct work_struct *work)
|
97 | 135 | if (!adap)
|
98 | 136 | return;
|
99 | 137 |
|
100 |
| - info.addr = i2c_addr; |
101 | 138 | strscpy(info.type, "lis3lv02d", I2C_NAME_SIZE);
|
102 | 139 |
|
103 |
| - i2c_dev = i2c_new_client_device(adap, &info); |
| 140 | + if (i2c_addr) { |
| 141 | + info.addr = i2c_addr; |
| 142 | + i2c_dev = i2c_new_client_device(adap, &info); |
| 143 | + } else { |
| 144 | + /* First try address 0x29 (most used) and then try 0x1d */ |
| 145 | + static const unsigned short addr_list[] = { 0x29, 0x1d, I2C_CLIENT_END }; |
| 146 | + |
| 147 | + i2c_dev = i2c_new_scanned_device(adap, &info, addr_list, detect_lis3lv02d); |
| 148 | + } |
| 149 | + |
104 | 150 | if (IS_ERR(i2c_dev)) {
|
105 | 151 | dev_err(&adap->dev, "error %ld registering i2c_client\n", PTR_ERR(i2c_dev));
|
106 | 152 | i2c_dev = NULL;
|
@@ -169,12 +215,14 @@ static int __init dell_lis3lv02d_init(void)
|
169 | 215 | put_device(dev);
|
170 | 216 |
|
171 | 217 | lis3lv02d_dmi_id = dmi_first_match(lis3lv02d_devices);
|
172 |
| - if (!lis3lv02d_dmi_id) { |
| 218 | + if (!lis3lv02d_dmi_id && !probe_i2c_addr) { |
173 | 219 | pr_warn("accelerometer is present on SMBus but its address is unknown, skipping registration\n");
|
| 220 | + pr_info("Pass dell_lis3lv02d.probe_i2c_addr=1 on the kernel command line to probe, this may be dangerous!\n"); |
174 | 221 | return 0;
|
175 | 222 | }
|
176 | 223 |
|
177 |
| - i2c_addr = (long)lis3lv02d_dmi_id->driver_data; |
| 224 | + if (lis3lv02d_dmi_id) |
| 225 | + i2c_addr = (long)lis3lv02d_dmi_id->driver_data; |
178 | 226 |
|
179 | 227 | /*
|
180 | 228 | * Register i2c-bus notifier + queue initial scan for lis3lv02d
|
|
0 commit comments