/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* ppm driver subroutines
*/
#include <sys/ddi_impldefs.h>
/*
* Append address to the device path, if it is set. Routine
* ddi_pathname does not look for device address if the node is in
* DS_INITIALIZED state.
*/
}
int ppm_parse_dc(char **, ppm_dc_t *);
int ppm_match_devs(char *, ppm_db_t *);
int ppm_count_char(char *, char);
int ppm_convert(char *, uint_t *);
void ppm_prop_free(struct ppm_cdata **);
/*
* lookup string property from configuration file ppm.conf
*/
static int
{
#ifdef DEBUG
#endif
int err;
if (err != DDI_PROP_SUCCESS) {
break;
}
}
return (err);
}
void
{
if (cdp) {
}
}
}
}
}
/*
* free ddi prop strings. Under error condition, free ppm_db_t lists as well.
*/
static int
{
if (err != DDI_SUCCESS) {
}
}
err = DDI_FAILURE;
}
return (err);
}
{
break;
}
return (domp);
}
/*
* for the purpose of optimizing we search for identical dc->path
* that has been opened per previous visit here. If search results
* in a hit, copy the device handle, else open the device.
*/
ppm_dc_t *
{
#ifdef DEBUG
#endif
/* search domain by domain.model */
break;
}
/* lookup hndl from same domain model */
return (key_dc);
}
}
}
/* otherwise, check other domains */
for (domp = ppm_domain_p;
if (PPM_DOMAIN_UP(domp)) {
"from domain %s\n",
return (key_dc);
}
}
}
}
return (NULL);
}
};
/*
* store up platform dependent information provided by ppm.conf file
* into private data base
*/
int
{
#ifdef DEBUG
#endif
int err;
/*
* get "ppm-domains" property
*/
}
if (ppm_domain_p == NULL)
ppm_domain_p = domp;
else {
ppm_domain_p = domp;
}
}
/*
* more per domain property strings in ppm.conf file tell us
* what the nature of domain, how to performe domain control, etc.
* Even the property names of those per domain properties are
* formed consisting its domain name string.
* Here we walk through our domain list, and fullfill the details.
*/
/*
* get "domain_xy-model" property
*/
}
break;
}
}
/* get "domain_xy-propname" property */
cdata[0] = &propnamedata;
}
/* get "domain_xy-devices" property */
}
}
/* get "domain_xy-control" property */
dc_namep++) {
if (err != DDI_SUCCESS)
}
}
#ifdef DEBUG
while (dc) {
}
#endif
}
return (DDI_SUCCESS);
}
/*
* scan conf devices within each domain for a matching device name
*/
{
#ifdef __x86
#endif /* __x86 */
if (PPM_DOMAIN_UP(domp)) {
/*
* allow claiming root without knowing
* its full name
*/
if (dip == ddi_root_node() &&
return (domp);
#ifdef __x86
/*
* Special rule to catch all CPU devices on x86.
*/
DDI_PROP_DONTPASS, "device_type",
&devtype) == DDI_SUCCESS) {
return (domp);
} else {
}
}
#endif /* __x86 */
return (domp);
}
}
}
return (NULL);
}
/*
* check ppm.conf file domain device pathname syntax, if correct,
* create device match pattern.
* return 1 for good, -1 for bad.
*/
ppm_db_t *
{
int wccnt, i;
int pos;
char *cp;
return (NULL);
if (*cp == '*')
break;
(i + 1), wcpos[i]))
}
#ifdef DEBUG
/* first '*', if exists, don't go beyond the string */
if (wccnt > 0)
/* second '*', if exists, better be the last character */
if (wccnt == 2)
#endif
/*
* first '*', if followed by any char, must be immediately
* followed by '@' and the rest better be bound by
* ['0-9', 'a-f', A-F'] until ended '0' or second '*''0'.
*/
if (*cp != '@')
return (NULL);
return (NULL);
}
}
/*
* match given device "path" to domain device pathname
* pattern dbp->name that contains one or two '*' character(s).
* Matching policy:
* 1). If one wildcard terminates match pattern, need exact match
* up to (but exclude) the wildcard;
* 2). If one wildcard does not terminate match pattern, it is to
* match driver name (terminates with '@') and must be followed
* by exact match of rest of pattern;
* 3). If two wildcards, first is to match driver name as in 2),
* second is to match fcnid (terminates with '/' or '\0') and
* must the last char of pattern.
*
* return 0 if match, and
* non 0 if mismatch
*/
int
{
int len;
/* match upto the first '*' regardless */
return (-1);
/* "<exact match>*" */
;
return ((*cp == 0) ? 0 : -1);
}
/* locate '@' */
cp++;
/* if one wildcard, match the rest in the pattern */
/* must have exact match after first wildcard up to second */
return (-1);
/* second wildcard match terminates with '/' or '\0' */
/* but only termination with '\0' is a successful match */
cp++;
return ((*cp == 0) ? 0 : -1);
}
/*
* By claiming a device, ppm gets involved in its power change
* power(9e) call.
*
* If 'dip' is a PCI device, this is the time to ask its parent
* what PCI bus speed it is running.
*
* returns 1 (claimed), 0 (not claimed)
*/
int
{
if (!domp)
claimed = 0;
switch (pciclk) {
case 33000000:
claimed = 1;
break;
case 66000000:
claimed = 1;
break;
default:
claimed = 0;
break;
}
}
claimed = 1;
#ifdef DEBUG
if (claimed) {
}
#endif
return (claimed);
}
/*
* add a device to the list of domain's owned devices (if it is not already
* on the list).
*/
{
return (owned);
}
/*
*/
{
int cmpt;
/*
* For devs which have exported "pm-components" we want to create
* a data structure for each component. When a driver chooses not
* to export the prop we treat its device as having a single
* component and build a structure for it anyway. All other ppm
* logic will act as if this device were always up and can thus
* make correct decisions about it in relation to other devices
* in its domain.
*/
("ppm_add_dev: %s to domain %s: ppm_dev(0x%p)\n",
}
/*
* devi_pm_ppm_private should be set only after all
* ppm_dev s related to all components have been
* initialized and domain's pwr_cnt is incremented
* for each of them.
*/
/* remember this device forever */
/*
* Initializing flag is set for devices which have gone through
* PPM_PMR_INIT_CHILD ctlop. By this point, these devices have
* been added to ppm structures and could participate in pm
* decision making, so clear the initializing flag.
*/
if (owned->initializing) {
owned->initializing = 0;
}
return (new);
}
/*
* returns an existing or newly created ppm device reference
*/
{
return (pdp);
}
/*
* scan a domain's device list and remove those with .dip
* matching the arg *dip; we need to scan the entire list
* for the case of devices with multiple components
*/
void
{
continue;
}
}
}
/*
* prepare kernel ioctl calls:
*/
void
{
/*
* Warning: This code is rather confusing.
*
* It intends to ensure that ppm_init_lyr() is only
* called ONCE for a device that may be associated
* with more than one domain control.
* So, what it does is first to check to see if
* there is a handle, and then if not it goes on
* to call the init_lyr() routine.
*
* The non-obvious thing is that the ppm_init_lyr()
* routine, in addition to opening the device
* associated with the dc (domain control) in
* question, has the side-effect of creating the
* handle for that dc as well.
*/
continue;
break;
}
}
}
}
/*
* ppm_init_lyr - initializing layered ioctl
* Return:
* DDI_SUCCESS - succeeded
* DDI_FAILURE - failed
*
*/
int
{
int err = 0;
}
(void) ldi_ident_release(li);
if (err != 0) {
return (err);
}
return (DDI_SUCCESS);
}
/*
* lock, unlock, or trylock for one power mutex
*/
void
{
switch (reqp->request_type) {
case PMR_PPM_LOCK_POWER:
break;
case PMR_PPM_UNLOCK_POWER:
break;
case PMR_PPM_TRY_LOCK_POWER:
break;
}
}
/*
* lock, unlock, or trylock for all power mutexes within a domain
*/
void
{
/*
* To simplify the implementation we let all the devices
* in the domain be represented by a single device (dip).
* We use the first device in the domain's devlist. This
* is safe because we return with the domain lock held
* which prevents the list from changing.
*/
/* domain lock remains held */
return;
return;
}
*iresp = 0;
return;
}
if (*iresp)
else
}
/*
* return FALSE: if any detached device during its previous life exported
* the "no-involuntary-power-cycles" property and detached with its
* power level not at its lowest, or there is a device in the process
* exported a property that it can tolerate clock off while bus is not
* quiescent; if a 66mhz PCI domain has devices that do not support stopping
* clock at D3; either one would count as a power holder.
* return TRUE: otherwise.
*/
{
int i = 0;
return (B_FALSE);
return (B_FALSE);
}
}
i++;
return (i == 0);
}
/*
* return the number of char 'c' occurrences in string s
*/
int
ppm_count_char(char *s, char c)
{
int i = 0;
char *cp = s;
while (*cp) {
if (*cp == c)
i++;
cp++;
}
return (i);
}
/*
* extract and convert a substring from input string "ss" in form of
* "name=value" into an hex or decimal integer
*/
int
{
char *cp;
int digit;
cp++;
hex_++;
cp++;
}
else
}
}
/*
* ppm_convert - convert a #define symbol to its integer value,
* only the #defines for ppm_dc.cmd and ppm_dc.method fields in
* ppmvar.h file are recognized.
*/
struct ppm_confdefs {
char *sym;
int val;
} ppm_confdefs_table[] = {
"ENTER_S3", PPMDC_ENTER_S3,
"EXIT_S3", PPMDC_EXIT_S3,
"CPU_NEXT", PPMDC_CPU_NEXT,
"PRE_CHNG", PPMDC_PRE_CHNG,
"CPU_GO", PPMDC_CPU_GO,
"POST_CHNG", PPMDC_POST_CHNG,
"FET_ON", PPMDC_FET_ON,
"FET_OFF", PPMDC_FET_OFF,
"CLK_OFF", PPMDC_CLK_OFF,
"CLK_ON", PPMDC_CLK_ON,
"LED_ON", PPMDC_LED_ON,
"LED_OFF", PPMDC_LED_OFF,
"KIO", PPMDC_KIO,
"VCORE", PPMDC_VCORE,
#ifdef sun4u
"I2CKIO", PPMDC_I2CKIO,
#endif
"CPUSPEEDKIO", PPMDC_CPUSPEEDKIO,
"PRE_PWR_OFF", PPMDC_PRE_PWR_OFF,
"PRE_PWR_ON", PPMDC_PRE_PWR_ON,
"POST_PWR_ON", PPMDC_POST_PWR_ON,
"PWR_OFF", PPMDC_PWR_OFF,
"PWR_ON", PPMDC_PWR_ON,
"RESET_OFF", PPMDC_RESET_OFF,
"RESET_ON", PPMDC_RESET_ON,
};
/*
* convert a #define'd symbol to its integer value where
* input "symbol" is expected to be in form of "SYMBOL=value"
*/
int
{
char *s;
}
s++;
}
}
/*
* parse a domain control property string into data structure struct ppm_dc
*/
int
{
char *line;
char *f, *b;
int i;
int err;
dclist = (char **)
while (*b != ' ' && *b != 0)
b++;
if (*b == 0)
else
*b = 0;
dclist[i] = f;
}
for (i = 0; i < count; i++) {
if (err == -1)
return (err);
continue;
}
f += strlen("path=");
continue;
}
if (err == -1)
return (err);
continue;
}
continue;
}
continue;
}
continue;
}
continue;
}
#ifdef sun4u
continue;
}
#endif
/* This must be before the if statement for delay */
#ifdef sun4u
#else
#endif
/*
* all delays are uint_t type instead of clock_t.
* If the delay is too long, it might get truncated.
* But, we don't expect delay to be too long.
*/
case PPMDC_KIO:
break;
#ifdef sun4u
case PPMDC_I2CKIO:
break;
#endif
default:
break;
}
continue;
}
#ifdef sun4u
#else
#endif
/*
* all delays are uint_t type instead of clock_t.
* If the delay is too long, it might get truncated.
* But, we don't expect delay to be too long.
*/
case PPMDC_KIO:
break;
#ifdef sun4u
case PPMDC_I2CKIO:
break;
#endif
case PPMDC_VCORE:
break;
default:
break;
}
continue;
}
/* we encounted unrecognized field, flag error */
return (-1);
}
return (DDI_SUCCESS);
}
/*
* search for domain control handle for a claimed device coupled with a
* domain control command. NULL device may indicate LED domain.
*/
ppm_dc_t *
{
#ifdef DEBUG
#endif
/*
* For convenience, we accept 'domp' as NULL for searching
* LED domain control operation.
*/
break;
"information.\n"))
return (NULL);
}
else
}
/*
* for the rest of ppm domains, lookup ppm_dc starting from domp
*/
switch (cmd) {
case PPMDC_CPU_NEXT:
case PPMDC_PRE_CHNG:
case PPMDC_CPU_GO:
case PPMDC_POST_CHNG:
case PPMDC_FET_OFF:
case PPMDC_FET_ON:
case PPMDC_CLK_OFF:
case PPMDC_CLK_ON:
case PPMDC_PRE_PWR_OFF:
case PPMDC_PRE_PWR_ON:
case PPMDC_POST_PWR_ON:
case PPMDC_PWR_OFF:
case PPMDC_PWR_ON:
case PPMDC_RESET_OFF:
case PPMDC_RESET_ON:
case PPMDC_ENTER_S3:
case PPMDC_EXIT_S3:
break;
default:
return (NULL);
}
return (dc);
}
}
return (NULL);
}
ppm_get_domain_by_dev(const char *p)
{
return (NULL);
break;
}
}
if (found)
break;
}
return (domp);
}
#ifdef DEBUG
/*
* convert a ctlop integer to a char string. this helps printing
* meaningful info when cltops are received from the pm framework.
* since some ctlops are so frequent, we use mask to limit output:
* a valid string is returned when ctlop is found and when
* (cmd.flags & mask) is true; otherwise NULL is returned.
*/
char *
{
struct ctlop_cmd {
int ctlop;
char *str;
};
FLINTSTR(0, PMR_PPM_ATTACH),
FLINTSTR(0, PMR_PPM_DETACH),
};
break;
return (NULL);
}
void
{
#ifdef sun4u
} else if (d->method == PPMDC_I2CKIO) {
#endif
} else if (d->method == PPMDC_VCORE) {
".delay(0x%x)",
} else if (d->method == PPMDC_CPUSPEEDKIO) {
}
}
#endif /* DEBUG */