// SPDX-License-Identifier: GPL-2.0-or-later /* * arch/powerpc/platforms/powermac/low_i2c.c * * Copyright (C) 2003-2005 Ben. Herrenschmidt (benh@kernel.crashing.org) * * The linux i2c layer isn't completely suitable for our needs for various * reasons ranging from too late initialisation to semantics not perfectly * matching some requirements of the apple platform functions etc... * * This file thus provides a simple low level unified i2c interface for * powermac that covers the various types of i2c busses used in Apple machines. * For now, keywest, PMU and SMU, though we could add Cuda, or other bit * banging busses found on older chipsets in earlier machines if we ever need * one of them. * * The drivers in this file are synchronous/blocking. In addition, the * keywest one is fairly slow due to the use of msleep instead of interrupts * as the interrupt is currently used by i2c-keywest. In the long run, we * might want to get rid of those high-level interfaces to linux i2c layer * either completely (converting all drivers) or replacing them all with a * single stub driver on top of this one. Once done, the interrupt will be * available for our use.
*/
/* * A bus structure. Each bus in the system has such a structure associated.
*/ struct pmac_i2c_bus
{ struct list_head link; struct device_node *controller; struct device_node *busnode; int type; int flags; struct i2c_adapter adapter; void *hostdata; int channel; /* some hosts have multiple */ int mode; /* current mode */ struct mutex mutex; int opened; int polled; /* open mode */ struct platform_device *platform_dev; struct lock_class_key lock_key;
/* ops */ int (*open)(struct pmac_i2c_bus *bus); void (*close)(struct pmac_i2c_bus *bus); int (*xfer)(struct pmac_i2c_bus *bus, u8 addrdir, int subsize,
u32 subaddr, u8 *data, int len);
};
static LIST_HEAD(pmac_i2c_busses);
/* * Keywest implementation
*/
struct pmac_i2c_host_kw
{ struct mutex mutex; /* Access mutex for use by
* i2c-keywest */ void __iomem *base; /* register base address */ int bsteps; /* register stepping */ int speed; /* speed */ int irq;
u8 *data; unsigned len; int state; int rw; int polled; int result; struct completion complete;
spinlock_t lock; struct timer_list timeout_timer;
};
static u8 kw_i2c_wait_interrupt(struct pmac_i2c_host_kw *host)
{ int i, j;
u8 isr;
for (i = 0; i < 1000; i++) {
isr = kw_read_reg(reg_isr) & KW_I2C_IRQ_MASK; if (isr != 0) return isr;
/* This code is used with the timebase frozen, we cannot rely * on udelay nor schedule when in polled mode ! * For now, just use a bogus loop....
*/ if (host->polled) { for (j = 1; j < 100000; j++)
mb();
} else
msleep(1);
} return isr;
}
if (host->state == state_idle) {
printk(KERN_WARNING "low_i2c: Keywest got an out of state" " interrupt, ignoring\n");
kw_write_reg(reg_isr, isr); return;
}
if (isr == 0) {
printk(KERN_WARNING "low_i2c: Timeout in i2c transfer" " on keywest !\n"); if (host->state != state_stop) {
kw_i2c_do_stop(host, -EIO); return;
}
ack = kw_read_reg(reg_status); if (ack & KW_I2C_STAT_BUSY)
kw_write_reg(reg_status, 0);
host->state = state_idle;
kw_write_reg(reg_ier, 0x00); if (!host->polled)
complete(&host->complete); return;
}
/* Set up address and r/w bit, strip possible stale bus number from * address top bits
*/
kw_write_reg(reg_addr, addrdir & 0xff);
/* Set up the sub address */ if ((mode_reg & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB
|| (mode_reg & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_COMBINED)
kw_write_reg(reg_subaddr, subaddr);
host = kzalloc(sizeof(*host), GFP_KERNEL); if (host == NULL) {
printk(KERN_ERR "low_i2c: Can't allocate host for %pOF\n",
np); return NULL;
}
/* Apple is kind enough to provide a valid AAPL,address property * on all i2c keywest nodes so far ... we would have to fallback * to macio parsing if that wasn't the case
*/
addrp = of_get_property(np, "AAPL,address", NULL); if (addrp == NULL) {
printk(KERN_ERR "low_i2c: Can't find address for %pOF\n",
np);
kfree(host); return NULL;
}
mutex_init(&host->mutex);
init_completion(&host->complete);
spin_lock_init(&host->lock);
timer_setup(&host->timeout_timer, kw_i2c_timeout, 0);
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)
printk(KERN_WARNING "low_i2c: Failed to map interrupt for %pOF\n",
np);
/* Make sure IRQ is disabled */
kw_write_reg(reg_ier, 0);
/* Request chip interrupt. We set IRQF_NO_SUSPEND because we don't * want that interrupt disabled between the 2 passes of driver * suspend or we'll have issues running the pfuncs
*/ if (request_irq(host->irq, kw_i2c_irq, IRQF_NO_SUSPEND, "keywest i2c", host))
host->irq = 0;
/* 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 = !of_node_name_eq(child, "i2c-bus");
of_node_put(child);
/* For a multibus setup, we get the bus count based on the * parent type
*/ if (multibus) { int chans, i;
/* 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 %pOF\n", busnode);
/* * 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;
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 %pOF\n", controller);
/* Look for childs, note that they might not be of the right * type as older device trees mix i2c busses and other things * at the same level
*/
for_each_child_of_node(controller, busnode) { if (!of_node_is_type(busnode, "i2c") &&
!of_node_is_type(busnode, "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) {
of_node_put(busnode); return;
}
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 %pOF !\n", mode, bus->busnode); 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;
/* some quirks for platform function decoding */ enum {
pmac_i2c_quirk_invmask = 0x00000001u,
pmac_i2c_quirk_skip = 0x00000002u,
};
staticvoid pmac_i2c_devscan(void (*callback)(struct device_node *dev, int quirks))
{ struct pmac_i2c_bus *bus; struct device_node *np; staticstruct 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 instantiated * 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_each_child_of_node(bus->busnode, np) { 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 (!of_node_name_eq(np, 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 64
struct 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;
};
bus = pmac_i2c_find_bus(func->node); if (bus == NULL) {
printk(KERN_ERR "low_i2c: Can't find bus for %pOF (pfunc)\n",
func->node); return NULL;
} if (pmac_i2c_open(bus, 0)) {
printk(KERN_ERR "low_i2c: Can't open i2c bus for %pOF (pfunc)\n",
func->node); 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;
}
/* 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
*/ staticvoid 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]);
}
}
/* * 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)
{ staticint i2c_inited;
/* 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...
*/ staticint __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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.