1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * isl29020.c - Intersil ALS Driver 4 * 5 * Copyright (C) 2008 Intel Corp 6 * 7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 * 9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 10 * 11 * Data sheet at: http://www.intersil.com/data/fn/fn6505.pdf 12 */ 13 14 #include <linux/module.h> 15 #include <linux/slab.h> 16 #include <linux/i2c.h> 17 #include <linux/err.h> 18 #include <linux/delay.h> 19 #include <linux/sysfs.h> 20 #include <linux/pm_runtime.h> 21 22 static DEFINE_MUTEX(mutex); 23 24 static ssize_t als_sensing_range_show(struct device *dev, 25 struct device_attribute *attr, char *buf) 26 { 27 struct i2c_client *client = to_i2c_client(dev); 28 int val; 29 30 val = i2c_smbus_read_byte_data(client, 0x00); 31 32 if (val < 0) 33 return val; 34 return sprintf(buf, "%d000\n", 1 << (2 * (val & 3))); 35 36 } 37 38 static ssize_t als_lux_input_data_show(struct device *dev, 39 struct device_attribute *attr, char *buf) 40 { 41 struct i2c_client *client = to_i2c_client(dev); 42 int ret_val, val; 43 unsigned long int lux; 44 int temp; 45 46 pm_runtime_get_sync(dev); 47 msleep(100); 48 49 mutex_lock(&mutex); 50 temp = i2c_smbus_read_byte_data(client, 0x02); /* MSB data */ 51 if (temp < 0) { 52 pm_runtime_put_sync(dev); 53 mutex_unlock(&mutex); 54 return temp; 55 } 56 57 ret_val = i2c_smbus_read_byte_data(client, 0x01); /* LSB data */ 58 mutex_unlock(&mutex); 59 60 if (ret_val < 0) { 61 pm_runtime_put_sync(dev); 62 return ret_val; 63 } 64 65 ret_val |= temp << 8; 66 val = i2c_smbus_read_byte_data(client, 0x00); 67 pm_runtime_put_sync(dev); 68 if (val < 0) 69 return val; 70 lux = ((((1 << (2 * (val & 3))))*1000) * ret_val) / 65536; 71 return sprintf(buf, "%ld\n", lux); 72 } 73 74 static ssize_t als_sensing_range_store(struct device *dev, 75 struct device_attribute *attr, const char *buf, size_t count) 76 { 77 struct i2c_client *client = to_i2c_client(dev); 78 int ret_val; 79 unsigned long val; 80 81 ret_val = kstrtoul(buf, 10, &val); 82 if (ret_val) 83 return ret_val; 84 85 if (val < 1 || val > 64000) 86 return -EINVAL; 87 88 /* Pick the smallest sensor range that will meet our requirements */ 89 if (val <= 1000) 90 val = 1; 91 else if (val <= 4000) 92 val = 2; 93 else if (val <= 16000) 94 val = 3; 95 else 96 val = 4; 97 98 ret_val = i2c_smbus_read_byte_data(client, 0x00); 99 if (ret_val < 0) 100 return ret_val; 101 102 ret_val &= 0xFC; /*reset the bit before setting them */ 103 ret_val |= val - 1; 104 ret_val = i2c_smbus_write_byte_data(client, 0x00, ret_val); 105 106 if (ret_val < 0) 107 return ret_val; 108 return count; 109 } 110 111 static void als_set_power_state(struct i2c_client *client, int enable) 112 { 113 int ret_val; 114 115 ret_val = i2c_smbus_read_byte_data(client, 0x00); 116 if (ret_val < 0) 117 return; 118 119 if (enable) 120 ret_val |= 0x80; 121 else 122 ret_val &= 0x7F; 123 124 i2c_smbus_write_byte_data(client, 0x00, ret_val); 125 } 126 127 static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR, 128 als_sensing_range_show, als_sensing_range_store); 129 static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux_input_data_show, NULL); 130 131 static struct attribute *mid_att_als[] = { 132 &dev_attr_lux0_sensor_range.attr, 133 &dev_attr_lux0_input.attr, 134 NULL 135 }; 136 137 static const struct attribute_group m_als_gr = { 138 .name = "isl29020", 139 .attrs = mid_att_als 140 }; 141 142 static int als_set_default_config(struct i2c_client *client) 143 { 144 int retval; 145 146 retval = i2c_smbus_write_byte_data(client, 0x00, 0xc0); 147 if (retval < 0) { 148 dev_err(&client->dev, "default write failed."); 149 return retval; 150 } 151 return 0; 152 } 153 154 static int isl29020_probe(struct i2c_client *client) 155 { 156 int res; 157 158 res = als_set_default_config(client); 159 if (res < 0) 160 return res; 161 162 res = sysfs_create_group(&client->dev.kobj, &m_als_gr); 163 if (res) { 164 dev_err(&client->dev, "isl29020: device create file failed\n"); 165 return res; 166 } 167 dev_info(&client->dev, "%s isl29020: ALS chip found\n", client->name); 168 als_set_power_state(client, 0); 169 pm_runtime_enable(&client->dev); 170 return res; 171 } 172 173 static void isl29020_remove(struct i2c_client *client) 174 { 175 pm_runtime_disable(&client->dev); 176 sysfs_remove_group(&client->dev.kobj, &m_als_gr); 177 } 178 179 static const struct i2c_device_id isl29020_id[] = { 180 { "isl29020" }, 181 { } 182 }; 183 184 MODULE_DEVICE_TABLE(i2c, isl29020_id); 185 186 #ifdef CONFIG_PM 187 188 static int isl29020_runtime_suspend(struct device *dev) 189 { 190 struct i2c_client *client = to_i2c_client(dev); 191 als_set_power_state(client, 0); 192 return 0; 193 } 194 195 static int isl29020_runtime_resume(struct device *dev) 196 { 197 struct i2c_client *client = to_i2c_client(dev); 198 als_set_power_state(client, 1); 199 return 0; 200 } 201 202 static const struct dev_pm_ops isl29020_pm_ops = { 203 .runtime_suspend = isl29020_runtime_suspend, 204 .runtime_resume = isl29020_runtime_resume, 205 }; 206 207 #define ISL29020_PM_OPS (&isl29020_pm_ops) 208 #else /* CONFIG_PM */ 209 #define ISL29020_PM_OPS NULL 210 #endif /* CONFIG_PM */ 211 212 static struct i2c_driver isl29020_driver = { 213 .driver = { 214 .name = "isl29020", 215 .pm = ISL29020_PM_OPS, 216 }, 217 .probe = isl29020_probe, 218 .remove = isl29020_remove, 219 .id_table = isl29020_id, 220 }; 221 222 module_i2c_driver(isl29020_driver); 223 224 MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com>"); 225 MODULE_DESCRIPTION("Intersil isl29020 ALS Driver"); 226 MODULE_LICENSE("GPL v2"); 227