Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Iiro Vuorio committed Oct 22, 2018
2 parents 012d404 + 27f60b8 commit 300c0bc
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 42 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ This driver exposes the i2c registers to userspace via sysfs. Some extra feature
also exposed, please take a look at the [sysfs interface](#sysfs-interface) chapter for more info.

## TODO
- [ ] add support for the device interrupt
- [X] add support for the device interrupt
- [X] sysfs entry for the interrupt
- [X] interrupt handling in kernel
- [ ] notifying userspace through the sysfs
- [X] notifying userspace through the sysfs
- [ ] complete the sysfs interface
- [X] finish the devicetree overlay
- [ ] add support for an "easier" interface (/class/iio maybe?)
Expand Down
93 changes: 53 additions & 40 deletions amg88xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@

#define DRIVER_NAME "amg88xx"

// Since we're dealing with 12-bit numbers the sign-bit needs to be extended.
#define CONVERT_TO_12BIT(dst, src) \
/*
* Since we're dealing with 12-bit numbers the sign-bit needs to be extended
* for the number to be represented correctly
*/
#define convert_to_s16(dst, src) \
dst = src & 0x800 ? (src | (0xf << 12)) : src

/* i2c register addresses */
Expand Down Expand Up @@ -60,7 +63,9 @@
#define SENSOR_FIRST_REG 0x80 // Pixel 1 low bits register
#define SENSOR_LAST_REG 0xFF // Pixel 64 high bits register

/* Low level access helper functions */
/*
* Low level access helper functions
*/
static inline int amg88xx_write8(struct i2c_client *client, u8 reg_addr, u8 value)
{
return i2c_smbus_write_byte_data(client, reg_addr, value);
Expand Down Expand Up @@ -102,10 +107,11 @@ static int amg88xx_write16(struct i2c_client *client, u8 regl, u8 regh, u16 valu
return ret;

return amg88xx_write8(client, regh, (u8)(value >> 8));

}

/* Device configuration options that are mapped to register values */
/*
* Device configuration options that are mapped to register values
*/
enum amg88xx_device_mode {
NORMAL_MODE = 0x0,
SLEEP_MODE = 0x10,
Expand Down Expand Up @@ -134,27 +140,32 @@ enum amg88xx_interrupt_state {
INT_DISABLED = 0,
INT_ENABLED = 1 };

/* Structure for holding device related data */
/*
* Structure for holding device related data
*/
struct amg88xx {
struct i2c_client *client;
struct gpio_desc *int_gpio;
};

/* Handler for the threaded irq */
/*
* Handler for the threaded irq
*/
static irqreturn_t irq_handler(int irq, void *dev)
{
struct amg88xx *device;

device = dev;

// Signal the userspace by notifying pollers on the interrupt file
//sysfs_notify(&device->client->dev->kobj, NULL, "interrupt"); FIXME implement this and add tests
printk(KERN_INFO "placeholder: notifying userspace\n");
// Signal the userspace by notifying pollers on the 'interrupt' file
sysfs_notify(&device->client->dev.kobj, NULL, "interrupt");

return IRQ_HANDLED;
}

/* Helper functions for device access */
/*
* Helper functions for device access
*/
static inline int amg88xx_set_dev_mode(struct amg88xx *dev, enum amg88xx_device_mode mode)
{
return amg88xx_write8(dev->client, DEVICE_MODE_REG, mode);
Expand Down Expand Up @@ -239,7 +250,7 @@ static int amg88xx_get_int_upper_limit(struct amg88xx *dev, s16 *limit)
if (ret < 0)
return ret;
else
CONVERT_TO_12BIT(*limit, ret);
convert_to_s16(*limit, ret);

return 0;
}
Expand All @@ -254,7 +265,7 @@ static int amg88xx_get_int_lower_limit(struct amg88xx *dev, s16 *limit)
if (ret < 0)
return ret;
else
CONVERT_TO_12BIT(*limit, ret);
convert_to_s16(*limit, ret);

return 0;
}
Expand All @@ -269,7 +280,7 @@ static int amg88xx_get_int_hysteresis(struct amg88xx *dev, s16 *hysteresis)
if (ret < 0)
return ret;
else
CONVERT_TO_12BIT(*hysteresis, ret);
convert_to_s16(*hysteresis, ret);

return 0;
}
Expand Down Expand Up @@ -306,7 +317,7 @@ static int amg88xx_read_thermistor(struct amg88xx *dev, s16 *result)
if (ret < 0)
return ret;

CONVERT_TO_12BIT(*result, ret);
convert_to_s16(*result, ret);

return 0;
}
Expand All @@ -323,15 +334,17 @@ static int amg88xx_read_sensor(struct amg88xx *dev, s16 *res_array)
if (ret < 0)
return ret;

