// SPDX-License-Identifier: GPL-2.0 // // adsp_awake.c -- Mediatek ADSP awake control // // Copyright (c) 2018 MediaTek Inc. // Author: Scholar Chang #include /* needed by all modules */ #include /* needed by module macros */ #include /* needed by file_operations* */ #include /* needed by miscdevice* */ #include #include #include /* needed by device_* */ #include /* needed by vmalloc */ #include /* needed by copy_to_user */ #include /* needed by file_operations* */ #include /* needed by kmalloc */ #include /* needed by poll */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "adsp_feature_define.h" #include "adsp_ipi.h" #include "adsp_helper.h" #include "adsp_excep.h" #include "adsp_dvfs.h" #define AP_AWAKE_STATE_NORMAL (0) #define AP_AWAKE_STATE_FORCE_LOCK (1) #define AP_AWAKE_STATE_FORCE_UNLOCK (2) #define ADSP_SW_INT1_BIT (0x1 << ADSP_SW_INT1) #define AP_AWAKE_LOCK_MASK (0x1 << AP_AWAKE_LOCK_BIT) #define AP_AWAKE_UNLOCK_MASK (0x1 << AP_AWAKE_UNLOCK_BIT) #define AP_AWAKE_DUMP_MASK (0x1 << AP_AWAKE_DUMP_BIT) #define AP_AWAKE_UPDATE_MASK (0x1 << AP_AWAKE_UPDATE_BIT) #define AP_AWAKE_STATE_MASK (0x3 << AP_AWAKE_STATE_BIT) #define ADSPPLL_UNLOCK_MASK (0x1 << ADSPPLL_UNLOCK_BIT) #define REG_AP_AWAKE (readl(ADSP_A_AP_AWAKE)) #define CHECK_AP_AWAKE_MASK(mask) ((REG_AP_AWAKE & mask) == 0) #define GET_AP_AWAKE_STATE() \ ((REG_AP_AWAKE & AP_AWAKE_STATE_MASK) >> AP_AWAKE_STATE_BIT) #define SET_AP_AWAKE_STATE(reg_val, state) \ ((reg_val & ~AP_AWAKE_STATE_MASK) | (state << AP_AWAKE_STATE_BIT)) struct mutex adsp_awake_mutexs[ADSP_CORE_TOTAL]; int adsp_awake_counts[ADSP_CORE_TOTAL]; DEFINE_SPINLOCK(adsp_awake_spinlock); static int adsp_awake_send_swint (int ret, uint32_t reg_val, uint32_t mask) { writel(reg_val | mask, ADSP_A_AP_AWAKE); writel(ADSP_SW_INT1_BIT, ADSP_SWINT_REG); { /* Wait for ADSP response */ uint32_t cnt = 0; do { if (CHECK_AP_AWAKE_MASK(mask)) { ret = 0; break; } udelay(10); cnt++; } while (cnt != ADSP_AWAKE_TIMEOUT); } return ret; } /* * acquire adsp lock flag, keep adsp awake * @param adsp_id: adsp core id * return 0 : get lock success * non-0 : get lock fail */ int adsp_awake_lock(enum adsp_core_id adsp_id) { if (adsp_id >= ADSP_CORE_TOTAL) { pr_err("%s() ID %d >= CORE TOTAL", __func__, adsp_id); return -EINVAL; } if (is_adsp_ready(adsp_id) == 0) { char *p_id = adsp_core_ids[adsp_id]; pr_warn("%s() %s not enabled\n", __func__, p_id); return -1; } { /* Protected by spin lock */ int *p_count = (int *)&adsp_awake_counts[adsp_id]; unsigned long spin_flags = 0; spinlock_t *p_spin = &adsp_awake_spinlock; spin_lock_irqsave(p_spin, spin_flags); if (*p_count > 0) { *p_count += 1; spin_unlock_irqrestore(p_spin, spin_flags); return 0; } spin_unlock_irqrestore(p_spin, spin_flags); } { /* Protected by mutex lock & spin lock */ int ret = -1; int *p_count = (int *)&adsp_awake_counts[adsp_id]; unsigned long spin_flags = 0; spinlock_t *p_spin = &adsp_awake_spinlock; struct mutex *p_mutex = &adsp_awake_mutexs[adsp_id]; uint32_t reg_val, mask; mutex_lock(p_mutex); spin_lock_irqsave(p_spin, spin_flags); reg_val = REG_AP_AWAKE; mask = AP_AWAKE_LOCK_MASK; ret = adsp_awake_send_swint (ret, reg_val, mask); if (ret != -1) *p_count += 1; spin_unlock_irqrestore(p_spin, spin_flags); if (ret == -1) { char *p_id = adsp_core_ids[adsp_id]; pr_err("%s() %s fail\n", __func__, p_id); WARN_ON(1); } mutex_unlock(p_mutex); return ret; } } EXPORT_SYMBOL_GPL(adsp_awake_lock); /* * release adsp awake lock flag * @param adsp_id: adsp core id * return 0 : release lock success * non-0 : release lock fail */ int adsp_awake_unlock(enum adsp_core_id adsp_id) { if (adsp_id >= ADSP_CORE_TOTAL) { pr_err("%s() ID %d >= CORE TOTAL", __func__, adsp_id); return -EINVAL; } if (is_adsp_ready(adsp_id) == 0) { char *p_id = adsp_core_ids[adsp_id]; pr_warn("%s() %s not enabled\n", __func__, p_id); return -1; } { /* Protected by spin lock */ int *p_count = (int *)&adsp_awake_counts[adsp_id]; unsigned long spin_flags = 0; spinlock_t *p_spin = &adsp_awake_spinlock; spin_lock_irqsave(p_spin, spin_flags); if (*p_count > 1) { *p_count -= 1; spin_unlock_irqrestore(p_spin, spin_flags); return 0; } spin_unlock_irqrestore(p_spin, spin_flags); } { /* Protected by mutex lock & spin lock */ int ret = -1; int *p_count = (int *)&adsp_awake_counts[adsp_id]; unsigned long spin_flags = 0; spinlock_t *p_spin = &adsp_awake_spinlock; struct mutex *p_mutex = &adsp_awake_mutexs[adsp_id]; uint32_t reg_val, mask; mutex_lock(p_mutex); spin_lock_irqsave(p_spin, spin_flags); reg_val = REG_AP_AWAKE; mask = AP_AWAKE_UNLOCK_MASK; ret = adsp_awake_send_swint (ret, reg_val, mask); if (ret != -1) *p_count -= 1; spin_unlock_irqrestore(p_spin, spin_flags); if (*p_count < 0) pr_err("%s() count = %d NOT SYNC\n", __func__, *p_count); mutex_unlock(p_mutex); return ret; } } EXPORT_SYMBOL_GPL(adsp_awake_unlock); void adsp_awake_init(void) { uint32_t i = 0; for (i = 0; i < ADSP_CORE_TOTAL; i++) adsp_awake_counts[i] = 0; for (i = 0; i < ADSP_CORE_TOTAL; i++) mutex_init(&adsp_awake_mutexs[i]); } /* * lock/unlock switch of adsppll clock * @param adsp_id: adsp core id * unlock: if unlock adsppll * return 0 : send command success * non-0 : adsp is not ready */ int adsp_awake_unlock_adsppll(enum adsp_core_id adsp_id, uint32_t unlock) { if (adsp_id >= ADSP_CORE_TOTAL) { pr_err("%s() ID %d >= CORE TOTAL", __func__, adsp_id); return -EINVAL; } if (is_adsp_ready(adsp_id) == 0) { char *p_id = adsp_core_ids[adsp_id]; pr_warn("%s() %s not enabled\n", __func__, p_id); return -1; } { /* Protected by mutex lock & spin lock */ int ret = 0; unsigned long spin_flags = 0; spinlock_t *p_spin = &adsp_awake_spinlock; struct mutex *p_mutex = &adsp_awake_mutexs[adsp_id]; uint32_t reg_val; mutex_lock(p_mutex); spin_lock_irqsave(p_spin, spin_flags); reg_val = REG_AP_AWAKE; if (unlock) reg_val |= ADSPPLL_UNLOCK_MASK; else reg_val &= ~ADSPPLL_UNLOCK_MASK; writel(reg_val, ADSP_A_AP_AWAKE); writel(ADSP_SW_INT1_BIT, ADSP_SWINT_REG); spin_unlock_irqrestore(p_spin, spin_flags); mutex_unlock(p_mutex); return ret; } } EXPORT_SYMBOL_GPL(adsp_awake_unlock_adsppll); /* * dump adsp lock list in adsp log * @param adsp_id: adsp core id * return 0 : dump list success * non-0 : dump list fail */ int adsp_awake_dump_list(enum adsp_core_id adsp_id) { if (adsp_id >= ADSP_CORE_TOTAL) { pr_err("%s() ID %d >= CORE TOTAL", __func__, adsp_id); return -EINVAL; } if (is_adsp_ready(adsp_id) == 0) { char *p_id = adsp_core_ids[adsp_id]; pr_warn("%s() %s not enabled\n", __func__, p_id); return -1; } { /* Protected by mutex lock & spin lock */ int ret = -1; unsigned long spin_flags = 0; spinlock_t *p_spin = &adsp_awake_spinlock; struct mutex *p_mutex = &adsp_awake_mutexs[adsp_id]; uint32_t reg_val, mask; mutex_lock(p_mutex); spin_lock_irqsave(p_spin, spin_flags); reg_val = REG_AP_AWAKE; mask = AP_AWAKE_DUMP_MASK; ret = adsp_awake_send_swint (ret, reg_val, mask); spin_unlock_irqrestore(p_spin, spin_flags); if (ret == -1) { char *p_id = adsp_core_ids[adsp_id]; pr_err("%s() %s fail\n", __func__, p_id); WARN_ON(1); } mutex_unlock(p_mutex); return ret; } } EXPORT_SYMBOL_GPL(adsp_awake_dump_list); static int adsp_awake_set_state(enum adsp_core_id adsp_id, uint32_t next_state) { if (adsp_id >= ADSP_CORE_TOTAL) { pr_err("%s() ID %d >= ADSP_CORE_TOTAL", __func__, adsp_id); return -EINVAL; } if (is_adsp_ready(adsp_id) == 0) { char *p_id = adsp_core_ids[adsp_id]; pr_warn("%s() %s not enabled\n", __func__, p_id); return -1; } { /* Protected by spin lock */ unsigned long spin_flags = 0; spinlock_t *p_spin = &adsp_awake_spinlock; uint32_t curr_state = GET_AP_AWAKE_STATE(); spin_lock_irqsave(p_spin, spin_flags); if (curr_state == next_state) { spin_unlock_irqrestore(p_spin, spin_flags); return 0; } spin_unlock_irqrestore(p_spin, spin_flags); } { /* Protected by mutex lock & spin lock */ int ret = -1; unsigned long spin_flags = 0; spinlock_t *p_spin = &adsp_awake_spinlock; struct mutex *p_mutex = &adsp_awake_mutexs[adsp_id]; uint32_t reg_val, mask; mutex_lock(p_mutex); spin_lock_irqsave(p_spin, spin_flags); reg_val = REG_AP_AWAKE; mask = AP_AWAKE_UPDATE_MASK; reg_val = SET_AP_AWAKE_STATE(reg_val, next_state); ret = adsp_awake_send_swint (ret, reg_val, mask); spin_unlock_irqrestore(p_spin, spin_flags); if (ret == -1) { char *p_id = adsp_core_ids[adsp_id]; pr_err("%s() %s fail\n", __func__, p_id); WARN_ON(1); } mutex_unlock(p_mutex); return ret; } } /* * force adsp lock, keep adsp awake * @param adsp_id: adsp core id * return 0 : force lock success * non-0 : force lock fail */ int adsp_awake_force_lock(enum adsp_core_id adsp_id) { return adsp_awake_set_state(adsp_id, AP_AWAKE_STATE_FORCE_LOCK); } EXPORT_SYMBOL_GPL(adsp_awake_force_lock); /* * force adsp unlock * @param adsp_id: adsp core id * return 0 : force unlock success * non-0 : force unlock fail */ int adsp_awake_force_unlock(enum adsp_core_id adsp_id) { return adsp_awake_set_state(adsp_id, AP_AWAKE_STATE_FORCE_UNLOCK); } EXPORT_SYMBOL_GPL(adsp_awake_force_unlock); /* * set adsp normal * @param adsp_id: adsp core id * return 0 : set normal success * non-0 : set normal fail */ int adsp_awake_set_normal(enum adsp_core_id adsp_id) { return adsp_awake_set_state(adsp_id, AP_AWAKE_STATE_NORMAL); } EXPORT_SYMBOL_GPL(adsp_awake_set_normal); static inline ssize_t adsp_awake_force_lock_show(struct device *kobj, struct device_attribute *att, char *buf) { if (is_adsp_ready(ADSP_A_ID) == 1) { adsp_awake_force_lock(ADSP_A_ID); return scnprintf(buf, PAGE_SIZE, "ADSP awake force lock\n"); } else return scnprintf(buf, PAGE_SIZE, "ADSP is not ready\n"); } DEVICE_ATTR_RO(adsp_awake_force_lock); static inline ssize_t adsp_awake_force_unlock_show(struct device *kobj, struct device_attribute *att, char *buf) { if (is_adsp_ready(ADSP_A_ID) == 1) { adsp_awake_force_unlock(ADSP_A_ID); return scnprintf(buf, PAGE_SIZE, "ADSP awake force unlock\n"); } else return scnprintf(buf, PAGE_SIZE, "ADSP is not ready\n"); } DEVICE_ATTR_RO(adsp_awake_force_unlock); static inline ssize_t adsp_awake_set_normal_show(struct device *kobj, struct device_attribute *att, char *buf) { if (is_adsp_ready(ADSP_A_ID) == 1) { adsp_awake_set_normal(ADSP_A_ID); return scnprintf(buf, PAGE_SIZE, "ADSP awake set normal\n"); } else return scnprintf(buf, PAGE_SIZE, "ADSP is not ready\n"); } DEVICE_ATTR_RO(adsp_awake_set_normal); static inline ssize_t adsp_awake_dump_list_show(struct device *kobj, struct device_attribute *att, char *buf) { if (is_adsp_ready(ADSP_A_ID) == 1) { adsp_awake_dump_list(ADSP_A_ID); return scnprintf(buf, PAGE_SIZE, "ADSP awake dump list\n"); } else return scnprintf(buf, PAGE_SIZE, "ADSP is not ready\n"); } DEVICE_ATTR_RO(adsp_awake_dump_list); static inline ssize_t adsp_awake_lock_show(struct device *kobj, struct device_attribute *att, char *buf) { if (is_adsp_ready(ADSP_A_ID) == 1) { adsp_awake_lock(ADSP_A_ID); return scnprintf(buf, PAGE_SIZE, "ADSP awake lock\n"); } else return scnprintf(buf, PAGE_SIZE, "ADSP is not ready\n"); } DEVICE_ATTR_RO(adsp_awake_lock); static inline ssize_t adsp_awake_unlock_show(struct device *kobj, struct device_attribute *att, char *buf) { if (is_adsp_ready(ADSP_A_ID) == 1) { adsp_awake_unlock(ADSP_A_ID); return scnprintf(buf, PAGE_SIZE, "ADSP awake unlock\n"); } else return scnprintf(buf, PAGE_SIZE, "ADSP is not ready\n"); } DEVICE_ATTR_RO(adsp_awake_unlock); static struct attribute *adsp_awake_attrs[] = { &dev_attr_adsp_awake_force_lock.attr, &dev_attr_adsp_awake_force_unlock.attr, &dev_attr_adsp_awake_set_normal.attr, &dev_attr_adsp_awake_dump_list.attr, &dev_attr_adsp_awake_lock.attr, &dev_attr_adsp_awake_unlock.attr, NULL, }; struct attribute_group adsp_awake_attr_group = { .attrs = adsp_awake_attrs, };