resource_34xx.c
来自「omap3 linux 2.6 用nocc去除了冗余代码」· C语言 代码 · 共 1,309 行 · 第 1/3 页
C
1,309 行
/* * linux/arch/arm/mach-omap3/resource.c * OMAP34XX Shared Resource Framework * * Copyright (C) 2006-2007 Texas Instruments, Inc. * Rajendra Nayak <rnayak@ti.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * History: * */#include <linux/errno.h>#include <linux/err.h>#include <linux/notifier.h>#include <asm/arch/resource.h>#include <linux/latency.h>#include <asm/arch/clock.h>#include "resource_34xx.h"#include "prcm-regs.h"static DECLARE_MUTEX(res_handle_mutex);static DECLARE_MUTEX(users_list_mutex);/* Pool of resource handles */short handle_flag[MAX_HANDLES];struct res_handle handle_list[MAX_HANDLES];/* Pool of users for a resource */short usr_flag[MAX_USERS];struct users_list usr_list[MAX_USERS];/* Pool of linked resource handles */struct res_handle_node linked_hlist[MAX_HANDLES];/* global variables which can be used in idle_thread */short core_active = LOGICAL_UNUSED;struct res_handle *vdd1_res;struct res_handle *vdd1_opp_co;struct res_handle *vdd2_opp_co;unsigned int vdd1_users;unsigned int vdd1_arm_prenotifications;unsigned int vdd1_arm_postnotifications;unsigned int vdd1_dsp_prenotifications;unsigned int vdd1_dsp_postnotifications;struct res_handle *vdd2_res;struct atomic_notifier_head freq_arm_pre_notifier_list;struct atomic_notifier_head freq_arm_post_notifier_list;struct atomic_notifier_head freq_dsp_pre_notifier_list;struct atomic_notifier_head freq_dsp_post_notifier_list;u32 valid_rate;#define S600M 600000000#define S550M 550000000#define S500M 500000000#define S250M 250000000#define S125M 125000000#define S19M 19200000#define S120M 120000000#define S477M 476000000#define S381M 381000000#define S191M 190000000#define S96M 96000000#define S166M 166000000#define S83M 83000000#define S66M 66500000#define S133M 133000000#define S266M 266000000#define S293M 293000000#define S320M 320000000#define down_srfmutex(x) if(!(in_atomic() || irqs_disabled())) \ down(x)#define up_srfmutex(x) if(!(in_atomic() || irqs_disabled())) \ up(x)unsigned int vdd1_arm_dsp_freq[5][4] = { {125, 90, CO_VDD1_OPP1, PRCM_VDD1_OPP1}, {250, 180, CO_VDD1_OPP2, PRCM_VDD1_OPP2}, {500, 360, CO_VDD1_OPP3, PRCM_VDD1_OPP3}, {550, 396, CO_VDD1_OPP4, PRCM_VDD1_OPP4}, {600, 430, CO_VDD1_OPP5, PRCM_VDD1_OPP5},};unsigned int vdd2_core_freq[3][3] = { {0, CO_VDD2_OPP1, PRCM_VDD2_OPP1}, {83, CO_VDD2_OPP2, PRCM_VDD2_OPP2}, {166, CO_VDD2_OPP3, PRCM_VDD2_OPP3},};#define scale_volt_then_freq (cur_opp_no < target_opp_no)unsigned int rnd_rate_vdd1[5] = { S125M, S250M, S500M, S550M, S600M};unsigned int rnd_rate_vdd2[3] = { 0, S83M, S166M};#define request_vdd2_co (target_value > CO_VDD1_OPP2)#define release_vdd2_co (target_value <= CO_VDD1_OPP2)#define vdd2_co_val CO_VDD2_OPP3/* WARNING: id_to_* has to be in sync with domains numbering in prcm.h */static char *id_to_lname[] = {"lat_iva2", "lat_mpu", "lat_core1", "lat_core1", "lat_3d", NULL, "lat_dss", "lat_cam", "lat_per", NULL, "lat_neon", "lat_core1", "lat_usb",};int nb_arm_freq_prenotify_func(struct notifier_block *n, unsigned long event, void *ptr){ atomic_notifier_call_chain(&freq_arm_pre_notifier_list, vdd1_arm_dsp_freq[event-1][0], NULL); return 0;}int nb_arm_freq_postnotify_func(struct notifier_block *n, unsigned long event, void *ptr){ atomic_notifier_call_chain(&freq_arm_post_notifier_list, vdd1_arm_dsp_freq[event-1][0], NULL); return 0;}int nb_dsp_freq_prenotify_func(struct notifier_block *n, unsigned long event, void *ptr){ atomic_notifier_call_chain(&freq_dsp_pre_notifier_list, vdd1_arm_dsp_freq[event-1][1], NULL); return 0;}int nb_dsp_freq_postnotify_func(struct notifier_block *n, unsigned long event, void *ptr){ atomic_notifier_call_chain(&freq_dsp_post_notifier_list, vdd1_arm_dsp_freq[event-1][1], NULL); return 0;}static struct notifier_block nb_arm_freq_prenotify = { nb_arm_freq_prenotify_func, NULL,};static struct notifier_block nb_arm_freq_postnotify = { nb_arm_freq_postnotify_func, NULL,};static struct notifier_block nb_dsp_freq_prenotify = { nb_dsp_freq_prenotify_func, NULL,};static struct notifier_block nb_dsp_freq_postnotify = { nb_dsp_freq_postnotify_func, NULL,};/* To set the opp for vdd1 */unsigned int vdd1_opp_setting(u32 target_opp_no){ unsigned int cur_opp_no, target_vdd1_opp; target_vdd1_opp = vdd1_arm_dsp_freq[target_opp_no-1][3]; cur_opp_no = get_opp_no(current_vdd1_opp); if (p_vdd1_clk == NULL) p_vdd1_clk = clk_get(NULL, "virt_vdd1_prcm_set"); if (scale_volt_then_freq) { prcm_do_voltage_scaling(target_vdd1_opp, current_vdd1_opp); valid_rate = clk_round_rate(p_vdd1_clk, rnd_rate_vdd1[target_opp_no-1]); p_vdd1_clk->set_rate(p_vdd1_clk, valid_rate); } else { valid_rate = clk_round_rate(p_vdd1_clk, rnd_rate_vdd1[target_opp_no-1]); p_vdd1_clk->set_rate(p_vdd1_clk, valid_rate); prcm_do_voltage_scaling(target_vdd1_opp, current_vdd1_opp); } return target_vdd1_opp;}/* To set the opp value for vdd2 */unsigned int vdd2_opp_setting(u32 target_opp_no){ unsigned int cur_opp_no, target_vdd2_opp; target_vdd2_opp = vdd2_core_freq[target_opp_no-1][2]; cur_opp_no = get_opp_no(current_vdd2_opp); if (p_vdd2_clk == NULL) p_vdd2_clk = clk_get(NULL, "virt_vdd2_prcm_set"); if (scale_volt_then_freq) { prcm_do_voltage_scaling(target_vdd2_opp, current_vdd2_opp); valid_rate = clk_round_rate(p_vdd2_clk, rnd_rate_vdd2[target_opp_no-1]); p_vdd2_clk->set_rate(p_vdd2_clk, valid_rate); } else { valid_rate = clk_round_rate(p_vdd2_clk, rnd_rate_vdd2[target_opp_no-1]); p_vdd2_clk->set_rate(p_vdd2_clk, valid_rate); prcm_do_voltage_scaling(target_vdd2_opp, current_vdd2_opp); } return target_vdd2_opp;}/* #define DEBUG_RES_FRWK 1 */# define DPRINTK(fmt, args...)/* This is the initializtion function for Shared resource framework. *//* Initializes all the members of res_list. Always returns 0. */int resource_init(void){ struct shared_resource **resp; int ind; int i; int linked_cnt = 0; DPRINTK("Initializing Shared Resource Framework\n"); /* Init the res_handle POOL */ for (ind = 0; ind < MAX_HANDLES; ind++) { handle_flag[ind] = UNUSED; handle_list[ind].res = NULL; handle_list[ind].usr_name = NULL; handle_list[ind].res_index = ind; handle_list[ind].usr_index = -1; } /* Init the users_list POOL */ for (ind = 0; ind < MAX_USERS; ind++) { usr_flag[ind] = UNUSED; usr_list[ind].usr_name = NULL; usr_list[ind].level = DEFAULT_LEVEL; usr_list[ind].index = ind; } /* Init all the resources in res_list */ for (resp = res_list; resp < res_list + ARRAY_SIZE(res_list); resp++) { INIT_LIST_HEAD(&(*resp)->users_list); init_MUTEX(&(*resp)->res_action_sem); INIT_LIST_HEAD(&(*resp)->linked_res_handles); /* * latency resources have to follow domain resources in * res_list! */ if (strncmp((*resp)->name, "lat_", 4) == 0) { for (i = 0; i < (*resp)->linked_res_num; i++) { if (linked_cnt >= MAX_HANDLES) goto res_init_fail; linked_hlist[linked_cnt].handle = resource_get((*resp)->name, (*resp)->linked_res_names[i]); if (IS_ERR(linked_hlist[linked_cnt].handle)) goto res_init_fail; list_add(&linked_hlist[linked_cnt].node, &(*resp)->linked_res_handles); linked_cnt++; } } } /* Setup the correct polarity for sysoffmode */ /* Should be done in prcm_init but being done here since * there are sequencing issues */ PRM_POLCTRL &= ~PRM_POL_SYSOFFMODE; return 0;res_init_fail: /* TODO: free all memory and resources */ return -1;}/* Returns a res_handle structure to the caller, which has a resource*//* structure and a name which is the clock name embedded in it. The clock *//* name is embedded to track futher calls by the same device.*/struct res_handle *resource_get(const char *name, const char *id){ struct shared_resource **resp; short index = 0; struct res_handle *res = ERR_PTR(-ENOENT); if (name == NULL || id == NULL) { printk(KERN_ERR "resource_get: Invalid pointer\n"); return res; } down_srfmutex(&res_handle_mutex); DPRINTK("resource_get for %s Clock-name %s\n", id, name); for (resp = res_list; resp < res_list + ARRAY_SIZE(res_list); resp++) { if (strcmp((*resp)->name, id) == 0) { /* look for a free handle from the handle_list POOL */ while (index < MAX_HANDLES) { if (handle_flag[index] == UNUSED) break; else index++; } if (index >= MAX_HANDLES) { /* No free handle available */ panic("FATAL ERROR: res_handle POOL EMPTY\n"); } handle_flag[index] = USED; handle_list[index].res = (*resp); handle_list[index].usr_name = name; handle_list[index].res_index = index; DPRINTK("Returning the handle for %s, index = %d\n", id, index); res = &handle_list[index]; } } up_srfmutex(&res_handle_mutex); return res;}/* Adds the request to the list of requests for the given resource.*//*Recalulates the target level to be set for the resource and updates *//*it if not same as the current level. Also calls notification functions *//*for registered users to notify the target level change */int resource_request(struct res_handle *res, unsigned short target_level){ struct shared_resource *resp; struct users_list *user, *cur_user = NULL; short index = 0; int ret = -1; if (res == ERR_PTR(-ENOENT)) { DPRINTK("Invalid resource handle passed to reource_request\n"); return ret; } resp = res->res; DPRINTK("resource_request: Clock-name %s\n", res->usr_name); down_srfmutex(&users_list_mutex); if (res->usr_index != -1) { cur_user = &usr_list[res->usr_index]; ret = resp->validate(resp, cur_user->level, target_level); if (ret) { DPRINTK("Validation failed\n"); ret = -EINVAL; goto ret; } DPRINTK("Updating resource level for %s, to %d\n", resp->name, target_level); cur_user->level = target_level; } else { ret = resp->validate(resp, DEFAULT_LEVEL, target_level); if (ret) { DPRINTK("Validation failed\n"); ret = -EINVAL; goto ret; } DPRINTK("Adding resource level for %s, to %d\n", resp->name, target_level); while (index < MAX_USERS) { if (usr_flag[index] == UNUSED) break; else index++; } if (index >= MAX_USERS) panic("FATAL ERROR: users_list POOL EMPTY\n"); usr_flag[index] = USED; usr_list[index].usr_name = res->usr_name; usr_list[index].level = target_level; usr_list[index].index = index; res->usr_index = index; list_add(&usr_list[index].node, &resp->users_list); cur_user = &usr_list[index]; } if (target_level == resp->curr_level) goto ret; /* Regenerate the target_level for the resource */ target_level = DEFAULT_LEVEL; list_for_each_entry(user, &(resp->users_list), node) { if (user->level > target_level) target_level = user->level; } DPRINTK("New Target level is %d\n", target_level); up_srfmutex(&users_list_mutex); if (target_level != resp->curr_level) { DPRINTK("Changing Level for resource %s to %d\n", resp->name, target_level); if ((resp->res_type != RES_FREQ_CO) && (target_level < MAX_LEVEL)) { /* Call the notification functions */ /* Currently not supported for Frequency constraints */ atomic_notifier_call_chain( &resp->pre_notifier_list[target_level], target_level, NULL); } down_srfmutex(&resp->res_action_sem); ret = resp->action(resp, resp->curr_level, target_level); up_srfmutex(&resp->res_action_sem); if (ret) panic("FATAL ERROR: Unable to Change\ level for resource %s to %d\n", resp->name, target_level); else /* If successful, change the resource curr_level */ resp->curr_level = target_level; if ((resp->res_type != RES_FREQ_CO) && (target_level < MAX_LEVEL)) atomic_notifier_call_chain( &resp->post_notifier_list[target_level], target_level, NULL); } else { /* return success */ ret = 0;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?