/* 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 #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 0; } 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 -EINVAL; } #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 -EINVAL; } 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 -EINVAL; } 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); }