/* * 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 #include #include #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 DEV_FAIL; } 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 DEV_OK; } 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; }