diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index dd0c3989218..41411647c8e 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -78,3 +78,52 @@ config MCP9808_I2C_DEV_NAME help Specify the device name of the I2C master device to which MCP9808 is connected. + +choice + prompt "MCP9808 trigger mode" + depends on MCP9808 + default MCP9808_TRIGGER_NONE + +config MCP9808_TRIGGER_NONE + bool "No trigger" + +config MCP9808_TRIGGER_GLOBAL_FIBER + depends on GPIO + select MCP9808_TRIGGER + select SENSOR_DELAYED_WORK + bool "Use global fiber" + +config MCP9808_TRIGGER_OWN_FIBER + depends on GPIO + select MCP9808_TRIGGER + bool "Use own fiber" + +endchoice + +config MCP9808_TRIGGER + bool + depends on MCP9808 + +config MCP9808_GPIO_CONTROLLER + string "GPIO controller for MCP9808 interrupt" + depends on MCP9808 && MCP9808_TRIGGER + default "GPIO_0" + help + The GPIO controller the MCP9808 interrupt is connected to. + +config MCP9808_GPIO_PIN + int "GPIO pin for MCP9808 interrupt" + depends on MCP9808 && MCP9808_TRIGGER + default 3 + help + The GPIO pin the MCP9808 interrupt is connected to. + +config MCP9808_FIBER_STACK_SIZE + int "Sensor delayed work fiber stack size" + depends on MCP9808 && MCP9808_TRIGGER_OWN_FIBER + default 1024 + +config MCP9808_FIBER_PRIORITY + int "MCP9808 fiber priority" + depends on MCP9808 && MCP9808_TRIGGER_OWN_FIBER + default 10 diff --git a/drivers/sensor/Makefile b/drivers/sensor/Makefile index 137e380688d..92dcb371c9d 100644 --- a/drivers/sensor/Makefile +++ b/drivers/sensor/Makefile @@ -3,3 +3,4 @@ ccflags-y +=-I$(srctree)/drivers obj-$(CONFIG_SENSOR_DELAYED_WORK) += sensor.o obj-$(CONFIG_MCP9808) += sensor_mcp9808.o +obj-$(CONFIG_MCP9808_TRIGGER) += sensor_mcp9808_trigger.o diff --git a/drivers/sensor/sensor_mcp9808.c b/drivers/sensor/sensor_mcp9808.c index d65fee0b792..0a4dc9fef3e 100644 --- a/drivers/sensor/sensor_mcp9808.c +++ b/drivers/sensor/sensor_mcp9808.c @@ -18,20 +18,13 @@ #include #include -#include #include -#include #include #include "sensor_mcp9808.h" -#ifndef CONFIG_SENSOR_DEBUG -#define DBG(...) { ; } -#else -#include -#define DBG printk -#endif /* CONFIG_SENSOR_DEBUG */ +struct mcp9808_data mcp9808_data; -static int mcp9808_reg_read(struct mcp9808_data *data, uint8_t reg, uint16_t *val) +int mcp9808_reg_read(struct mcp9808_data *data, uint8_t reg, uint16_t *val) { int ret; @@ -93,6 +86,8 @@ static int mcp9808_channel_get(struct device *dev, static struct sensor_driver_api mcp9808_api_funcs = { .sample_fetch = mcp9808_sample_fetch, .channel_get = mcp9808_channel_get, + .attr_set = mcp9808_attr_set, + .trigger_set = mcp9808_trigger_set, }; int mcp9808_init(struct device *dev) @@ -110,10 +105,10 @@ int mcp9808_init(struct device *dev) data->i2c_slave_addr = CONFIG_MCP9808_I2C_ADDR; + mcp9808_setup_interrupt(dev); + return DEV_OK; } -static struct mcp9808_data mcp9808_data; - DEVICE_INIT(mcp9808, CONFIG_MCP9808_DEV_NAME, mcp9808_init, &mcp9808_data, NULL, SECONDARY, CONFIG_MCP9808_INIT_PRIORITY); diff --git a/drivers/sensor/sensor_mcp9808.h b/drivers/sensor/sensor_mcp9808.h index a4659d47771..47e96675d75 100644 --- a/drivers/sensor/sensor_mcp9808.h +++ b/drivers/sensor/sensor_mcp9808.h @@ -19,20 +19,82 @@ #include #include +#include #include +#ifndef CONFIG_SENSOR_DEBUG +#define DBG(...) { ; } +#else +#include +#define DBG printk +#endif /* CONFIG_SENSOR_DEBUG */ + +#define MCP9808_REG_CONFIG 0x01 +#define MCP9808_REG_UPPER_LIMIT 0x02 +#define MCP9808_REG_LOWER_LIMIT 0x03 +#define MCP9808_REG_CRITICAL 0x04 #define MCP9808_REG_TEMP_AMB 0x05 +#define MCP9808_ALERT_INT BIT(0) +#define MCP9808_ALERT_CNT BIT(3) +#define MCP9808_INT_CLEAR BIT(5) + #define MCP9808_SIGN_BIT BIT(12) #define MCP9808_TEMP_INT_MASK 0x0ff0 #define MCP9808_TEMP_INT_SHIFT 4 #define MCP9808_TEMP_FRAC_MASK 0x000f +#define MCP9808_TEMP_MAX 0xffc + struct mcp9808_data { struct device *i2c_master; uint16_t i2c_slave_addr; uint16_t reg_val; + +#ifdef CONFIG_MCP9808_TRIGGER_OWN_FIBER + struct nano_sem sem; +#endif + +#ifdef CONFIG_MCP9808_TRIGGER_GLOBAL_FIBER + struct sensor_work work; +#endif + +#ifdef CONFIG_MCP9808_TRIGGER + struct sensor_trigger trig; + sensor_trigger_handler_t trigger_handler; +#endif }; +int mcp9808_reg_read(struct mcp9808_data *data, uint8_t reg, uint16_t *val); + +#ifdef CONFIG_MCP9808_TRIGGER +int mcp9808_attr_set(struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val); +int mcp9808_trigger_set(struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler); +void mcp9808_setup_interrupt(struct device *dev); +#else +static inline int mcp9808_attr_set(struct device *dev, + enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + return DEV_INVALID_OP; +} + +static inline int mcp9808_trigger_set(struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + return DEV_INVALID_OP; +} + +static void mcp9808_setup_interrupt(struct device *dev) +{ +} +#endif /* CONFIG_MCP9808_TRIGGER */ + #endif /* __SENSOR_MCP9808_H__ */ diff --git a/drivers/sensor/sensor_mcp9808_trigger.c b/drivers/sensor/sensor_mcp9808_trigger.c new file mode 100644 index 00000000000..dda4d098e94 --- /dev/null +++ b/drivers/sensor/sensor_mcp9808_trigger.c @@ -0,0 +1,201 @@ +/* sensor_mcp9808_trigger.c - Trigger support for MCP9808 */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include "sensor_mcp9808.h" + +extern struct mcp9808_data mcp9808_data; + +static int mcp9808_reg_write(struct mcp9808_data *data, uint8_t reg, uint16_t val) +{ + uint16_t be_val = sys_cpu_to_be16(val); + + struct i2c_msg msgs[2] = { + { + .buf = ®, + .len = 1, + .flags = I2C_MSG_WRITE | I2C_MSG_RESTART, + }, + { + .buf = (uint8_t *) &be_val, + .len = 2, + .flags = I2C_MSG_WRITE | I2C_MSG_STOP, + }, + }; + + return i2c_transfer(data->i2c_master, msgs, 2, data->i2c_slave_addr); +} + +static int mcp9808_reg_update(struct mcp9808_data *data, uint8_t reg, + uint16_t mask, uint16_t val) +{ + int ret; + uint16_t old_val, new_val; + + ret = mcp9808_reg_read(data, reg, &old_val); + if (ret) { + return ret; + } + + new_val = old_val & ~mask; + new_val |= val; + + if (new_val == old_val) { + return DEV_OK; + } + + return mcp9808_reg_write(data, reg, new_val); +} + +int mcp9808_attr_set(struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + struct mcp9808_data *data = dev->driver_data; + uint16_t reg_val = 0; + uint8_t reg_addr; + int32_t val2; + +#ifdef CONFIG_SENSOR_DEBUG + if (chan != SENSOR_CHAN_TEMP) { + return DEV_INVALID_CONF; + } +#endif + + switch (val->type) { + case SENSOR_TYPE_INT_PLUS_MICRO: + val2 = val->val2; + while (val2 > 0) { + reg_val += (1 << 2); + val2 -= 250000; + } + /* Fall through. */ + case SENSOR_TYPE_INT: + reg_val |= val->val1 << 4; + break; + default: + return DEV_INVALID_CONF; + } + + switch (attr) { + case SENSOR_ATTR_LOWER_THRESH: + reg_addr = MCP9808_REG_LOWER_LIMIT; + break; + case SENSOR_ATTR_UPPER_THRESH: + reg_addr = MCP9808_REG_UPPER_LIMIT; + break; + default: + return DEV_INVALID_CONF; + } + + return mcp9808_reg_write(data, reg_addr, reg_val); +} + +int mcp9808_trigger_set(struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + struct mcp9808_data *data = dev->driver_data; + + data->trig = *trig; + data->trigger_handler = handler; + + return mcp9808_reg_update(data, MCP9808_REG_CONFIG, MCP9808_ALERT_CNT, + handler == NULL ? 0 : MCP9808_ALERT_CNT); +} + +#ifdef CONFIG_MCP9808_TRIGGER_OWN_FIBER + +static void mcp9808_gpio_cb(struct device *dev, uint32_t pin) +{ + struct mcp9808_data *data = &mcp9808_data; + + nano_isr_sem_give(&data->sem); +} + +static void mcp9808_fiber_main(int arg1, int arg2) +{ + struct device *dev = INT_TO_POINTER(arg1); + struct mcp9808_data *data = dev->driver_data; + + ARG_UNUSED(arg2); + + while (1) { + nano_fiber_sem_take(&data->sem, TICKS_UNLIMITED); + data->trigger_handler(dev, &data->trig); + mcp9808_reg_update(data, MCP9808_REG_CONFIG, + MCP9808_INT_CLEAR, MCP9808_INT_CLEAR); + } +} + +static char __stack mcp9808_fiber_stack[CONFIG_MCP9808_FIBER_STACK_SIZE]; + +#else /* CONFIG_MCP9808_TRIGGER_GLOBAL_FIBER */ + +static void mcp9808_gpio_cb(struct device *dev, uint32_t pin) +{ + struct mcp9808_data *data = &mcp9808_data; + + nano_isr_fifo_put(sensor_get_work_fifo(), &data->work); +} + +static void mcp9808_gpio_fiber_cb(void *arg) +{ + struct device *dev = arg; + struct mcp9808_data *data = dev->driver_data; + + data->trigger_handler(dev, &data->trig); + mcp9808_reg_update(data, MCP9808_REG_CONFIG, + MCP9808_INT_CLEAR, MCP9808_INT_CLEAR); +} + +#endif /* CONFIG_MCP9808_TRIGGER_GLOBAL_FIBER */ + +void mcp9808_setup_interrupt(struct device *dev) +{ + struct mcp9808_data *data = dev->driver_data; + struct device *gpio; + + mcp9808_reg_update(data, MCP9808_REG_CONFIG, MCP9808_ALERT_INT, + MCP9808_ALERT_INT); + mcp9808_reg_write(data, MCP9808_REG_CRITICAL, MCP9808_TEMP_MAX); + mcp9808_reg_update(data, MCP9808_REG_CONFIG, MCP9808_INT_CLEAR, + MCP9808_INT_CLEAR); + +#ifdef CONFIG_MCP9808_TRIGGER_OWN_FIBER + nano_sem_init(&data->sem); + + fiber_fiber_start(mcp9808_fiber_stack, + CONFIG_MCP9808_FIBER_STACK_SIZE, + mcp9808_fiber_main, POINTER_TO_INT(dev), 0, + CONFIG_MCP9808_FIBER_PRIORITY, 0); +#else /* CONFIG_MCP9808_TRIGGER_GLOBAL_FIBER */ + data->work.handler = mcp9808_gpio_fiber_cb; + data->work.arg = dev; +#endif + + gpio = device_get_binding(CONFIG_MCP9808_GPIO_CONTROLLER); + gpio_pin_configure(gpio, CONFIG_MCP9808_GPIO_PIN, + GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE | + GPIO_INT_ACTIVE_LOW | GPIO_INT_DEBOUNCE); + gpio_set_callback(gpio, mcp9808_gpio_cb); + gpio_pin_enable_callback(gpio, CONFIG_MCP9808_GPIO_PIN); +} diff --git a/samples/sensor/mcp9808/prj.conf b/samples/sensor/mcp9808/prj.conf index 6f179b4df58..a352a2c121b 100644 --- a/samples/sensor/mcp9808/prj.conf +++ b/samples/sensor/mcp9808/prj.conf @@ -4,8 +4,11 @@ CONFIG_I2C=y CONFIG_I2C_QUARK_SE_SS=y CONFIG_I2C_QUARK_SE_SS_0=y +CONFIG_GPIO=y + CONFIG_NANO_TIMEOUTS=y CONFIG_SENSOR=y CONFIG_SENSOR_DEBUG=y CONFIG_MCP9808=y +CONFIG_MCP9808_TRIGGER_GLOBAL=y diff --git a/samples/sensor/mcp9808/src/main.c b/samples/sensor/mcp9808/src/main.c index 0b17ab8ec58..8275689e245 100644 --- a/samples/sensor/mcp9808/src/main.c +++ b/samples/sensor/mcp9808/src/main.c @@ -28,6 +28,18 @@ #define PRINT printk #endif +#ifdef CONFIG_MCP9808_TRIGGER +static void trigger_handler(struct device *dev, struct sensor_trigger *trig) +{ + struct sensor_value temp; + + sensor_sample_fetch(dev); + sensor_channel_get(dev, SENSOR_CHAN_TEMP, &temp); + + PRINT("trigger fired, temp %d.%06d\n", temp.val1, temp.val2); +} +#endif + void main(void) { struct device *dev = device_get_binding("MCP9808"); @@ -42,6 +54,22 @@ void main(void) PRINT("dev %p name %s\n", dev, dev->config->name); #endif +#ifdef CONFIG_MCP9808_TRIGGER + struct sensor_value val; + struct sensor_trigger trig; + + val.type = SENSOR_TYPE_INT; + val.val1 = 26; + + sensor_attr_set(dev, SENSOR_CHAN_TEMP, + SENSOR_ATTR_UPPER_THRESH, &val); + + trig.type = SENSOR_TRIG_THRESHOLD; + trig.chan = SENSOR_CHAN_TEMP; + + sensor_trigger_set(dev, &trig, trigger_handler); +#endif + while (1) { struct sensor_value temp;