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