/* Each child device can be monitored by up to PCF8584_MAX_CHANNELS. *Propertyofaportorchannelasdefinedbythefirmware.
*/ struct pcf8584_channel { unsignedchar chnl_no; unsignedchar io_direction; unsignedchar type; unsignedchar last;
};
/* Each child device may have one or more tables of bytes to help decode *data.Tablepropertyasdefinedbythefirmware.
*/ struct pcf8584_tblprop { unsignedint type; unsignedint scale; unsignedint offset; /* offset from the beginning of the table */ unsignedint size;
};
/* i2c child */ struct i2c_child_t { /* Either ADC or GPIO. */ unsignedchar i2ctype; unsignedlong addr; struct pcf8584_channel chnl_array[PCF8584_MAX_CHANNELS];
/* Channel info. */ unsignedint total_chnls; /* Number of monitor channels. */ unsignedchar fan_mask; /* Byte mask for fan status channels. */ unsignedchar voltage_mask; /* Byte mask for voltage status channels. */ struct pcf8584_tblprop tblprop_array[PCF8584_MAX_CHANNELS];
/* Properties of all monitor channels. */ unsignedint total_tbls; /* Number of monitor tables. */ char *tables; /* Pointer to table(s). */ char chnls_desc[CHANNEL_DESC_SZ]; /* Channel description. */ char mon_type[PCF8584_MAX_CHANNELS];
};
/* Function Description: Test the PIN bit (Pending Interrupt Not) *totestwhenserialtransmissioniscompleted. *Return:None.
*/ staticvoid envtrl_i2c_test_pin(void)
{ int limit = 1000000;
while (--limit > 0) { if (!(readb(i2c + PCF8584_CSR) & STATUS_PIN)) break;
udelay(1);
}
if (limit <= 0)
printk(KERN_INFO PFX "Pin status will not clear.\n");
}
/* Function Description: Test busy bit. *Return:None.
*/ staticvoid envctrl_i2c_test_bb(void)
{ int limit = 1000000;
while (--limit > 0) { /* Busy bit 0 means busy. */ if (readb(i2c + PCF8584_CSR) & STATUS_BB) break;
udelay(1);
}
if (limit <= 0)
printk(KERN_INFO PFX "Busy bit will not clear.\n");
}
/* Function Description: Send the address for a read access. *Return:0ifnotacknowledged,otherwiseacknowledged.
*/ staticint envctrl_i2c_read_addr(unsignedchar addr)
{
envctrl_i2c_test_bb();
/* Function Description: Read 1 byte of data from addr *setbyenvctrl_i2c_read_addr() *Return:Datafromaddresssetbyenvctrl_i2c_read_addr().
*/ staticunsignedchar envctrl_i2c_read_data(void)
{
envtrl_i2c_test_pin();
writeb(CONTROL_ES0, i2c + PCF8584_CSR); /* Send neg ack. */ return readb(i2c + PCF8584_DATA);
}
/* Function Description: Instruct the device which port to read data from. *Return:None.
*/ staticvoid envctrl_i2c_write_data(unsignedchar port)
{
envtrl_i2c_test_pin();
writeb(port, i2c + PCF8584_DATA);
}
/* Function Description: Generate Stop condition after last byte is sent. *Return:None.
*/ staticvoid envctrl_i2c_stop(void)
{
envtrl_i2c_test_pin();
writeb(OBD_SEND_STOP, i2c + PCF8584_CSR);
}
/* Do a single byte read and send stop. */
rd = envctrl_i2c_read_data();
envctrl_i2c_stop(); return rd;
}
/* Function Description: Decode data read from an adc device using firmware *table. *Return:Numberofreadbytes.Dataisstoredinbufdatainasciiformat.
*/ staticint envctrl_i2c_data_translate(unsignedchar data, int translate_type, int scale, char *tbl, char *bufdata)
{ int len = 0;
switch (translate_type) { case ENVCTRL_TRANSLATE_NO: /* No decode necessary. */
len = 1;
bufdata[0] = data; break;
case ENVCTRL_TRANSLATE_FULL: /* Decode this way: data = table[data]. */
len = 1;
bufdata[0] = tbl[data]; break;
case ENVCTRL_TRANSLATE_SCALE: /* Decode this way: data = table[data]/scale */
sprintf(bufdata,"%d ", (tbl[data] * 10) / (scale));
len = strlen(bufdata);
bufdata[len - 1] = bufdata[len - 2];
bufdata[len - 2] = '.'; break;
default: break;
}
return len;
}
/* Function Description: Read cpu-related data such as cpu temperature, voltage. *Return:Numberofreadbytes.Dataisstoredinbufdatainasciiformat.
*/ staticint envctrl_read_cpu_info(int cpu, struct i2c_child_t *pchild, char mon_type, unsignedchar *bufdata)
{ unsignedchar data; int i, j = -1; char *tbl;
/* Find the right monitor type and channel. */ for (i = 0; i < PCF8584_MAX_CHANNELS; i++) { if (pchild->mon_type[i] == mon_type) { if (++j == cpu) { break;
}
}
}
if (j != cpu) return0;
/* Read data from address and port. */
data = envctrl_i2c_read_8591((unsignedchar)pchild->addr,
(unsignedchar)pchild->chnl_array[i].chnl_no);
/* Function Description: Read fan status. *Return:Always1byte.Statusstoredinbufdata.
*/ staticint envctrl_i2c_fan_status(struct i2c_child_t *pchild, unsignedchar data, char *bufdata)
{ unsignedchar tmp, ret = 0; int i, j = 0;
tmp = data & pchild->fan_mask;
if (tmp == pchild->fan_mask) { /* All bits are on. All fans are functioning. */
ret = ENVCTRL_ALL_FANS_GOOD;
} elseif (tmp == 0) { /* No bits are on. No fans are functioning. */
ret = ENVCTRL_ALL_FANS_BAD;
} else { /* Go through all channels, mark 'on' the matched bits. *Noticethatfan_maskmayhavediscontiguousbitsbut *returnmaskarealwayscontiguous.Forexampleifwe *monitor4fansatchannels0,1,2,4,thereturnmask *shouldbe00010000ifonlyfanatchannel4isworking.
*/ for (i = 0; i < PCF8584_MAX_CHANNELS;i++) { if (pchild->fan_mask & chnls_mask[i]) { if (!(chnls_mask[i] & tmp))
ret |= chnls_mask[j];
j++;
}
}
}
bufdata[0] = ret; return1;
}
/* Function Description: Read global addressing line. *Return:Always1byte.Statusstoredinbufdata.
*/ staticint envctrl_i2c_globaladdr(struct i2c_child_t *pchild, unsignedchar data, char *bufdata)
{ /* Translatation table is not necessary, as global *addristheintegervalueoftheGA#bits. * *NOTE:MSBisdocumentedaszero,butIseeitas'1'always.... * *----------------------------------------------- *|0|FAL|DEG|GA4|GA3|GA2|GA1|GA0| *----------------------------------------------- *GA0-GA4integervalueofGlobalAddress(backplaneslot#) *DEG0=cPCIPowersupplyoutputisstartingtodegrade *1=cPCIPowersupplyoutputisOK *FAL0=cPCIPowersupplyhasfailed *1=cPCIPowersupplyoutputisOK
*/
bufdata[0] = (data & ENVCTRL_GLOBALADDR_ADDR_MASK); return1;
}
/* Function Description: Read standard voltage and power supply status. *Return:Always1byte.Statusstoredinbufdata.
*/ staticunsignedchar envctrl_i2c_voltage_status(struct i2c_child_t *pchild, unsignedchar data, char *bufdata)
{ unsignedchar tmp, ret = 0; int i, j = 0;
tmp = data & pchild->voltage_mask;
/* Two channels are used to monitor voltage and power supply. */ if (tmp == pchild->voltage_mask) { /* All bits are on. Voltage and power supply are okay. */
ret = ENVCTRL_VOLTAGE_POWERSUPPLY_GOOD;
} elseif (tmp == 0) { /* All bits are off. Voltage and power supply are bad */
ret = ENVCTRL_VOLTAGE_POWERSUPPLY_BAD;
} else { /* Either voltage or power supply has problem. */ for (i = 0; i < PCF8584_MAX_CHANNELS; i++) { if (pchild->voltage_mask & chnls_mask[i]) {
j++;
/* Break out when there is a mismatch. */ if (!(chnls_mask[i] & tmp)) break;
}
}
/* Make a wish that hardware will always use the *firstchannelforvoltageandthesecondfor *powersupply.
*/ if (j == 1)
ret = ENVCTRL_VOLTAGE_BAD; else
ret = ENVCTRL_POWERSUPPLY_BAD;
}
bufdata[0] = ret; return1;
}
/* Function Description: Read a byte from /dev/envctrl. Mapped to user read(). *Return:Numberofreadbytes.0forerror.
*/ static ssize_t
envctrl_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{ struct i2c_child_t *pchild; unsignedchar data[10]; int ret = 0;
/* Get the type of read as decided in ioctl() call. *Findtheappropriatei2cchild. *Getthedataandputbacktotheuserbuffer.
*/
switch ((int)(long)file->private_data) { case ENVCTRL_RD_WARNING_TEMPERATURE: if (warning_temperature == 0) return0;
data[0] = (unsignedchar)(warning_temperature);
ret = 1; if (copy_to_user(buf, data, ret))
ret = -EFAULT; break;
case ENVCTRL_RD_SHUTDOWN_TEMPERATURE: if (shutdown_temperature == 0) return0;
data[0] = (unsignedchar)(shutdown_temperature);
ret = 1; if (copy_to_user(buf, data, ret))
ret = -EFAULT; break;
case ENVCTRL_RD_MTHRBD_TEMPERATURE: if (!(pchild = envctrl_get_i2c_child(ENVCTRL_MTHRBDTEMP_MON))) return0;
ret = envctrl_read_noncpu_info(pchild, ENVCTRL_MTHRBDTEMP_MON, data); if (copy_to_user(buf, data, ret))
ret = -EFAULT; break;
case ENVCTRL_RD_CPU_TEMPERATURE: if (!(pchild = envctrl_get_i2c_child(ENVCTRL_CPUTEMP_MON))) return0;
ret = envctrl_read_cpu_info(read_cpu, pchild, ENVCTRL_CPUTEMP_MON, data);
/* Reset cpu to the default cpu0. */ if (copy_to_user(buf, data, ret))
ret = -EFAULT; break;
case ENVCTRL_RD_CPU_VOLTAGE: if (!(pchild = envctrl_get_i2c_child(ENVCTRL_CPUVOLTAGE_MON))) return0;
ret = envctrl_read_cpu_info(read_cpu, pchild, ENVCTRL_CPUVOLTAGE_MON, data);
/* Reset cpu to the default cpu0. */ if (copy_to_user(buf, data, ret))
ret = -EFAULT; break;
case ENVCTRL_RD_SCSI_TEMPERATURE: if (!(pchild = envctrl_get_i2c_child(ENVCTRL_SCSITEMP_MON))) return0;
ret = envctrl_read_noncpu_info(pchild, ENVCTRL_SCSITEMP_MON, data); if (copy_to_user(buf, data, ret))
ret = -EFAULT; break;
case ENVCTRL_RD_ETHERNET_TEMPERATURE: if (!(pchild = envctrl_get_i2c_child(ENVCTRL_ETHERTEMP_MON))) return0;
ret = envctrl_read_noncpu_info(pchild, ENVCTRL_ETHERTEMP_MON, data); if (copy_to_user(buf, data, ret))
ret = -EFAULT; break;
case ENVCTRL_RD_FAN_STATUS: if (!(pchild = envctrl_get_i2c_child(ENVCTRL_FANSTAT_MON))) return0;
data[0] = envctrl_i2c_read_8574(pchild->addr);
ret = envctrl_i2c_fan_status(pchild,data[0], data); if (copy_to_user(buf, data, ret))
ret = -EFAULT; break;
case ENVCTRL_RD_GLOBALADDRESS: if (!(pchild = envctrl_get_i2c_child(ENVCTRL_GLOBALADDR_MON))) return0;
data[0] = envctrl_i2c_read_8574(pchild->addr);
ret = envctrl_i2c_globaladdr(pchild, data[0], data); if (copy_to_user(buf, data, ret))
ret = -EFAULT; break;
case ENVCTRL_RD_VOLTAGE_STATUS: if (!(pchild = envctrl_get_i2c_child(ENVCTRL_VOLTAGESTAT_MON))) /* If voltage monitor not present, check for CPCI equivalent */ if (!(pchild = envctrl_get_i2c_child(ENVCTRL_GLOBALADDR_MON))) return0;
data[0] = envctrl_i2c_read_8574(pchild->addr);
ret = envctrl_i2c_voltage_status(pchild, data[0], data); if (copy_to_user(buf, data, ret))
ret = -EFAULT; break;
default: break;
}
return ret;
}
/* Function Description: Command what to read. Mapped to user ioctl(). *Return:Gives0forimplementedcommands,-EINVALotherwise.
*/ staticlong
envctrl_ioctl(struct file *file, unsignedint cmd, unsignedlong arg)
{ char __user *infobuf;
switch (cmd) { case ENVCTRL_RD_WARNING_TEMPERATURE: case ENVCTRL_RD_SHUTDOWN_TEMPERATURE: case ENVCTRL_RD_MTHRBD_TEMPERATURE: case ENVCTRL_RD_FAN_STATUS: case ENVCTRL_RD_VOLTAGE_STATUS: case ENVCTRL_RD_ETHERNET_TEMPERATURE: case ENVCTRL_RD_SCSI_TEMPERATURE: case ENVCTRL_RD_GLOBALADDRESS:
file->private_data = (void *)(long)cmd; break;
case ENVCTRL_RD_CPU_TEMPERATURE: case ENVCTRL_RD_CPU_VOLTAGE: /* Check to see if application passes in any cpu number, *thedefaultiscpu0.
*/
infobuf = (char __user *) arg; if (infobuf == NULL) {
read_cpu = 0;
}else {
get_user(read_cpu, infobuf);
}
/* Save the command for use when reading. */
file->private_data = (void *)(long)cmd; break;
default: return -EINVAL;
}
return0;
}
/* Function Description: open device. Mapped to user open(). *Return:Always0.
*/ staticint
envctrl_open(struct inode *inode, struct file *file)
{
file->private_data = NULL; return0;
}
/* Function Description: Open device. Mapped to user close(). *Return:Always0.
*/ staticint
envctrl_release(struct inode *inode, struct file *file)
{ return0;
}
/* Function Description: Set monitor type based on firmware description. *Return:None.
*/ staticvoid envctrl_set_mon(struct i2c_child_t *pchild, constchar *chnl_desc, int chnl_no)
{ /* Firmware only has temperature type. It does not distinguish *differentkindsoftemperatures.Weusechanneldescription *todisinguishthem.
*/ if (!(strcmp(chnl_desc,"temp,cpu")) ||
!(strcmp(chnl_desc,"temp,cpu0")) ||
!(strcmp(chnl_desc,"temp,cpu1")) ||
!(strcmp(chnl_desc,"temp,cpu2")) ||
!(strcmp(chnl_desc,"temp,cpu3")))
pchild->mon_type[chnl_no] = ENVCTRL_CPUTEMP_MON;
/* Function Description: Initialize child device monitoring fan status. *Return:None.
*/ staticvoid envctrl_init_fanstat(struct i2c_child_t *pchild)
{ int i;
/* Go through all channels and set up the mask. */ for (i = 0; i < pchild->total_chnls; i++)
pchild->fan_mask |= chnls_mask[(pchild->chnl_array[i]).chnl_no];
/* We only need to know if this child has fan status monitored. *Wedon'tcarewhichchannelssincewehavethemaskalready.
*/
pchild->mon_type[0] = ENVCTRL_FANSTAT_MON;
}
/* Function Description: Initialize child device for global addressing line. *Return:None.
*/ staticvoid envctrl_init_globaladdr(struct i2c_child_t *pchild)
{ int i;
/* Voltage/PowerSupply monitoring is piggybacked *withGlobalAddressonCompactPCI.Seecomments *withinenvctrl_i2c_globaladdrforbitassignments. * *Themaskiscreatedherebyassigningmaskbitstoeach *bitpositionthatrepresentsPCF8584_VOLTAGE_TYPEdata. *Channelnumbersarenotconsecutivewithintheglobaladdr *node(why?),soweusetheactualcountervalueaschnls_mask *indexinsteadofthechnl_array[x].chnl_novalue. * *NOTE:Thisloopcouldbereplacedwithaconstantrepresenting *amaskofbits5&6(ENVCTRL_GLOBALADDR_PSTAT_MASK).
*/ for (i = 0; i < pchild->total_chnls; i++) { if (PCF8584_VOLTAGE_TYPE == pchild->chnl_array[i].type) {
pchild->voltage_mask |= chnls_mask[i];
}
}
/* We only need to know if this child has global addressing *linemonitored.Wedon'tcarewhichchannelssinceweknow *themaskalready(ENVCTRL_GLOBALADDR_ADDR_MASK).
*/
pchild->mon_type[0] = ENVCTRL_GLOBALADDR_MON;
}
/* Initialize child device monitoring voltage status. */ staticvoid envctrl_init_voltage_status(struct i2c_child_t *pchild)
{ int i;
/* Go through all channels and set up the mask. */ for (i = 0; i < pchild->total_chnls; i++)
pchild->voltage_mask |= chnls_mask[(pchild->chnl_array[i]).chnl_no];
/* We only need to know if this child has voltage status monitored. *Wedon'tcarewhichchannelssincewehavethemaskalready.
*/
pchild->mon_type[0] = ENVCTRL_VOLTAGESTAT_MON;
}
/* Function Description: Initialize i2c child device. *Return:None.
*/ staticvoid envctrl_init_i2c_child(struct device_node *dp, struct i2c_child_t *pchild)
{ int len, i, tbls_size = 0; constvoid *pval;
root_node = of_find_node_by_path("/"); if (of_node_name_eq(root_node, "SUNW,UltraSPARC-IIi-cEngine")) { for (len = 0; len < PCF8584_MAX_CHANNELS; ++len) {
pchild->mon_type[len] = ENVCTRL_NOMON;
}
of_node_put(root_node); return;
}
of_node_put(root_node);
}
/* Get the monitor channels. */
pval = of_get_property(dp, "channels-in-use", &len);
memcpy(pchild->chnl_array, pval, len);
pchild->total_chnls = len / sizeof(struct pcf8584_channel);
for (i = 0; i < pchild->total_chnls; i++) { switch (pchild->chnl_array[i].type) { case PCF8584_TEMP_TYPE:
envctrl_init_adc(pchild, dp); break;
case PCF8584_GLOBALADDR_TYPE:
envctrl_init_globaladdr(pchild);
i = pchild->total_chnls; break;
case PCF8584_FANSTAT_TYPE:
envctrl_init_fanstat(pchild);
i = pchild->total_chnls; break;
case PCF8584_VOLTAGE_TYPE: if (pchild->i2ctype == I2C_ADC) {
envctrl_init_adc(pchild,dp);
} else {
envctrl_init_voltage_status(pchild);
}
i = pchild->total_chnls; break;
default: break;
}
}
}
/* Function Description: Search the child device list for a device. *Return:Thei2cchildiffound.NULLotherwise.
*/ staticstruct i2c_child_t *envctrl_get_i2c_child(unsignedchar mon_type)
{ int i, j;
for (i = 0; i < ENVCTRL_MAX_CPU*2; i++) { for (j = 0; j < PCF8584_MAX_CHANNELS; j++) { if (i2c_childlist[i].mon_type[j] == mon_type) { return (struct i2c_child_t *)(&(i2c_childlist[i]));
}
}
} return NULL;
}
/* Register the device as a minor miscellaneous device. */
err = misc_register(&envctrl_dev); if (err) {
printk(KERN_ERR PFX "Unable to get misc minor %d\n",
envctrl_dev.minor); goto out_iounmap;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.