⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 envctrl.c

📁 讲述linux的初始化过程
💻 C
📖 第 1 页 / 共 2 页
字号:
		 * power supply.		 */		if (j == 1)			ret = ENVCTRL_VOLTAGE_BAD;		else			ret = ENVCTRL_POWERSUPPLY_BAD;	}	bufdata[0] = ret;	return 1;}/* Function Description: Read a byte from /dev/envctrl. Mapped to user read(). * Return: Number of read bytes. 0 for error. */static ssize_tenvctrl_read(struct file *file, char *buf, size_t count, loff_t *ppos){	struct i2c_child_t *pchild;	unsigned char data[10];	int ret = 0;	/* Get the type of read as decided in ioctl() call.	 * Find the appropriate i2c child.	 * Get the data and put back to the user buffer.	 */	switch ((int)(long)file->private_data) {	case ENVCTRL_RD_WARNING_TEMPERATURE:		if (warning_temperature == 0)			return 0;		data[0] = (unsigned char)(warning_temperature);		ret = 1;		copy_to_user((unsigned char *)buf, data, ret);		break;	case ENVCTRL_RD_SHUTDOWN_TEMPERATURE:		if (shutdown_temperature == 0)			return 0;		data[0] = (unsigned char)(shutdown_temperature);		ret = 1;		copy_to_user((unsigned char *)buf, data, ret);		break;	case ENVCTRL_RD_MTHRBD_TEMPERATURE:		if (!(pchild = envctrl_get_i2c_child(ENVCTRL_MTHRBDTEMP_MON)))			return 0;		ret = envctrl_read_noncpu_info(pchild, ENVCTRL_MTHRBDTEMP_MON, data);		copy_to_user((unsigned char *)buf, data, ret);		break;	case ENVCTRL_RD_CPU_TEMPERATURE:		if (!(pchild = envctrl_get_i2c_child(ENVCTRL_CPUTEMP_MON)))			return 0;		ret = envctrl_read_cpu_info(pchild, ENVCTRL_CPUTEMP_MON, data);		/* Reset cpu to the default cpu0. */		copy_to_user((unsigned char *)buf, data, ret);		break;	case ENVCTRL_RD_CPU_VOLTAGE:		if (!(pchild = envctrl_get_i2c_child(ENVCTRL_CPUVOLTAGE_MON)))			return 0;		ret = envctrl_read_cpu_info(pchild, ENVCTRL_CPUVOLTAGE_MON, data);		/* Reset cpu to the default cpu0. */		copy_to_user((unsigned char *)buf, data, ret);		break;	case ENVCTRL_RD_SCSI_TEMPERATURE:		if (!(pchild = envctrl_get_i2c_child(ENVCTRL_SCSITEMP_MON)))			return 0;		ret = envctrl_read_noncpu_info(pchild, ENVCTRL_SCSITEMP_MON, data);		copy_to_user((unsigned char *)buf, data, ret);		break;	case ENVCTRL_RD_ETHERNET_TEMPERATURE:		if (!(pchild = envctrl_get_i2c_child(ENVCTRL_ETHERTEMP_MON)))			return 0;		ret = envctrl_read_noncpu_info(pchild, ENVCTRL_ETHERTEMP_MON, data);		copy_to_user((unsigned char *)buf, data, ret);		break;	case ENVCTRL_RD_FAN_STATUS:		if (!(pchild = envctrl_get_i2c_child(ENVCTRL_FANSTAT_MON)))			return 0;		data[0] = envctrl_i2c_read_8574(pchild->addr);		ret = envctrl_i2c_fan_status(pchild,data[0], data);		copy_to_user((unsigned char *)buf, data, ret);		break;		case ENVCTRL_RD_GLOBALADDRESS:		if (!(pchild = envctrl_get_i2c_child(ENVCTRL_GLOBALADDR_MON)))			return 0;		data[0] = envctrl_i2c_read_8574(pchild->addr);		ret = envctrl_i2c_globaladdr(pchild, data[0], data);		copy_to_user((unsigned char *)buf, data, ret);		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)))				return 0;		data[0] = envctrl_i2c_read_8574(pchild->addr);		ret = envctrl_i2c_voltage_status(pchild, data[0], data);		copy_to_user((unsigned char *)buf, data, ret);		break;	default:		break;	};	return ret;}/* Function Description: Command what to read.  Mapped to user ioctl(). * Return: Gives 0 for implemented commands, -EINVAL otherwise. */static intenvctrl_ioctl(struct inode *inode, struct file *file,	      unsigned int cmd, unsigned long arg){	char *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,		 * the default is cpu0.		 */		infobuf = (char *) 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;	};	return 0;}/* Function Description: open device. Mapped to user open(). * Return: Always 0. */static intenvctrl_open(struct inode *inode, struct file *file){	file->private_data = 0;	MOD_INC_USE_COUNT;	return 0;}/* Function Description: Open device. Mapped to user close(). * Return: Always 0. */static intenvctrl_release(struct inode *inode, struct file *file){	MOD_DEC_USE_COUNT;	return 0;}static struct file_operations envctrl_fops = {	owner:		THIS_MODULE,	read:		envctrl_read,	ioctl:		envctrl_ioctl,	open:		envctrl_open,	release:	envctrl_release,};	static struct miscdevice envctrl_dev = {	ENVCTRL_MINOR,	"envctrl",	&envctrl_fops};/* Function Description: Set monitor type based on firmware description. * Return: None. */static void envctrl_set_mon(struct i2c_child_t *pchild,			    char *chnl_desc,			    int chnl_no){	/* Firmware only has temperature type.  It does not distinguish	 * different kinds of temperatures.  We use channel description	 * to disinguish them.	 */	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;	if (!(strcmp(chnl_desc,"vddcore,cpu0")) ||	    !(strcmp(chnl_desc,"vddcore,cpu1")) ||	    !(strcmp(chnl_desc,"vddcore,cpu2")) ||	    !(strcmp(chnl_desc,"vddcore,cpu3")))		pchild->mon_type[chnl_no] = ENVCTRL_CPUVOLTAGE_MON;	if (!(strcmp(chnl_desc,"temp,motherboard")))		pchild->mon_type[chnl_no] = ENVCTRL_MTHRBDTEMP_MON;	if (!(strcmp(chnl_desc,"temp,scsi")))		pchild->mon_type[chnl_no] = ENVCTRL_SCSITEMP_MON;	if (!(strcmp(chnl_desc,"temp,ethernet")))		pchild->mon_type[chnl_no] = ENVCTRL_ETHERTEMP_MON;}/* Function Description: Initialize monitor channel with channel desc, *                       decoding tables, monitor type, optional properties. * Return: None. */static void envctrl_init_adc(struct i2c_child_t *pchild, int node){	char chnls_desc[CHANNEL_DESC_SZ];	int i, len, j = 0;	char *ptr;	/* Firmware describe channels into a stream separated by a '\0'.	 * Replace all '\0' with a space.	 */        len = prom_getproperty(node, "channels-description", chnls_desc,			       CHANNEL_DESC_SZ);        for (i = 0; i < len; i++) {                if (chnls_desc[i] == '\0')                        chnls_desc[i] = ' ';        }	ptr = strtok(chnls_desc, " ");	while (ptr != NULL) {		envctrl_set_mon(pchild, ptr, j);		ptr = strtok(NULL, " ");		j++;	}	/* Get optional properties. */        len = prom_getproperty(node, "warning-temp", (char *)&warning_temperature,			       sizeof(warning_temperature));        len = prom_getproperty(node, "shutdown-temp", (char *)&shutdown_temperature,			       sizeof(shutdown_temperature));}/* Function Description: Initialize child device monitoring fan status. * Return: None. */static void 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.	 * We dont care which channels since we have the mask already.	 */	pchild->mon_type[0] = ENVCTRL_FANSTAT_MON;}/* Function Description: Initialize child device for global addressing line. * Return: None. */static void envctrl_init_globaladdr(struct i2c_child_t *pchild){	int i;	/* Voltage/PowerSupply monitoring is piggybacked 	 * with Global Address on CompactPCI.  See comments	 * within envctrl_i2c_globaladdr for bit assignments.	 *	 * The mask is created here by assigning mask bits to each	 * bit position that represents PCF8584_VOLTAGE_TYPE data.	 * Channel numbers are not consecutive within the globaladdr	 * node (why?), so we use the actual counter value as chnls_mask	 * index instead of the chnl_array[x].chnl_no value.	 *	 * NOTE: This loop could be replaced with a constant representing	 * a mask of bits 5&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 	 * line monitored.  We dont care which channels since we know 	 * the mask already (ENVCTRL_GLOBALADDR_ADDR_MASK).	 */	pchild->mon_type[0] = ENVCTRL_GLOBALADDR_MON;}/* Initialize child device monitoring voltage status. */static void 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.	 * We dont care which channels since we have the mask already.	 */	pchild->mon_type[0] = ENVCTRL_VOLTAGESTAT_MON;}/* Function Description: Initialize i2c child device. * Return: None. */static void envctrl_init_i2c_child(struct linux_ebus_child *edev_child,				   struct i2c_child_t *pchild){	int node, len, i, tbls_size = 0;	node = edev_child->prom_node;	/* Get device address. */	len = prom_getproperty(node, "reg",			       (char *) &(pchild->addr),			       sizeof(pchild->addr));	/* Get tables property.  Read firmware temperature tables. */	len = prom_getproperty(node, "translation",			       (char *) pchild->tblprop_array,			       (PCF8584_MAX_CHANNELS *				sizeof(struct pcf8584_tblprop)));	if (len > 0) {                pchild->total_tbls = len / sizeof(struct pcf8584_tblprop);		for (i = 0; i < pchild->total_tbls; i++) {			if ((pchild->tblprop_array[i].size + pchild->tblprop_array[i].offset) > tbls_size) {				tbls_size = pchild->tblprop_array[i].size + pchild->tblprop_array[i].offset;			}		}                pchild->tables = kmalloc(tbls_size, GFP_KERNEL);                len = prom_getproperty(node, "tables",				       (char *) pchild->tables, tbls_size);                if (len <= 0) {			printk("envctrl: Failed to get table.\n");			return;		}	}	/* SPARCengine ASM Reference Manual (ref. SMI doc 805-7581-04)	 * sections 2.5, 3.5, 4.5 state node 0x70 for CP1400/1500 is	 * "For Factory Use Only."	 *	 * We ignore the node on these platforms by assigning the	 * 'NULL' monitor type.	 */	if (ENVCTRL_CPCI_IGNORED_NODE == pchild->addr) {		int len;		char prop[56];		len = prom_getproperty(prom_root_node, "name", prop, sizeof(prop));		if (0 < len && (0 == strncmp(prop, "SUNW,UltraSPARC-IIi-cEngine", len)))		{			for (len = 0; len < PCF8584_MAX_CHANNELS; ++len) {				pchild->mon_type[len] = ENVCTRL_NOMON;			}			return;		}	}	/* Get the monitor channels. */	len = prom_getproperty(node, "channels-in-use",			       (char *) pchild->chnl_array,			       (PCF8584_MAX_CHANNELS *				sizeof(struct pcf8584_channel)));	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, node);			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,node);			} else {				envctrl_init_voltage_status(pchild);			}			i = pchild->total_chnls;			break;		default:			break;		};	}}/* Function Description: Search the child device list for a device. * Return : The i2c child if found. NULL otherwise. */static struct i2c_child_t *envctrl_get_i2c_child(unsigned char 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;}static int __init envctrl_init(void){#ifdef CONFIG_PCI	struct linux_ebus *ebus = NULL;	struct linux_ebus_device *edev = NULL;	struct linux_ebus_child *edev_child = NULL;	int i = 0;	/* Traverse through ebus and ebus device list for i2c device and	 * adc and gpio nodes.	 */	for_each_ebus(ebus) {		for_each_ebusdev(edev, ebus) {			if (!strcmp(edev->prom_name, "i2c")) {				i2c = ioremap(	edev->resource[0].start, 								sizeof(struct pcf8584_reg));				for_each_edevchild(edev, edev_child) {					if (!strcmp("gpio", edev_child->prom_name)) {						i2c_childlist[i].i2ctype = I2C_GPIO;						envctrl_init_i2c_child(edev_child, &(i2c_childlist[i++]));					}					if (!strcmp("adc", edev_child->prom_name)) {						i2c_childlist[i].i2ctype = I2C_ADC;						envctrl_init_i2c_child(edev_child, &(i2c_childlist[i++]));					}				}				goto done;			}		}	}done:	if (!edev) {		printk("envctrl: I2C device not found.\n");		return -ENODEV;	}	/* Set device address. */	envctrl_writeb(CONTROL_PIN, &i2c->csr);	envctrl_writeb(PCF8584_ADDRESS, &i2c->data);	/* Set system clock and SCL frequencies. */ 	envctrl_writeb(CONTROL_PIN | CONTROL_ES1, &i2c->csr);	envctrl_writeb(CLK_4_43 | BUS_CLK_90, &i2c->data);	/* Enable serial interface. */	envctrl_writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_ACK, &i2c->csr);	udelay(200);	/* Register the device as a minor miscellaneous device. */	if (misc_register(&envctrl_dev)) {		printk("envctrl: Unable to get misc minor %d\n",		       envctrl_dev.minor);	}	/* Note above traversal routine post-incremented 'i' to accomodate 	 * a next child device, so we decrement before reverse-traversal of	 * child devices.	 */	printk("envctrl: initialized ");	for (--i; i >= 0; --i) {		printk("[%s 0x%lx]%s", 			(I2C_ADC == i2c_childlist[i].i2ctype) ? ("adc") : 			((I2C_GPIO == i2c_childlist[i].i2ctype) ? ("gpio") : ("unknown")), 			i2c_childlist[i].addr, (0 == i) ? ("\n") : (" "));	}	return 0;#else	return -ENODEV;#endif}static void __exit envctrl_cleanup(void){	int i;	iounmap(i2c);	misc_deregister(&envctrl_dev);	for (i = 0; i < ENVCTRL_MAX_CPU * 2; i++) {		if (i2c_childlist[i].tables)			kfree(i2c_childlist[i].tables);	}}module_init(envctrl_init);module_exit(envctrl_cleanup);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -