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 const struct i2c_device_id *id) 156 { 157 int res; 158 159 res = als_set_default_config(client); 160 if (res < 0) 161 return res; 162 163 res = sysfs_create_group(&client->dev.kobj, &m_als_gr); 164 if (res) { 165 dev_err(&client->dev, "isl29020: device create file failed\n"); 166 return res; 167 } 168 dev_info(&client->dev, "%s isl29020: ALS chip found\n", client->name); 169 als_set_power_state(client, 0); 170 pm_runtime_enable(&client->dev); 171 return res; 172 } 173 174 static void isl29020_remove(struct i2c_client *client) 175 { 176 pm_runtime_disable(&client->dev); 177 sysfs_remove_group(&client->dev.kobj, &m_als_gr); 178 } 179 180 static const struct i2c_device_id isl29020_id[] = { 181 { "isl29020", 0 }, 182 { } 183 }; 184 185 MODULE_DEVICE_TABLE(i2c, isl29020_id); 186 187 #ifdef CONFIG_PM 188 189 static int isl29020_runtime_suspend(struct device *dev) 190 { 191 struct i2c_client *client = to_i2c_client(dev); 192 als_set_power_state(client, 0); 193 return 0; 194 } 195 196 static int isl29020_runtime_resume(struct device *dev) 197 { 198 struct i2c_client *client = to_i2c_client(dev); 199 als_set_power_state(client, 1); 200 return 0; 201 } 202 203 static const struct dev_pm_ops isl29020_pm_ops = { 204 .runtime_suspend = isl29020_runtime_suspend, 205 .runtime_resume = isl29020_runtime_resume, 206 }; 207 208 #define ISL29020_PM_OPS (&isl29020_pm_ops) 209 #else /* CONFIG_PM */ 210 #define ISL29020_PM_OPS NULL 211 #endif /* CONFIG_PM */ 212 213 static struct i2c_driver isl29020_driver = { 214 .driver = { 215 .name = "isl29020", 216 .pm = ISL29020_PM_OPS, 217 }, 218 .probe = isl29020_probe, 219 .remove = isl29020_remove, 220 .id_table = isl29020_id, 221 }; 222 223 module_i2c_driver(isl29020_driver); 224 225 MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com>"); 226 MODULE_DESCRIPTION("Intersil isl29020 ALS Driver"); 227 MODULE_LICENSE("GPL v2"); 228