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 + -
显示快捷键?