| /* 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 <errno.h> |
| |
| #include <nanokernel.h> |
| #include <i2c.h> |
| #include <gpio.h> |
| #include <misc/byteorder.h> |
| #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); |
| } |