| /* |
| * Copyright (c) 2015 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 <init.h> |
| #include <clock_control.h> |
| #include "wdt_dw.h" |
| |
| #ifdef WDT_DW_INT_MASK |
| static inline void _wdt_dw_int_unmask(void) |
| { |
| sys_write32(sys_read32(WDT_DW_INT_MASK) & INT_UNMASK_IA, |
| WDT_DW_INT_MASK); |
| } |
| #else |
| #define _wdt_dw_int_unmask() |
| #endif |
| |
| #ifdef CONFIG_WDT_DW_CLOCK_GATE |
| static inline void _wdt_dw_clock_config(struct device *dev) |
| { |
| char *drv = CONFIG_WDT_DW_CLOCK_GATE_DRV_NAME; |
| struct device *clk; |
| |
| clk = device_get_binding(drv); |
| if (clk) { |
| struct wdt_dw_runtime *context = dev->driver_data; |
| |
| context->clock = clk; |
| } |
| } |
| |
| static inline void _wdt_dw_clock_on(struct device *dev) |
| { |
| struct wdt_dw_dev_config *config = dev->config->config_info; |
| struct wdt_dw_runtime *context = dev->driver_data; |
| |
| clock_control_on(context->clock, config->clock_data); |
| } |
| |
| static inline void _wdt_dw_clock_off(struct device *dev) |
| { |
| struct wdt_dw_dev_config *config = dev->config->config_info; |
| struct wdt_dw_runtime *context = dev->driver_data; |
| |
| clock_control_off(context->clock, config->clock_data); |
| } |
| #else |
| #define _wdt_dw_clock_config(...) |
| #define _wdt_dw_clock_on(...) |
| #define _wdt_dw_clock_off(...) |
| #endif |
| |
| /** |
| * Enables the clock for the peripheral watchdog |
| */ |
| static void wdt_dw_enable(struct device *dev) |
| { |
| _wdt_dw_clock_on(dev); |
| |
| #if defined(CONFIG_SOC_QUARK_SE) || defined(CONFIG_SOC_QUARK_D2000) |
| sys_set_bit(SCSS_PERIPHERAL_BASE + SCSS_PERIPH_CFG0, 1); |
| #endif |
| } |
| |
| static void wdt_dw_disable(struct device *dev) |
| { |
| _wdt_dw_clock_off(dev); |
| |
| #if defined(CONFIG_SOC_QUARK_SE) || defined(CONFIG_SOC_QUARK_D2000) |
| sys_clear_bit(SCSS_PERIPHERAL_BASE + SCSS_PERIPH_CFG0, 1); |
| #endif |
| } |
| |
| void wdt_dw_isr(void *arg) |
| { |
| struct device *dev = arg; |
| struct wdt_dw_runtime *context = dev->driver_data; |
| |
| if (context->cb_fn) { |
| context->cb_fn(dev); |
| } |
| } |
| |
| static void wdt_dw_get_config(struct device *dev, struct wdt_config *config) |
| { |
| struct wdt_dw_dev_config *wdt_dev = dev->config->config_info; |
| struct wdt_dw_runtime *context = dev->driver_data; |
| |
| config->timeout = sys_read32(wdt_dev->base_address + WDT_TORR) & |
| WDT_TIMEOUT_MASK; |
| config->mode = (sys_read32(wdt_dev->base_address + WDT_CR) & WDT_MODE) |
| >> WDT_MODE_OFFSET; |
| config->interrupt_fn = context->cb_fn; |
| } |
| |
| static void wdt_dw_reload(struct device *dev) |
| { |
| struct wdt_dw_dev_config *wdt_dev = dev->config->config_info; |
| |
| sys_write32(WDT_CRR_VAL, wdt_dev->base_address + WDT_CRR); |
| } |
| |
| static int wdt_dw_set_config(struct device *dev, struct wdt_config *config) |
| { |
| struct wdt_dw_dev_config *wdt_dev = dev->config->config_info; |
| struct wdt_dw_runtime *context = dev->driver_data; |
| |
| sys_write32(config->timeout, wdt_dev->base_address + WDT_TORR); |
| |
| /* Set response mode */ |
| if (WDT_MODE_RESET == config->mode) { |
| sys_clear_bit(wdt_dev->base_address + WDT_CR, 1); |
| } else { |
| if (!config->interrupt_fn) { |
| return -EIO; |
| } |
| |
| context->cb_fn = config->interrupt_fn; |
| sys_set_bit(wdt_dev->base_address + WDT_CR, 1); |
| } |
| |
| /* Enable WDT, cannot be disabled until soc reset */ |
| sys_set_bit(wdt_dev->base_address + WDT_CR, 0); |
| |
| wdt_dw_reload(dev); |
| |
| return 0; |
| } |
| |
| static struct wdt_driver_api wdt_dw_funcs = { |
| .set_config = wdt_dw_set_config, |
| .get_config = wdt_dw_get_config, |
| .enable = wdt_dw_enable, |
| .disable = wdt_dw_disable, |
| .reload = wdt_dw_reload, |
| }; |
| |
| int wdt_dw_init(struct device *dev); |
| |
| struct wdt_dw_runtime wdt_runtime; |
| |
| struct wdt_dw_dev_config wdt_dev = { |
| .base_address = CONFIG_WDT_DW_BASE_ADDR, |
| #ifdef CONFIG_WDT_DW_CLOCK_GATE |
| .clock_data = UINT_TO_POINTER(CONFIG_WDT_DW_CLOCK_GATE_SUBSYS), |
| #endif |
| }; |
| |
| DEVICE_INIT(wdt, CONFIG_WDT_DW_DRV_NAME, &wdt_dw_init, |
| &wdt_runtime, &wdt_dev, |
| SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); |
| |
| int wdt_dw_init(struct device *dev) |
| { |
| dev->driver_api = &wdt_dw_funcs; |
| |
| IRQ_CONNECT(CONFIG_WDT_DW_IRQ, CONFIG_WDT_DW_IRQ_PRI, wdt_dw_isr, |
| DEVICE_GET(wdt), 0); |
| irq_enable(CONFIG_WDT_DW_IRQ); |
| |
| _wdt_dw_int_unmask(); |
| |
| _wdt_dw_clock_config(dev); |
| |
| return 0; |
| } |
| |