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

📄 i2c-pmac-smu.c

📁 linux-2.6.15.6
💻 C
字号:
/*    i2c Support for Apple SMU Controller    Copyright (c) 2005 Benjamin Herrenschmidt, IBM Corp.                       <benh@kernel.crashing.org>    This program is free software; you can redistribute it and/or modify    it under the terms of the GNU General Public License as published by    the Free Software Foundation; either version 2 of the License, or    (at your option) any later version.    This program is distributed in the hope that it will be useful,    but WITHOUT ANY WARRANTY; without even the implied warranty of    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    GNU General Public License for more details.    You should have received a copy of the GNU General Public License    along with this program; if not, write to the Free Software    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/i2c.h>#include <linux/init.h>#include <linux/completion.h>#include <linux/device.h>#include <asm/prom.h>#include <asm/of_device.h>#include <asm/smu.h>static int probe;MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");MODULE_DESCRIPTION("I2C driver for Apple's SMU");MODULE_LICENSE("GPL");module_param(probe, bool, 0);/* Physical interface */struct smu_iface{	struct i2c_adapter	adapter;	struct completion	complete;	u32			busid;};static void smu_i2c_done(struct smu_i2c_cmd *cmd, void *misc){	struct smu_iface	*iface = misc;	complete(&iface->complete);}/* * SMBUS-type transfer entrypoint */static s32 smu_smbus_xfer(	struct i2c_adapter*	adap,				u16			addr,				unsigned short		flags,				char			read_write,				u8			command,				int			size,				union i2c_smbus_data*	data){	struct smu_iface	*iface = i2c_get_adapdata(adap);	struct smu_i2c_cmd	cmd;	int			rc = 0;	int			read = (read_write == I2C_SMBUS_READ);	cmd.info.bus = iface->busid;	cmd.info.devaddr = (addr << 1) | (read ? 0x01 : 0x00);	/* Prepare datas & select mode */	switch (size) {        case I2C_SMBUS_QUICK:		cmd.info.type = SMU_I2C_TRANSFER_SIMPLE;		cmd.info.datalen = 0;	    	break;        case I2C_SMBUS_BYTE:		cmd.info.type = SMU_I2C_TRANSFER_SIMPLE;		cmd.info.datalen = 1;		if (!read)			cmd.info.data[0] = data->byte;	    	break;        case I2C_SMBUS_BYTE_DATA:		cmd.info.type = SMU_I2C_TRANSFER_STDSUB;		cmd.info.datalen = 1;		cmd.info.sublen = 1;		cmd.info.subaddr[0] = command;		cmd.info.subaddr[1] = 0;		cmd.info.subaddr[2] = 0;		if (!read)			cmd.info.data[0] = data->byte;	    	break;        case I2C_SMBUS_WORD_DATA:		cmd.info.type = SMU_I2C_TRANSFER_STDSUB;		cmd.info.datalen = 2;		cmd.info.sublen = 1;		cmd.info.subaddr[0] = command;		cmd.info.subaddr[1] = 0;		cmd.info.subaddr[2] = 0;		if (!read) {			cmd.info.data[0] = data->byte & 0xff;			cmd.info.data[1] = (data->byte >> 8) & 0xff;		}		break;	/* Note that these are broken vs. the expected smbus API where	 * on reads, the lenght is actually returned from the function,	 * but I think the current API makes no sense and I don't want	 * any driver that I haven't verified for correctness to go	 * anywhere near a pmac i2c bus anyway ...	 */        case I2C_SMBUS_BLOCK_DATA:		cmd.info.type = SMU_I2C_TRANSFER_STDSUB;		cmd.info.datalen = data->block[0] + 1;		if (cmd.info.datalen > 6)			return -EINVAL;		if (!read)			memcpy(cmd.info.data, data->block, cmd.info.datalen);		cmd.info.sublen = 1;		cmd.info.subaddr[0] = command;		cmd.info.subaddr[1] = 0;		cmd.info.subaddr[2] = 0;		break;	case I2C_SMBUS_I2C_BLOCK_DATA:		cmd.info.type = SMU_I2C_TRANSFER_STDSUB;		cmd.info.datalen = data->block[0];		if (cmd.info.datalen > 7)			return -EINVAL;		if (!read)			memcpy(cmd.info.data, &data->block[1],			       cmd.info.datalen);		cmd.info.sublen = 1;		cmd.info.subaddr[0] = command;		cmd.info.subaddr[1] = 0;		cmd.info.subaddr[2] = 0;		break;        default:	    	return -EINVAL;	}	/* Turn a standardsub read into a combined mode access */ 	if (read_write == I2C_SMBUS_READ &&	    cmd.info.type == SMU_I2C_TRANSFER_STDSUB)		cmd.info.type = SMU_I2C_TRANSFER_COMBINED;	/* Finish filling command and submit it */	cmd.done = smu_i2c_done;	cmd.misc = iface;	rc = smu_queue_i2c(&cmd);	if (rc < 0)		return rc;	wait_for_completion(&iface->complete);	rc = cmd.status;	if (!read || rc < 0)		return rc;	switch (size) {        case I2C_SMBUS_BYTE:        case I2C_SMBUS_BYTE_DATA:		data->byte = cmd.info.data[0];	    	break;        case I2C_SMBUS_WORD_DATA:		data->word = ((u16)cmd.info.data[1]) << 8;		data->word |= cmd.info.data[0];		break;	/* Note that these are broken vs. the expected smbus API where	 * on reads, the lenght is actually returned from the function,	 * but I think the current API makes no sense and I don't want	 * any driver that I haven't verified for correctness to go	 * anywhere near a pmac i2c bus anyway ...	 */        case I2C_SMBUS_BLOCK_DATA:	case I2C_SMBUS_I2C_BLOCK_DATA:		memcpy(&data->block[0], cmd.info.data, cmd.info.datalen);		break;	}	return rc;}static u32smu_smbus_func(struct i2c_adapter * adapter){	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |	       I2C_FUNC_SMBUS_BLOCK_DATA;}/* For now, we only handle combined mode (smbus) */static struct i2c_algorithm smu_algorithm = {	.smbus_xfer	= smu_smbus_xfer,	.functionality	= smu_smbus_func,};static int create_iface(struct device_node *np, struct device *dev){	struct smu_iface* iface;	u32 *reg, busid;	int rc;	reg = (u32 *)get_property(np, "reg", NULL);	if (reg == NULL) {		printk(KERN_ERR "i2c-pmac-smu: can't find bus number !\n");		return -ENXIO;	}	busid = *reg;	iface = kzalloc(sizeof(struct smu_iface), GFP_KERNEL);	if (iface == NULL) {		printk(KERN_ERR "i2c-pmac-smu: can't allocate inteface !\n");		return -ENOMEM;	}	init_completion(&iface->complete);	iface->busid = busid;	dev_set_drvdata(dev, iface);	sprintf(iface->adapter.name, "smu-i2c-%02x", busid);	iface->adapter.algo = &smu_algorithm;	iface->adapter.algo_data = NULL;	iface->adapter.client_register = NULL;	iface->adapter.client_unregister = NULL;	i2c_set_adapdata(&iface->adapter, iface);	iface->adapter.dev.parent = dev;	rc = i2c_add_adapter(&iface->adapter);	if (rc) {		printk(KERN_ERR "i2c-pamc-smu.c: Adapter %s registration "		       "failed\n", iface->adapter.name);		i2c_set_adapdata(&iface->adapter, NULL);	}	if (probe) {		unsigned char addr;		printk("Probe: ");		for (addr = 0x00; addr <= 0x7f; addr++) {			if (i2c_smbus_xfer(&iface->adapter,addr,					   0,0,0,I2C_SMBUS_QUICK,NULL) >= 0)				printk("%02x ", addr);		}		printk("\n");	}	printk(KERN_INFO "SMU i2c bus %x registered\n", busid);	return 0;}static int dispose_iface(struct device *dev){	struct smu_iface *iface = dev_get_drvdata(dev);	int rc;	rc = i2c_del_adapter(&iface->adapter);	i2c_set_adapdata(&iface->adapter, NULL);	/* We aren't that prepared to deal with this... */	if (rc)		printk("i2c-pmac-smu.c: Failed to remove bus %s !\n",		       iface->adapter.name);	dev_set_drvdata(dev, NULL);	kfree(iface);	return 0;}static int create_iface_of_platform(struct of_device* dev,				    const struct of_device_id *match){	return create_iface(dev->node, &dev->dev);}static int dispose_iface_of_platform(struct of_device* dev){	return dispose_iface(&dev->dev);}static struct of_device_id i2c_smu_match[] ={	{		.compatible	= "smu-i2c",	},	{},};static struct of_platform_driver i2c_smu_of_platform_driver ={	.name 		= "i2c-smu",	.match_table	= i2c_smu_match,	.probe		= create_iface_of_platform,	.remove		= dispose_iface_of_platform};static int __init i2c_pmac_smu_init(void){	of_register_driver(&i2c_smu_of_platform_driver);	return 0;}static void __exit i2c_pmac_smu_cleanup(void){	of_unregister_driver(&i2c_smu_of_platform_driver);}module_init(i2c_pmac_smu_init);module_exit(i2c_pmac_smu_cleanup);

⌨️ 快捷键说明

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