📄 low_i2c.c
字号:
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 = ∁ 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 = ∁ 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 = ∁ 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 + -