CONVERT_TO_12BIT(res_array[index], ret);
convert_to_s16(res_array[index], ret);

reg_addr += 2;
}

return 0;
}

/* sysfs entries and the functions to implement them */
/*
* sysfs entries and the functions to implement them
*/
static ssize_t show_sensor(struct device *dev, struct device_attribute *attr,
char *buf)
{
Expand All @@ -347,15 +360,15 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *attr,

ret = amg88xx_read_sensor(device, sensor_array);
if (ret < 0) {
printk(KERN_ERR "Failed to read the sensor\n");
dev_err(dev, "Failed to read the sensor\n");
return ret;
}

for (row = 0; row < 8; row++) {
for (col = 0; col < 8; col++) {
/*
* Write all the values on a row. Each value is sepparated by a comma
* and there is newline character after the last value
* and there is newline character after the last value
*/
nwrite = scnprintf(&buf[index],
PAGE_SIZE - (index - 1),
Expand All @@ -380,7 +393,7 @@ static ssize_t show_thermistor(struct device *dev, struct device_attribute *attr

ret = amg88xx_read_thermistor(device, &thermistor_value);
if (ret < 0) {
printk(KERN_ERR "Failed to read thermistor value\n");
dev_err(dev, "Failed to read thermistor value\n");
return ret;
}

Expand All @@ -400,7 +413,7 @@ static ssize_t show_device_mode(struct device *dev, struct device_attribute *att

ret = amg88xx_get_dev_mode(device, &mode);
if (ret < 0) {
printk(KERN_ERR "Failed to read device mode\n");
dev_err(dev, "Failed to read device mode\n");
return ret;
}

Expand All @@ -418,7 +431,7 @@ static ssize_t show_device_mode(struct device *dev, struct device_attribute *att
str = mode_strs[3];
break;
default:
printk(KERN_ERR "Unkown mode from hw\n");
dev_err(dev, "Unkown mode from hw\n");
return -EREMOTEIO;
}

Expand All @@ -443,13 +456,13 @@ static ssize_t store_device_mode(struct device *dev, struct device_attribute *at
} else if (strncmp("standby_10\n", buf, count) == 0) {
mode = STANDBY10_MODE;
} else {
printk(KERN_ERR "Input is not a supported mode\n");
dev_err(dev, "Input is not a supported mode\n");
return -EINVAL;
}

ret = amg88xx_set_dev_mode(device, mode);
if (ret < 0) {
printk(KERN_ERR "Failed to set device mode\n");
dev_err(dev, "Failed to set device mode\n");
return ret;
}

Expand All @@ -472,7 +485,7 @@ static ssize_t show_interrupt_mode(struct device *dev, struct device_attribute *

ret = amg88xx_get_int_conf(device, &mode, &enabled);
if (ret < 0) {
printk(KERN_ERR "Failed to read interrupt mode\n");
dev_err(dev, "Failed to read interrupt mode\n");
return ret;
}

Expand All @@ -496,13 +509,13 @@ static ssize_t store_interrupt_mode(struct device *dev, struct device_attribute
} else if (strncmp("differential\n", buf, count) == 0) {
mode = DIFFERENCE_MODE; //FIXME
} else {
printk(KERN_ERR "Invalid interrupt mode\n");
dev_err(dev, "Invalid interrupt mode\n");
return -EINVAL;
}

ret = amg88xx_set_int_mode(device, mode);
if (ret < 0) {
printk(KERN_ERR "Failed to set interrupt mode\n");
dev_err(dev, "Failed to set interrupt mode\n");
return ret;
}

Expand All @@ -525,7 +538,7 @@ static ssize_t show_interrupt_state(struct device *dev, struct device_attribute

ret = amg88xx_get_int_conf(device, &mode, &enabled);
if (ret < 0) {
printk(KERN_ERR "Failed to read interrupt mode\n");
dev_err(dev, "Failed to read interrupt mode\n");
return ret;
}

Expand All @@ -549,13 +562,13 @@ static ssize_t store_interrupt_state(struct device *dev, struct device_attribute
} else if (strncmp("disabled\n", buf, count) == 0) {
state = INT_DISABLED;
} else {
printk(KERN_ERR "Invalid interrupt state\n");
dev_err(dev, "Invalid interrupt state\n");
return -EINVAL;
}

ret = amg88xx_set_int_state(device, state);
if (ret < 0) {
printk(KERN_ERR "Failed to set interrupt state\n");
dev_err(dev, "Failed to set interrupt state\n");
return ret;
}

Expand Down Expand Up @@ -626,13 +639,13 @@ static ssize_t store_interrupt_levels(struct device *dev, struct device_attribut
// Convert the value to u16 number and check for upper limit
ret = kstrtos16(temp, 10, &values[i]);
if (ret < 0) {
printk(KERN_ERR "Failed to read value for %s from input\n",
dev_err(dev, "Failed to read value for %s from input\n",
i == 0 ? "upper limit" : (i == 1 ? "lower limit" : "hysteresis"));
goto exit;
}

if (values[i] < -2048|| values[i] > 2047) {
printk(KERN_ERR "Illegal input value for %s\n",
dev_err(dev, "Illegal input value for %s\n",
i == 0 ? "upper limit" : (i == 1 ? "lower limit" : "hysteresis"));
ret = -EINVAL;
goto exit;
Expand All @@ -643,19 +656,19 @@ static ssize_t store_interrupt_levels(struct device *dev, struct device_attribut

ret = amg88xx_set_int_upper_limit(device, values[0]);
if (ret < 0) {
printk(KERN_ERR "Failed to set the interrupt upper limit\n");
dev_err(dev, "Failed to set the interrupt upper limit\n");
goto exit;
}

ret = amg88xx_set_int_lower_limit(device, values[1]);
if (ret < 0) {
printk(KERN_ERR "Failed to set the interrupt lower limit\n");
dev_err(dev, "Failed to set the interrupt lower limit\n");
goto exit;
}

ret = amg88xx_set_int_hysteresis(device, values[2]);
if (ret < 0) {
printk(KERN_ERR "Failed to set the interrupt hysteresis\n");
dev_err(dev, "Failed to set the interrupt hysteresis\n");
goto exit;
}

Expand All @@ -681,7 +694,7 @@ static ssize_t show_interrupt(struct device *dev, struct device_attribute *attr,

ret = gpiod_get_value(device->int_gpio);
if (ret < 0) {
printk(KERN_ERR "Failed to read interrupt gpio value\n");
dev_err(dev, "Failed to read interrupt gpio value\n");
return ret;
}

Expand All @@ -706,7 +719,7 @@ static int amg88xx_probe_new(struct i2c_client *client)

device->int_gpio = gpiod_get(&client->dev, "interrupt", GPIOD_IN);
if (IS_ERR(device->int_gpio)) {
printk(KERN_ERR "Failed to get a gpio line for interrupt\n");
dev_err(&client->dev, "Failed to get a gpio line for interrupt\n");
return PTR_ERR(device->int_gpio);
}

Expand All @@ -718,15 +731,15 @@ static int amg88xx_probe_new(struct i2c_client *client)
client->name,
device);
if (ret < 0) {
printk(KERN_ERR "Failed to request a threaded irq\n");
dev_err(&client->dev, "Failed to request a threaded irq\n");
return ret;
}

dev_set_drvdata(&client->dev, device);

ret = amg88xx_reset(device);
if (ret < 0) {
printk(KERN_ERR "Failed to reset device\n");
dev_err(&client->dev, "Failed to reset device\n");
return ret;
}

Expand Down Expand Up @@ -761,7 +774,7 @@ static int amg88xx_remove(struct i2c_client *client)

ret = amg88xx_set_dev_mode(device, SLEEP_MODE);
if (ret < 0) {
printk(KERN_ERR "Failed to put the device to sleep\n");
dev_err(&client->dev, "Failed to put the device to sleep\n");
return ret;
}

Expand Down

0 comments on commit 300c0bc

Please sign in to comment.