static int kempld_wdt_set_stage_action(struct kempld_wdt_data *wdt_data, struct kempld_wdt_stage *stage, u8 action) { struct kempld_device_data *pld = wdt_data->pld; u8 stage_cfg; if (!stage || !stage->mask) return -EINVAL; kempld_get_mutex(pld); stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id)); stage_cfg &= ~STAGE_CFG_ACTION_MASK; stage_cfg |= (action & STAGE_CFG_ACTION_MASK); if (action == ACTION_RESET) stage_cfg |= STAGE_CFG_ASSERT; else stage_cfg &= ~STAGE_CFG_ASSERT; kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg); kempld_release_mutex(pld); return 0; }
static int kempld_get_info_generic(struct kempld_device_data *pld) { u16 version; u8 spec; kempld_get_mutex(pld); version = kempld_read16(pld, KEMPLD_VERSION); spec = kempld_read8(pld, KEMPLD_SPEC); pld->info.buildnr = kempld_read16(pld, KEMPLD_BUILDNR); pld->info.minor = KEMPLD_VERSION_GET_MINOR(version); pld->info.major = KEMPLD_VERSION_GET_MAJOR(version); pld->info.number = KEMPLD_VERSION_GET_NUMBER(version); pld->info.type = KEMPLD_VERSION_GET_TYPE(version); if (spec == 0xff) { pld->info.spec_minor = 0; pld->info.spec_major = 1; } else { pld->info.spec_minor = KEMPLD_SPEC_GET_MINOR(spec); pld->info.spec_major = KEMPLD_SPEC_GET_MAJOR(spec); } if (pld->info.spec_major > 0) pld->feature_mask = kempld_read16(pld, KEMPLD_FEATURE); else pld->feature_mask = 0; kempld_release_mutex(pld); return 0; }
static int kempld_wdt_set_stage_timeout(struct kempld_wdt_data *wdt_data, struct kempld_wdt_stage *stage, unsigned int timeout) { struct kempld_device_data *pld = wdt_data->pld; u32 prescaler = kempld_prescaler[PRESCALER_21]; u64 stage_timeout64; u32 stage_timeout; u32 remainder; u8 stage_cfg; if (!stage) return -EINVAL; stage_timeout64 = (u64)timeout * pld->pld_clock; remainder = do_div(stage_timeout64, prescaler); if (remainder) stage_timeout64++; if (stage_timeout64 > stage->mask) return -EINVAL; stage_timeout = stage_timeout64 & stage->mask; kempld_get_mutex(pld); stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id)); stage_cfg &= ~STAGE_CFG_PRESCALER_MASK; stage_cfg |= STAGE_CFG_SET_PRESCALER(PRESCALER_21); kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg); kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id), stage_timeout); kempld_release_mutex(pld); return 0; }
void kempld_enable_uart_for_console(void) { if (!CONFIG(CONSOLE_SERIAL)) return; if (kempld_get_mutex(100) < 0) return; switch (CONFIG_UART_FOR_CONSOLE) { case 0: kempld_write8(KEMPLD_UART_0, KEMPLD_UART_ENABLE | KEMPLD_UART_3F8 << KEMPLD_UART_IO_SHIFT); break; case 1: kempld_write8(KEMPLD_UART_1, KEMPLD_UART_ENABLE | KEMPLD_UART_2F8 << KEMPLD_UART_IO_SHIFT); break; default: break; } kempld_release_mutex(); }
static int kempld_wdt_probe(struct platform_device *pdev) { struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent); struct kempld_wdt_data *wdt_data; struct device *dev = &pdev->dev; struct watchdog_device *wdd; u8 status; int ret = 0; wdt_data = devm_kzalloc(dev, sizeof(*wdt_data), GFP_KERNEL); if (!wdt_data) return -ENOMEM; wdt_data->pld = pld; wdd = &wdt_data->wdd; wdd->parent = dev; kempld_get_mutex(pld); status = kempld_read8(pld, KEMPLD_WDT_CFG); kempld_release_mutex(pld); /* Enable nowayout if watchdog is already locked */ if (status & (KEMPLD_WDT_CFG_ENABLE_LOCK | KEMPLD_WDT_CFG_GLOBAL_LOCK)) { if (!nowayout) dev_warn(dev, "Forcing nowayout - watchdog lock enabled!\n"); nowayout = true; } wdd->info = &kempld_wdt_info; wdd->ops = &kempld_wdt_ops; watchdog_set_drvdata(wdd, wdt_data); watchdog_set_nowayout(wdd, nowayout); ret = kempld_wdt_probe_stages(wdd); if (ret) return ret; kempld_wdt_set_timeout(wdd, timeout); kempld_wdt_set_pretimeout(wdd, pretimeout); /* Check if watchdog is already enabled */ if (status & KEMPLD_WDT_CFG_ENABLE) { /* Get current watchdog settings */ kempld_wdt_update_timeouts(wdt_data); dev_info(dev, "Watchdog was already enabled\n"); } platform_set_drvdata(pdev, wdt_data); ret = watchdog_register_device(wdd); if (ret) return ret; dev_info(dev, "Watchdog registered with %ds timeout\n", wdd->timeout); return 0; }
static void kempld_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct kempld_gpio_data *gpio = container_of(chip, struct kempld_gpio_data, chip); struct kempld_device_data *pld = gpio->pld; kempld_get_mutex(pld); kempld_gpio_bitop(pld, KEMPLD_GPIO_LVL_NUM(offset), offset, value); kempld_release_mutex(pld); }
static int kempld_gpio_get_bit(struct kempld_device_data *pld, u8 reg, u8 bit) { u8 status; kempld_get_mutex(pld); status = kempld_read8(pld, reg); kempld_release_mutex(pld); return !!(status & KEMPLD_GPIO_MASK(bit)); }
static int kempld_wdt_probe_stages(struct watchdog_device *wdd) { struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); struct kempld_device_data *pld = wdt_data->pld; struct kempld_wdt_stage *pretimeout_stage; struct kempld_wdt_stage *timeout_stage; u8 index, data, data_orig; u32 mask; int i, j; pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT]; timeout_stage = &wdt_data->stage[STAGE_TIMEOUT]; pretimeout_stage->mask = 0; timeout_stage->mask = 0; for (i = 0; i < 3; i++) { index = KEMPLD_WDT_STAGE_TIMEOUT(i); mask = 0; kempld_get_mutex(pld); /* Probe each byte individually. */ for (j = 0; j < 4; j++) { data_orig = kempld_read8(pld, index + j); kempld_write8(pld, index + j, 0x00); data = kempld_read8(pld, index + j); /* A failed write means this byte is reserved */ if (data != 0x00) break; kempld_write8(pld, index + j, data_orig); mask |= 0xff << (j * 8); } kempld_release_mutex(pld); /* Assign available stages to timeout and pretimeout */ if (!timeout_stage->mask) { timeout_stage->mask = mask; timeout_stage->id = i; } else { if (pld->feature_mask & KEMPLD_FEATURE_BIT_NMI) { pretimeout_stage->mask = timeout_stage->mask; timeout_stage->mask = mask; pretimeout_stage->id = timeout_stage->id; timeout_stage->id = i; } break; } } if (!timeout_stage->mask) return -ENODEV; return 0; }
static int kempld_wdt_keepalive(struct watchdog_device *wdd) { struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); struct kempld_device_data *pld = wdt_data->pld; kempld_get_mutex(pld); kempld_write8(pld, KEMPLD_WDT_KICK, 'K'); kempld_release_mutex(pld); return 0; }
static int kempld_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { struct kempld_gpio_data *gpio = container_of(chip, struct kempld_gpio_data, chip); struct kempld_device_data *pld = gpio->pld; kempld_get_mutex(pld); kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR_NUM(offset), offset, 0); kempld_release_mutex(pld); return 0; }
/* Disable watchdog if it is active during suspend */ static int kempld_wdt_suspend(struct platform_device *pdev, pm_message_t message) { struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev); struct kempld_device_data *pld = wdt_data->pld; struct watchdog_device *wdd = &wdt_data->wdd; kempld_get_mutex(pld); wdt_data->pm_status_store = kempld_read8(pld, KEMPLD_WDT_CFG); kempld_release_mutex(pld); kempld_wdt_update_timeouts(wdt_data); if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE) return kempld_wdt_stop(wdd); return 0; }
static int kempld_wdt_stop(struct watchdog_device *wdd) { struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); struct kempld_device_data *pld = wdt_data->pld; u8 status; kempld_get_mutex(pld); status = kempld_read8(pld, KEMPLD_WDT_CFG); status &= ~KEMPLD_WDT_CFG_ENABLE; kempld_write8(pld, KEMPLD_WDT_CFG, status); status = kempld_read8(pld, KEMPLD_WDT_CFG); kempld_release_mutex(pld); /* Check if the watchdog was disabled */ if (status & KEMPLD_WDT_CFG_ENABLE) return -EACCES; return 0; }
static int kempld_gpio_pincount(struct kempld_device_data *pld) { u16 evt, evt_back; kempld_get_mutex(pld); /* Backup event register as it might be already initialized */ evt_back = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE); /* Clear event register */ kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, 0x0000); /* Read back event register */ evt = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE); /* Restore event register */ kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, evt_back); kempld_release_mutex(pld); return evt ? __ffs(evt) : 16; }
static void kempld_wdt_update_timeouts(struct kempld_wdt_data *wdt_data) { struct kempld_device_data *pld = wdt_data->pld; struct kempld_wdt_stage *pretimeout_stage; struct kempld_wdt_stage *timeout_stage; unsigned int pretimeout, timeout; pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT]; timeout_stage = &wdt_data->stage[STAGE_TIMEOUT]; kempld_get_mutex(pld); pretimeout = kempld_wdt_get_timeout(wdt_data, pretimeout_stage); timeout = kempld_wdt_get_timeout(wdt_data, timeout_stage); kempld_release_mutex(pld); if (pretimeout) wdt_data->pretimeout = timeout; else wdt_data->pretimeout = 0; wdt_data->wdd.timeout = pretimeout + timeout; }
static int kempld_wdt_start(struct watchdog_device *wdd) { struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); struct kempld_device_data *pld = wdt_data->pld; u8 status; int ret; ret = kempld_wdt_set_timeout(wdd, wdd->timeout); if (ret) return ret; kempld_get_mutex(pld); status = kempld_read8(pld, KEMPLD_WDT_CFG); status |= KEMPLD_WDT_CFG_ENABLE; kempld_write8(pld, KEMPLD_WDT_CFG, status); status = kempld_read8(pld, KEMPLD_WDT_CFG); kempld_release_mutex(pld); /* Check if the watchdog was enabled */ if (!(status & KEMPLD_WDT_CFG_ENABLE)) return -EACCES; return 0; }