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

📄 low_i2c.c

📁 linux内核源码
💻 C
📖 第 1 页 / 共 3 页
字号:
void pmac_i2c_attach_adapter(struct pmac_i2c_bus *bus,			     struct i2c_adapter *adapter){	WARN_ON(bus->adapter != NULL);	bus->adapter = adapter;}EXPORT_SYMBOL_GPL(pmac_i2c_attach_adapter);void pmac_i2c_detach_adapter(struct pmac_i2c_bus *bus,			     struct i2c_adapter *adapter){	WARN_ON(bus->adapter != adapter);	bus->adapter = NULL;}EXPORT_SYMBOL_GPL(pmac_i2c_detach_adapter);struct i2c_adapter *pmac_i2c_get_adapter(struct pmac_i2c_bus *bus){	return bus->adapter;}EXPORT_SYMBOL_GPL(pmac_i2c_get_adapter);struct pmac_i2c_bus *pmac_i2c_adapter_to_bus(struct i2c_adapter *adapter){	struct pmac_i2c_bus *bus;	list_for_each_entry(bus, &pmac_i2c_busses, link)		if (bus->adapter == adapter)			return bus;	return NULL;}EXPORT_SYMBOL_GPL(pmac_i2c_adapter_to_bus);int pmac_i2c_match_adapter(struct device_node *dev, struct i2c_adapter *adapter){	struct pmac_i2c_bus *bus = pmac_i2c_find_bus(dev);	if (bus == NULL)		return 0;	return (bus->adapter == adapter);}EXPORT_SYMBOL_GPL(pmac_i2c_match_adapter);int pmac_low_i2c_lock(struct device_node *np){	struct pmac_i2c_bus *bus, *found = NULL;	list_for_each_entry(bus, &pmac_i2c_busses, link) {		if (np == bus->controller) {			found = bus;			break;		}	}	if (!found)		return -ENODEV;	return pmac_i2c_open(bus, 0);}EXPORT_SYMBOL_GPL(pmac_low_i2c_lock);int pmac_low_i2c_unlock(struct device_node *np){	struct pmac_i2c_bus *bus, *found = NULL;	list_for_each_entry(bus, &pmac_i2c_busses, link) {		if (np == bus->controller) {			found = bus;			break;		}	}	if (!found)		return -ENODEV;	pmac_i2c_close(bus);	return 0;}EXPORT_SYMBOL_GPL(pmac_low_i2c_unlock);int pmac_i2c_open(struct pmac_i2c_bus *bus, int polled){	int rc;	mutex_lock(&bus->mutex);	bus->polled = polled || pmac_i2c_force_poll;	bus->opened = 1;	bus->mode = pmac_i2c_mode_std;	if (bus->open && (rc = bus->open(bus)) != 0) {		bus->opened = 0;		mutex_unlock(&bus->mutex);		return rc;	}	return 0;}EXPORT_SYMBOL_GPL(pmac_i2c_open);void pmac_i2c_close(struct pmac_i2c_bus *bus){	WARN_ON(!bus->opened);	if (bus->close)		bus->close(bus);	bus->opened = 0;	mutex_unlock(&bus->mutex);}EXPORT_SYMBOL_GPL(pmac_i2c_close);int pmac_i2c_setmode(struct pmac_i2c_bus *bus, int mode){	WARN_ON(!bus->opened);	/* Report me if you see the error below as there might be a new	 * "combined4" mode that I need to implement for the SMU bus	 */	if (mode < pmac_i2c_mode_dumb || mode > pmac_i2c_mode_combined) {		printk(KERN_ERR "low_i2c: Invalid mode %d requested on"		       " bus %s !\n", mode, bus->busnode->full_name);		return -EINVAL;	}	bus->mode = mode;	return 0;}EXPORT_SYMBOL_GPL(pmac_i2c_setmode);int pmac_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize,		  u32 subaddr, u8 *data, int len){	int rc;	WARN_ON(!bus->opened);	DBG("xfer() chan=%d, addrdir=0x%x, mode=%d, subsize=%d, subaddr=0x%x,"	    " %d bytes, bus %s\n", bus->channel, addrdir, bus->mode, subsize,	    subaddr, len, bus->busnode->full_name);	rc = bus->xfer(bus, addrdir, subsize, subaddr, data, len);#ifdef DEBUG	if (rc)		DBG("xfer error %d\n", rc);#endif	return rc;}EXPORT_SYMBOL_GPL(pmac_i2c_xfer);/* some quirks for platform function decoding */enum {	pmac_i2c_quirk_invmask = 0x00000001u,	pmac_i2c_quirk_skip = 0x00000002u,};static void pmac_i2c_devscan(void (*callback)(struct device_node *dev,					      int quirks)){	struct pmac_i2c_bus *bus;	struct device_node *np;	static struct whitelist_ent {		char *name;		char *compatible;		int quirks;	} whitelist[] = {		/* XXX Study device-tree's & apple drivers are get the quirks		 * right !		 */		/* Workaround: It seems that running the clockspreading		 * properties on the eMac will cause lockups during boot.		 * The machine seems to work fine without that. So for now,		 * let's make sure i2c-hwclock doesn't match about "imic"		 * clocks and we'll figure out if we really need to do		 * something special about those later.		 */		{ "i2c-hwclock", "imic5002", pmac_i2c_quirk_skip },		{ "i2c-hwclock", "imic5003", pmac_i2c_quirk_skip },		{ "i2c-hwclock", NULL, pmac_i2c_quirk_invmask },		{ "i2c-cpu-voltage", NULL, 0},		{  "temp-monitor", NULL, 0 },		{  "supply-monitor", NULL, 0 },		{ NULL, NULL, 0 },	};	/* Only some devices need to have platform functions instanciated	 * here. For now, we have a table. Others, like 9554 i2c GPIOs used	 * on Xserve, if we ever do a driver for them, will use their own	 * platform function instance	 */	list_for_each_entry(bus, &pmac_i2c_busses, link) {		for (np = NULL;		     (np = of_get_next_child(bus->busnode, np)) != NULL;) {			struct whitelist_ent *p;			/* If multibus, check if device is on that bus */			if (bus->flags & pmac_i2c_multibus)				if (bus != pmac_i2c_find_bus(np))					continue;			for (p = whitelist; p->name != NULL; p++) {				if (strcmp(np->name, p->name))					continue;				if (p->compatible &&				    !of_device_is_compatible(np, p->compatible))					continue;				if (p->quirks & pmac_i2c_quirk_skip)					break;				callback(np, p->quirks);				break;			}		}	}}#define MAX_I2C_DATA	64struct pmac_i2c_pf_inst{	struct pmac_i2c_bus	*bus;	u8			addr;	u8			buffer[MAX_I2C_DATA];	u8			scratch[MAX_I2C_DATA];	int			bytes;	int			quirks;};static void* pmac_i2c_do_begin(struct pmf_function *func, struct pmf_args *args){	struct pmac_i2c_pf_inst *inst;	struct pmac_i2c_bus	*bus;	bus = pmac_i2c_find_bus(func->node);	if (bus == NULL) {		printk(KERN_ERR "low_i2c: Can't find bus for %s (pfunc)\n",		       func->node->full_name);		return NULL;	}	if (pmac_i2c_open(bus, 0)) {		printk(KERN_ERR "low_i2c: Can't open i2c bus for %s (pfunc)\n",		       func->node->full_name);		return NULL;	}	/* XXX might need GFP_ATOMIC when called during the suspend process,	 * but then, there are already lots of issues with suspending when	 * near OOM that need to be resolved, the allocator itself should	 * probably make GFP_NOIO implicit during suspend	 */	inst = kzalloc(sizeof(struct pmac_i2c_pf_inst), GFP_KERNEL);	if (inst == NULL) {		pmac_i2c_close(bus);		return NULL;	}	inst->bus = bus;	inst->addr = pmac_i2c_get_dev_addr(func->node);	inst->quirks = (int)(long)func->driver_data;	return inst;}static void pmac_i2c_do_end(struct pmf_function *func, void *instdata){	struct pmac_i2c_pf_inst *inst = instdata;	if (inst == NULL)		return;	pmac_i2c_close(inst->bus);	if (inst)		kfree(inst);}static int pmac_i2c_do_read(PMF_STD_ARGS, u32 len){	struct pmac_i2c_pf_inst *inst = instdata;	inst->bytes = len;	return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_read, 0, 0,			     inst->buffer, len);}static int pmac_i2c_do_write(PMF_STD_ARGS, u32 len, const u8 *data){	struct pmac_i2c_pf_inst *inst = instdata;	return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_write, 0, 0,			     (u8 *)data, len);}/* This function is used to do the masking & OR'ing for the "rmw" type * callbacks. Ze should apply the mask and OR in the values in the * buffer before writing back. The problem is that it seems that * various darwin drivers implement the mask/or differently, thus * we need to check the quirks first */static void pmac_i2c_do_apply_rmw(struct pmac_i2c_pf_inst *inst,				  u32 len, const u8 *mask, const u8 *val){	int i;	if (inst->quirks & pmac_i2c_quirk_invmask) {		for (i = 0; i < len; i ++)			inst->scratch[i] = (inst->buffer[i] & mask[i]) | val[i];	} else {		for (i = 0; i < len; i ++)			inst->scratch[i] = (inst->buffer[i] & ~mask[i])				| (val[i] & mask[i]);	}}static int pmac_i2c_do_rmw(PMF_STD_ARGS, u32 masklen, u32 valuelen,			   u32 totallen, const u8 *maskdata,			   const u8 *valuedata){	struct pmac_i2c_pf_inst *inst = instdata;	if (masklen > inst->bytes || valuelen > inst->bytes ||	    totallen > inst->bytes || valuelen > masklen)		return -EINVAL;	pmac_i2c_do_apply_rmw(inst, masklen, maskdata, valuedata);	return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_write, 0, 0,			     inst->scratch, totallen);}static int pmac_i2c_do_read_sub(PMF_STD_ARGS, u8 subaddr, u32 len){	struct pmac_i2c_pf_inst *inst = instdata;	inst->bytes = len;	return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_read, 1, subaddr,			     inst->buffer, len);}static int pmac_i2c_do_write_sub(PMF_STD_ARGS, u8 subaddr, u32 len,				     const u8 *data){	struct pmac_i2c_pf_inst *inst = instdata;	return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_write, 1,			     subaddr, (u8 *)data, len);}static int pmac_i2c_do_set_mode(PMF_STD_ARGS, int mode){	struct pmac_i2c_pf_inst *inst = instdata;	return pmac_i2c_setmode(inst->bus, mode);}static int pmac_i2c_do_rmw_sub(PMF_STD_ARGS, u8 subaddr, u32 masklen,			       u32 valuelen, u32 totallen, const u8 *maskdata,			       const u8 *valuedata){	struct pmac_i2c_pf_inst *inst = instdata;	if (masklen > inst->bytes || valuelen > inst->bytes ||	    totallen > inst->bytes || valuelen > masklen)		return -EINVAL;	pmac_i2c_do_apply_rmw(inst, masklen, maskdata, valuedata);	return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_write, 1,			     subaddr, inst->scratch, totallen);}static int pmac_i2c_do_mask_and_comp(PMF_STD_ARGS, u32 len,				     const u8 *maskdata,				     const u8 *valuedata){	struct pmac_i2c_pf_inst *inst = instdata;	int i, match;	/* Get return value pointer, it's assumed to be a u32 */	if (!args || !args->count || !args->u[0].p)		return -EINVAL;	/* Check buffer */	if (len > inst->bytes)		return -EINVAL;	for (i = 0, match = 1; match && i < len; i ++)		if ((inst->buffer[i] & maskdata[i]) != valuedata[i])			match = 0;	*args->u[0].p = match;	return 0;}static int pmac_i2c_do_delay(PMF_STD_ARGS, u32 duration){	msleep((duration + 999) / 1000);	return 0;}static struct pmf_handlers pmac_i2c_pfunc_handlers = {	.begin			= pmac_i2c_do_begin,	.end			= pmac_i2c_do_end,	.read_i2c		= pmac_i2c_do_read,	.write_i2c		= pmac_i2c_do_write,	.rmw_i2c		= pmac_i2c_do_rmw,	.read_i2c_sub		= pmac_i2c_do_read_sub,	.write_i2c_sub		= pmac_i2c_do_write_sub,	.rmw_i2c_sub		= pmac_i2c_do_rmw_sub,	.set_i2c_mode		= pmac_i2c_do_set_mode,	.mask_and_compare	= pmac_i2c_do_mask_and_comp,	.delay			= pmac_i2c_do_delay,};static void __init pmac_i2c_dev_create(struct device_node *np, int quirks){	DBG("dev_create(%s)\n", np->full_name);	pmf_register_driver(np, &pmac_i2c_pfunc_handlers,			    (void *)(long)quirks);}static void __init pmac_i2c_dev_init(struct device_node *np, int quirks){	DBG("dev_create(%s)\n", np->full_name);	pmf_do_functions(np, NULL, 0, PMF_FLAGS_ON_INIT, NULL);}static void pmac_i2c_dev_suspend(struct device_node *np, int quirks){	DBG("dev_suspend(%s)\n", np->full_name);	pmf_do_functions(np, NULL, 0, PMF_FLAGS_ON_SLEEP, NULL);}static void pmac_i2c_dev_resume(struct device_node *np, int quirks){	DBG("dev_resume(%s)\n", np->full_name);	pmf_do_functions(np, NULL, 0, PMF_FLAGS_ON_WAKE, NULL);}void pmac_pfunc_i2c_suspend(void){	pmac_i2c_devscan(pmac_i2c_dev_suspend);}void pmac_pfunc_i2c_resume(void){	pmac_i2c_devscan(pmac_i2c_dev_resume);}/* * Initialize us: probe all i2c busses on the machine, instantiate * busses and platform functions as needed. *//* This is non-static as it might be called early by smp code */int __init pmac_i2c_init(void){	static int i2c_inited;	if (i2c_inited)		return 0;	i2c_inited = 1;	if (!machine_is(powermac))		return 0;	/* Probe keywest-i2c busses */	kw_i2c_probe();#ifdef CONFIG_ADB_PMU	/* Probe PMU i2c busses */	pmu_i2c_probe();#endif#ifdef CONFIG_PMAC_SMU	/* Probe SMU i2c busses */	smu_i2c_probe();#endif	/* Now add plaform functions for some known devices */	pmac_i2c_devscan(pmac_i2c_dev_create);	return 0;}arch_initcall(pmac_i2c_init);/* Since pmac_i2c_init can be called too early for the platform device * registration, we need to do it at a later time. In our case, subsys * happens to fit well, though I agree it's a bit of a hack... */static int __init pmac_i2c_create_platform_devices(void){	struct pmac_i2c_bus *bus;	int i = 0;	/* In the case where we are initialized from smp_init(), we must	 * not use the timer (and thus the irq). It's safe from now on	 * though	 */	pmac_i2c_force_poll = 0;	/* Create platform devices */	list_for_each_entry(bus, &pmac_i2c_busses, link) {		bus->platform_dev =			platform_device_alloc("i2c-powermac", i++);		if (bus->platform_dev == NULL)			return -ENOMEM;		bus->platform_dev->dev.platform_data = bus;		platform_device_add(bus->platform_dev);	}	/* Now call platform "init" functions */	pmac_i2c_devscan(pmac_i2c_dev_init);	return 0;}subsys_initcall(pmac_i2c_create_platform_devices);

⌨️ 快捷键说明

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