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

📄 low_i2c.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	psteps = of_get_property(np, "AAPL,address-step", NULL);	steps = psteps ? (*psteps) : 0x10;	for (host->bsteps = 0; (steps & 0x01) == 0; host->bsteps++)		steps >>= 1;	/* Select interface rate */	host->speed = KW_I2C_MODE_25KHZ;	prate = of_get_property(np, "AAPL,i2c-rate", NULL);	if (prate) switch(*prate) {	case 100:		host->speed = KW_I2C_MODE_100KHZ;		break;	case 50:		host->speed = KW_I2C_MODE_50KHZ;		break;	case 25:		host->speed = KW_I2C_MODE_25KHZ;		break;	}		host->irq = irq_of_parse_and_map(np, 0);	if (host->irq == NO_IRQ)		printk(KERN_WARNING		       "low_i2c: Failed to map interrupt for %s\n",		       np->full_name);	host->base = ioremap((*addrp), 0x1000);	if (host->base == NULL) {		printk(KERN_ERR "low_i2c: Can't map registers for %s\n",		       np->full_name);		kfree(host);		return NULL;	}	/* Make sure IRQ is disabled */	kw_write_reg(reg_ier, 0);	/* Request chip interrupt */	if (request_irq(host->irq, kw_i2c_irq, 0, "keywest i2c", host))		host->irq = NO_IRQ;	printk(KERN_INFO "KeyWest i2c @0x%08x irq %d %s\n",	       *addrp, host->irq, np->full_name);	return host;}static void __init kw_i2c_add(struct pmac_i2c_host_kw *host,			      struct device_node *controller,			      struct device_node *busnode,			      int channel){	struct pmac_i2c_bus *bus;	bus = kzalloc(sizeof(struct pmac_i2c_bus), GFP_KERNEL);	if (bus == NULL)		return;	bus->controller = of_node_get(controller);	bus->busnode = of_node_get(busnode);	bus->type = pmac_i2c_bus_keywest;	bus->hostdata = host;	bus->channel = channel;	bus->mode = pmac_i2c_mode_std;	bus->open = kw_i2c_open;	bus->close = kw_i2c_close;	bus->xfer = kw_i2c_xfer;	mutex_init(&bus->mutex);	if (controller == busnode)		bus->flags = pmac_i2c_multibus;	list_add(&bus->link, &pmac_i2c_busses);	printk(KERN_INFO " channel %d bus %s\n", channel,	       (controller == busnode) ? "<multibus>" : busnode->full_name);}static void __init kw_i2c_probe(void){	struct device_node *np, *child, *parent;	/* Probe keywest-i2c busses */	for (np = NULL;	     (np = of_find_compatible_node(np, "i2c","keywest-i2c")) != NULL;){		struct pmac_i2c_host_kw *host;		int multibus, chans, i;		/* Found one, init a host structure */		host = kw_i2c_host_init(np);		if (host == NULL)			continue;		/* Now check if we have a multibus setup (old style) or if we		 * have proper bus nodes. Note that the "new" way (proper bus		 * nodes) might cause us to not create some busses that are		 * kept hidden in the device-tree. In the future, we might		 * want to work around that by creating busses without a node		 * but not for now		 */		child = of_get_next_child(np, NULL);		multibus = !child || strcmp(child->name, "i2c-bus");		of_node_put(child);		/* For a multibus setup, we get the bus count based on the		 * parent type		 */		if (multibus) {			parent = of_get_parent(np);			if (parent == NULL)				continue;			chans = parent->name[0] == 'u' ? 2 : 1;			for (i = 0; i < chans; i++)				kw_i2c_add(host, np, np, i);		} else {			for (child = NULL;			     (child = of_get_next_child(np, child)) != NULL;) {				const u32 *reg = of_get_property(child,						"reg", NULL);				if (reg == NULL)					continue;				kw_i2c_add(host, np, child, *reg);			}		}	}}/* * * PMU implementation * */#ifdef CONFIG_ADB_PMU/* * i2c command block to the PMU */struct pmu_i2c_hdr {	u8	bus;	u8	mode;	u8	bus2;	u8	address;	u8	sub_addr;	u8	comb_addr;	u8	count;	u8	data[];};static void pmu_i2c_complete(struct adb_request *req){	complete(req->arg);}static int pmu_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize,			u32 subaddr, u8 *data, int len){	struct adb_request *req = bus->hostdata;	struct pmu_i2c_hdr *hdr = (struct pmu_i2c_hdr *)&req->data[1];	struct completion comp;	int read = addrdir & 1;	int retry;	int rc = 0;	/* For now, limit ourselves to 16 bytes transfers */	if (len > 16)		return -EINVAL;	init_completion(&comp);	for (retry = 0; retry < 16; retry++) {		memset(req, 0, sizeof(struct adb_request));		hdr->bus = bus->channel;		hdr->count = len;		switch(bus->mode) {		case pmac_i2c_mode_std:			if (subsize != 0)				return -EINVAL;			hdr->address = addrdir;			hdr->mode = PMU_I2C_MODE_SIMPLE;			break;		case pmac_i2c_mode_stdsub:		case pmac_i2c_mode_combined:			if (subsize != 1)				return -EINVAL;			hdr->address = addrdir & 0xfe;			hdr->comb_addr = addrdir;			hdr->sub_addr = subaddr;			if (bus->mode == pmac_i2c_mode_stdsub)				hdr->mode = PMU_I2C_MODE_STDSUB;			else				hdr->mode = PMU_I2C_MODE_COMBINED;			break;		default:			return -EINVAL;		}		INIT_COMPLETION(comp);		req->data[0] = PMU_I2C_CMD;		req->reply[0] = 0xff;		req->nbytes = sizeof(struct pmu_i2c_hdr) + 1;		req->done = pmu_i2c_complete;		req->arg = &comp;		if (!read && len) {			memcpy(hdr->data, data, len);			req->nbytes += len;		}		rc = pmu_queue_request(req);		if (rc)			return rc;		wait_for_completion(&comp);		if (req->reply[0] == PMU_I2C_STATUS_OK)			break;		msleep(15);	}	if (req->reply[0] != PMU_I2C_STATUS_OK)		return -EIO;	for (retry = 0; retry < 16; retry++) {		memset(req, 0, sizeof(struct adb_request));		/* I know that looks like a lot, slow as hell, but darwin		 * does it so let's be on the safe side for now		 */		msleep(15);		hdr->bus = PMU_I2C_BUS_STATUS;		INIT_COMPLETION(comp);		req->data[0] = PMU_I2C_CMD;		req->reply[0] = 0xff;		req->nbytes = 2;		req->done = pmu_i2c_complete;		req->arg = &comp;		rc = pmu_queue_request(req);		if (rc)			return rc;		wait_for_completion(&comp);		if (req->reply[0] == PMU_I2C_STATUS_OK && !read)			return 0;		if (req->reply[0] == PMU_I2C_STATUS_DATAREAD && read) {			int rlen = req->reply_len - 1;			if (rlen != len) {				printk(KERN_WARNING "low_i2c: PMU returned %d"				       " bytes, expected %d !\n", rlen, len);				return -EIO;			}			if (len)				memcpy(data, &req->reply[1], len);			return 0;		}	}	return -EIO;}static void __init pmu_i2c_probe(void){	struct pmac_i2c_bus *bus;	struct device_node *busnode;	int channel, sz;	if (!pmu_present())		return;	/* There might or might not be a "pmu-i2c" node, we use that	 * or via-pmu itself, whatever we find. I haven't seen a machine	 * with separate bus nodes, so we assume a multibus setup	 */	busnode = of_find_node_by_name(NULL, "pmu-i2c");	if (busnode == NULL)		busnode = of_find_node_by_name(NULL, "via-pmu");	if (busnode == NULL)		return;	printk(KERN_INFO "PMU i2c %s\n", busnode->full_name);	/*	 * We add bus 1 and 2 only for now, bus 0 is "special"	 */	for (channel = 1; channel <= 2; channel++) {		sz = sizeof(struct pmac_i2c_bus) + sizeof(struct adb_request);		bus = kzalloc(sz, GFP_KERNEL);		if (bus == NULL)			return;		bus->controller = busnode;		bus->busnode = busnode;		bus->type = pmac_i2c_bus_pmu;		bus->channel = channel;		bus->mode = pmac_i2c_mode_std;		bus->hostdata = bus + 1;		bus->xfer = pmu_i2c_xfer;		mutex_init(&bus->mutex);		bus->flags = pmac_i2c_multibus;		list_add(&bus->link, &pmac_i2c_busses);		printk(KERN_INFO " channel %d bus <multibus>\n", channel);	}}#endif /* CONFIG_ADB_PMU *//* * * SMU implementation * */#ifdef CONFIG_PMAC_SMUstatic void smu_i2c_complete(struct smu_i2c_cmd *cmd, void *misc){	complete(misc);}static int smu_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize,			u32 subaddr, u8 *data, int len){	struct smu_i2c_cmd *cmd = bus->hostdata;	struct completion comp;	int read = addrdir & 1;	int rc = 0;	if ((read && len > SMU_I2C_READ_MAX) ||	    ((!read) && len > SMU_I2C_WRITE_MAX))		return -EINVAL;	memset(cmd, 0, sizeof(struct smu_i2c_cmd));	cmd->info.bus = bus->channel;	cmd->info.devaddr = addrdir;	cmd->info.datalen = len;	switch(bus->mode) {	case pmac_i2c_mode_std:		if (subsize != 0)			return -EINVAL;		cmd->info.type = SMU_I2C_TRANSFER_SIMPLE;		break;	case pmac_i2c_mode_stdsub:	case pmac_i2c_mode_combined:		if (subsize > 3 || subsize < 1)			return -EINVAL;		cmd->info.sublen = subsize;		/* that's big-endian only but heh ! */		memcpy(&cmd->info.subaddr, ((char *)&subaddr) + (4 - subsize),		       subsize);		if (bus->mode == pmac_i2c_mode_stdsub)			cmd->info.type = SMU_I2C_TRANSFER_STDSUB;		else			cmd->info.type = SMU_I2C_TRANSFER_COMBINED;		break;	default:		return -EINVAL;	}	if (!read && len)		memcpy(cmd->info.data, data, len);	init_completion(&comp);	cmd->done = smu_i2c_complete;	cmd->misc = &comp;	rc = smu_queue_i2c(cmd);	if (rc < 0)		return rc;	wait_for_completion(&comp);	rc = cmd->status;	if (read && len)		memcpy(data, cmd->info.data, len);	return rc < 0 ? rc : 0;}static void __init smu_i2c_probe(void){	struct device_node *controller, *busnode;	struct pmac_i2c_bus *bus;	const u32 *reg;	int sz;	if (!smu_present())		return;	controller = of_find_node_by_name(NULL, "smu-i2c-control");	if (controller == NULL)		controller = of_find_node_by_name(NULL, "smu");	if (controller == NULL)		return;	printk(KERN_INFO "SMU i2c %s\n", controller->full_name);	/* Look for childs, note that they might not be of the right	 * type as older device trees mix i2c busses and other thigns	 * at the same level	 */	for (busnode = NULL;	     (busnode = of_get_next_child(controller, busnode)) != NULL;) {		if (strcmp(busnode->type, "i2c") &&		    strcmp(busnode->type, "i2c-bus"))			continue;		reg = of_get_property(busnode, "reg", NULL);		if (reg == NULL)			continue;		sz = sizeof(struct pmac_i2c_bus) + sizeof(struct smu_i2c_cmd);		bus = kzalloc(sz, GFP_KERNEL);		if (bus == NULL)			return;		bus->controller = controller;		bus->busnode = of_node_get(busnode);		bus->type = pmac_i2c_bus_smu;		bus->channel = *reg;		bus->mode = pmac_i2c_mode_std;		bus->hostdata = bus + 1;		bus->xfer = smu_i2c_xfer;		mutex_init(&bus->mutex);		bus->flags = 0;		list_add(&bus->link, &pmac_i2c_busses);		printk(KERN_INFO " channel %x bus %s\n",		       bus->channel, busnode->full_name);	}}#endif /* CONFIG_PMAC_SMU *//* * * Core code * */struct pmac_i2c_bus *pmac_i2c_find_bus(struct device_node *node){	struct device_node *p = of_node_get(node);	struct device_node *prev = NULL;	struct pmac_i2c_bus *bus;	while(p) {		list_for_each_entry(bus, &pmac_i2c_busses, link) {			if (p == bus->busnode) {				if (prev && bus->flags & pmac_i2c_multibus) {					const u32 *reg;					reg = of_get_property(prev, "reg",								NULL);					if (!reg)						continue;					if (((*reg) >> 8) != bus->channel)						continue;				}				of_node_put(p);				of_node_put(prev);				return bus;			}		}		of_node_put(prev);		prev = p;		p = of_get_parent(p);	}	return NULL;}EXPORT_SYMBOL_GPL(pmac_i2c_find_bus);u8 pmac_i2c_get_dev_addr(struct device_node *device){	const u32 *reg = of_get_property(device, "reg", NULL);	if (reg == NULL)		return 0;	return (*reg) & 0xff;}EXPORT_SYMBOL_GPL(pmac_i2c_get_dev_addr);struct device_node *pmac_i2c_get_controller(struct pmac_i2c_bus *bus){	return bus->controller;}EXPORT_SYMBOL_GPL(pmac_i2c_get_controller);struct device_node *pmac_i2c_get_bus_node(struct pmac_i2c_bus *bus){	return bus->busnode;}EXPORT_SYMBOL_GPL(pmac_i2c_get_bus_node);int pmac_i2c_get_type(struct pmac_i2c_bus *bus){	return bus->type;}EXPORT_SYMBOL_GPL(pmac_i2c_get_type);int pmac_i2c_get_flags(struct pmac_i2c_bus *bus){	return bus->flags;}EXPORT_SYMBOL_GPL(pmac_i2c_get_flags);int pmac_i2c_get_channel(struct pmac_i2c_bus *bus){	return bus->channel;}EXPORT_SYMBOL_GPL(pmac_i2c_get_channel);

⌨️ 快捷键说明

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