Commit a28d3af2 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Paul Mackerras

[PATCH] 2/5 powerpc: Rework PowerMac i2c part 2

This is the continuation of the previous patch. This one removes the old
PowerMac i2c drivers (i2c-keywest and i2c-pmac-smu) and replaces them
both with a single stub driver that uses the new PowerMac low i2c layer.

Now that i2c-keywest is gone, the low-i2c code is extended to support
interrupt driver transfers. All i2c busses now appear as platform
devices. Compatibility with existing drivers should be maintained as the
i2c bus names have been kept identical, except for the SMU bus but in
that later case, all users has been fixed.

With that patch added, matching a device node to an i2c_adapter becomes
trivial.
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent 730745a5
This diff is collapsed.
......@@ -650,7 +650,7 @@ static int pmac_check_legacy_ioport(unsigned int baseport)
static int __init pmac_declare_of_platform_devices(void)
{
struct device_node *np, *npp;
struct device_node *np;
np = of_find_node_by_name(NULL, "valkyrie");
if (np)
......@@ -658,22 +658,6 @@ static int __init pmac_declare_of_platform_devices(void)
np = of_find_node_by_name(NULL, "platinum");
if (np)
of_platform_device_create(np, "platinum", NULL);
npp = of_find_node_by_name(NULL, "uni-n");
if (npp == NULL)
npp = of_find_node_by_name(NULL, "u3");
if (npp == NULL)
npp = of_find_node_by_name(NULL, "u4");
if (npp) {
for (np = NULL; (np = of_get_next_child(npp, np)) != NULL;) {
if (strncmp(np->name, "i2c", 3) == 0) {
of_platform_device_create(np, "uni-n-i2c",
NULL);
of_node_put(np);
break;
}
}
of_node_put(npp);
}
np = of_find_node_by_type(NULL, "smu");
if (np) {
of_platform_device_create(np, "smu", NULL);
......
......@@ -236,27 +236,17 @@ config I2C_IXP2000
This support is also available as a module. If so, the module
will be called i2c-ixp2000.
config I2C_KEYWEST
tristate "Powermac Keywest I2C interface"
config I2C_POWERMAC
tristate "Powermac I2C interface"
depends on I2C && PPC_PMAC
default y
help
This supports the use of the I2C interface in the combo-I/O
chip on recent Apple machines. Say Y if you have such a machine.
This exposes the various PowerMac i2c interfaces to the linux i2c
layer and to userland. It is used by various drivers on the powemac
platform, thus should generally be enabled.
This support is also available as a module. If so, the module
will be called i2c-keywest.
config I2C_PMAC_SMU
tristate "Powermac SMU I2C interface"
depends on I2C && PMAC_SMU
help
This supports the use of the I2C interface in the SMU
chip on recent Apple machines like the iMac G5. It is used
among others by the thermal control driver for those machines.
Say Y if you have such a machine.
This support is also available as a module. If so, the module
will be called i2c-pmac-smu.
will be called i2c-powermac.
config I2C_MPC
tristate "MPC107/824x/85xx/52xx"
......
......@@ -19,8 +19,7 @@ obj-$(CONFIG_I2C_ISA) += i2c-isa.o
obj-$(CONFIG_I2C_ITE) += i2c-ite.o
obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o
obj-$(CONFIG_I2C_IXP4XX) += i2c-ixp4xx.o
obj-$(CONFIG_I2C_KEYWEST) += i2c-keywest.o
obj-$(CONFIG_I2C_PMAC_SMU) += i2c-pmac-smu.o
obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o
......
This diff is collapsed.
#ifndef __I2C_KEYWEST_H__
#define __I2C_KEYWEST_H__
/* The Tumbler audio equalizer can be really slow sometimes */
#define POLL_TIMEOUT (2*HZ)
/* Register indices */
typedef enum {
reg_mode = 0,
reg_control,
reg_status,
reg_isr,
reg_ier,
reg_addr,
reg_subaddr,
reg_data
} reg_t;
/* Mode register */
#define KW_I2C_MODE_100KHZ 0x00
#define KW_I2C_MODE_50KHZ 0x01
#define KW_I2C_MODE_25KHZ 0x02
#define KW_I2C_MODE_DUMB 0x00
#define KW_I2C_MODE_STANDARD 0x04
#define KW_I2C_MODE_STANDARDSUB 0x08
#define KW_I2C_MODE_COMBINED 0x0C
#define KW_I2C_MODE_MODE_MASK 0x0C
#define KW_I2C_MODE_CHAN_MASK 0xF0
/* Control register */
#define KW_I2C_CTL_AAK 0x01
#define KW_I2C_CTL_XADDR 0x02
#define KW_I2C_CTL_STOP 0x04
#define KW_I2C_CTL_START 0x08
/* Status register */
#define KW_I2C_STAT_BUSY 0x01
#define KW_I2C_STAT_LAST_AAK 0x02
#define KW_I2C_STAT_LAST_RW 0x04
#define KW_I2C_STAT_SDA 0x08
#define KW_I2C_STAT_SCL 0x10
/* IER & ISR registers */
#define KW_I2C_IRQ_DATA 0x01
#define KW_I2C_IRQ_ADDR 0x02
#define KW_I2C_IRQ_STOP 0x04
#define KW_I2C_IRQ_START 0x08
#define KW_I2C_IRQ_MASK 0x0F
/* Physical interface */
struct keywest_iface
{
struct device_node *node;
void __iomem * base;
unsigned bsteps;
int irq;
spinlock_t lock;
struct keywest_chan *channels;
unsigned chan_count;
u8 cur_mode;
char read_write;
u8 *data;
unsigned datalen;
int state;
int result;
struct timer_list timeout_timer;
struct completion complete;
};
enum {
state_idle,
state_addr,
state_read,
state_write,
state_stop,
state_dead
};
/* Channel on an interface */
struct keywest_chan
{
struct i2c_adapter adapter;
struct keywest_iface* iface;
unsigned chan_no;
};
/* Register access */
static inline u8 __read_reg(struct keywest_iface *iface, reg_t reg)
{
return in_8(iface->base
+ (((unsigned)reg) << iface->bsteps));
}
static inline void __write_reg(struct keywest_iface *iface, reg_t reg, u8 val)
{
out_8(iface->base
+ (((unsigned)reg) << iface->bsteps), val);
(void)__read_reg(iface, reg_subaddr);
}
#define write_reg(reg, val) __write_reg(iface, reg, val)
#define read_reg(reg) __read_reg(iface, reg)
#endif /* __I2C_KEYWEST_H__ */
/*
i2c Support for Apple SMU Controller
Copyright (c) 2005 Benjamin Herrenschmidt, IBM Corp.
<benh@kernel.crashing.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <asm/prom.h>
#include <asm/of_device.h>
#include <asm/smu.h>
static int probe;
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("I2C driver for Apple's SMU");
MODULE_LICENSE("GPL");
module_param(probe, bool, 0);
/* Physical interface */
struct smu_iface
{
struct i2c_adapter adapter;
struct completion complete;
u32 busid;
};
static void smu_i2c_done(struct smu_i2c_cmd *cmd, void *misc)
{
struct smu_iface *iface = misc;
complete(&iface->complete);
}
/*
* SMBUS-type transfer entrypoint
*/
static s32 smu_smbus_xfer( struct i2c_adapter* adap,
u16 addr,
unsigned short flags,
char read_write,
u8 command,
int size,
union i2c_smbus_data* data)
{
struct smu_iface *iface = i2c_get_adapdata(adap);
struct smu_i2c_cmd cmd;
int rc = 0;
int read = (read_write == I2C_SMBUS_READ);
cmd.info.bus = iface->busid;
cmd.info.devaddr = (addr << 1) | (read ? 0x01 : 0x00);
/* Prepare datas & select mode */
switch (size) {
case I2C_SMBUS_QUICK:
cmd.info.type = SMU_I2C_TRANSFER_SIMPLE;
cmd.info.datalen = 0;
break;
case I2C_SMBUS_BYTE:
cmd.info.type = SMU_I2C_TRANSFER_SIMPLE;
cmd.info.datalen = 1;
if (!read)
cmd.info.data[0] = data->byte;
break;
case I2C_SMBUS_BYTE_DATA:
cmd.info.type = SMU_I2C_TRANSFER_STDSUB;
cmd.info.datalen = 1;
cmd.info.sublen = 1;
cmd.info.subaddr[0] = command;
cmd.info.subaddr[1] = 0;
cmd.info.subaddr[2] = 0;
if (!read)
cmd.info.data[0] = data->byte;
break;
case I2C_SMBUS_WORD_DATA:
cmd.info.type = SMU_I2C_TRANSFER_STDSUB;
cmd.info.datalen = 2;
cmd.info.sublen = 1;
cmd.info.subaddr[0] = command;
cmd.info.subaddr[1] = 0;
cmd.info.subaddr[2] = 0;
if (!read) {
cmd.info.data[0] = data->word & 0xff;
cmd.info.data[1] = (data->word >> 8) & 0xff;
}
break;
/* Note that these are broken vs. the expected smbus API where
* on reads, the lenght is actually returned from the function,
* but I think the current API makes no sense and I don't want
* any driver that I haven't verified for correctness to go
* anywhere near a pmac i2c bus anyway ...
*/
case I2C_SMBUS_BLOCK_DATA:
cmd.info.type = SMU_I2C_TRANSFER_STDSUB;
cmd.info.datalen = data->block[0] + 1;
if (cmd.info.datalen > (SMU_I2C_WRITE_MAX + 1))
return -EINVAL;
if (!read)
memcpy(cmd.info.data, data->block, cmd.info.datalen);
cmd.info.sublen = 1;
cmd.info.subaddr[0] = command;
cmd.info.subaddr[1] = 0;
cmd.info.subaddr[2] = 0;
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
cmd.info.type = SMU_I2C_TRANSFER_STDSUB;
cmd.info.datalen = data->block[0];
if (cmd.info.datalen > 7)
return -EINVAL;
if (!read)
memcpy(cmd.info.data, &data->block[1],
cmd.info.datalen);
cmd.info.sublen = 1;
cmd.info.subaddr[0] = command;
cmd.info.subaddr[1] = 0;
cmd.info.subaddr[2] = 0;
break;
default:
return -EINVAL;
}
/* Turn a standardsub read into a combined mode access */
if (read_write == I2C_SMBUS_READ &&
cmd.info.type == SMU_I2C_TRANSFER_STDSUB)
cmd.info.type = SMU_I2C_TRANSFER_COMBINED;
/* Finish filling command and submit it */
cmd.done = smu_i2c_done;
cmd.misc = iface;
rc = smu_queue_i2c(&cmd);
if (rc < 0)
return rc;
wait_for_completion(&iface->complete);
rc = cmd.status;
if (!read || rc < 0)
return rc;
switch (size) {
case I2C_SMBUS_BYTE:
case I2C_SMBUS_BYTE_DATA:
data->byte = cmd.info.data[0];
break;
case I2C_SMBUS_WORD_DATA:
data->word = ((u16)cmd.info.data[1]) << 8;
data->word |= cmd.info.data[0];
break;
/* Note that these are broken vs. the expected smbus API where
* on reads, the lenght is actually returned from the function,
* but I think the current API makes no sense and I don't want
* any driver that I haven't verified for correctness to go
* anywhere near a pmac i2c bus anyway ...
*/
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_I2C_BLOCK_DATA:
memcpy(&data->block[0], cmd.info.data, cmd.info.datalen);
break;
}
return rc;
}
static u32
smu_smbus_func(struct i2c_adapter * adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA;
}
/* For now, we only handle combined mode (smbus) */
static struct i2c_algorithm smu_algorithm = {
.smbus_xfer = smu_smbus_xfer,
.functionality = smu_smbus_func,
};
static int create_iface(struct device_node *np, struct device *dev)
{
struct smu_iface* iface;
u32 *reg, busid;
int rc;
reg = (u32 *)get_property(np, "reg", NULL);
if (reg == NULL) {
printk(KERN_ERR "i2c-pmac-smu: can't find bus number !\n");
return -ENXIO;
}
busid = *reg;
iface = kzalloc(sizeof(struct smu_iface), GFP_KERNEL);
if (iface == NULL) {
printk(KERN_ERR "i2c-pmac-smu: can't allocate inteface !\n");
return -ENOMEM;
}
init_completion(&iface->complete);
iface->busid = busid;
dev_set_drvdata(dev, iface);
sprintf(iface->adapter.name, "smu-i2c-%02x", busid);
iface->adapter.algo = &smu_algorithm;
iface->adapter.algo_data = NULL;
iface->adapter.client_register = NULL;
iface->adapter.client_unregister = NULL;
i2c_set_adapdata(&iface->adapter, iface);
iface->adapter.dev.parent = dev;
rc = i2c_add_adapter(&iface->adapter);
if (rc) {
printk(KERN_ERR "i2c-pamc-smu.c: Adapter %s registration "
"failed\n", iface->adapter.name);
i2c_set_adapdata(&iface->adapter, NULL);
}
if (probe) {
unsigned char addr;
printk("Probe: ");
for (addr = 0x00; addr <= 0x7f; addr++) {
if (i2c_smbus_xfer(&iface->adapter,addr,
0,0,0,I2C_SMBUS_QUICK,NULL) >= 0)
printk("%02x ", addr);
}
printk("\n");
}
printk(KERN_INFO "SMU i2c bus %x registered\n", busid);
return 0;
}
static int dispose_iface(struct device *dev)
{
struct smu_iface *iface = dev_get_drvdata(dev);
int rc;
rc = i2c_del_adapter(&iface->adapter);
i2c_set_adapdata(&iface->adapter, NULL);
/* We aren't that prepared to deal with this... */
if (rc)
printk("i2c-pmac-smu.c: Failed to remove bus %s !\n",
iface->adapter.name);
dev_set_drvdata(dev, NULL);
kfree(iface);
return 0;
}
static int create_iface_of_platform(struct of_device* dev,
const struct of_device_id *match)
{
struct device_node *node = dev->node;
if (device_is_compatible(node, "smu-i2c") ||
(node->parent != NULL &&
device_is_compatible(node->parent, "smu-i2c-control")))
return create_iface(node, &dev->dev);
return -ENODEV;
}
static int dispose_iface_of_platform(struct of_device* dev)
{
return dispose_iface(&dev->dev);
}
static struct of_device_id i2c_smu_match[] =
{
{
.compatible = "smu-i2c",
},
{
.compatible = "i2c-bus",
},
{},
};
static struct of_platform_driver i2c_smu_of_platform_driver =
{
.name = "i2c-smu",
.match_table = i2c_smu_match,
.probe = create_iface_of_platform,
.remove = dispose_iface_of_platform
};
static int __init i2c_pmac_smu_init(void)
{
of_register_driver(&i2c_smu_of_platform_driver);
return 0;
}
static void __exit i2c_pmac_smu_cleanup(void)
{
of_unregister_driver(&i2c_smu_of_platform_driver);
}
module_init(i2c_pmac_smu_init);
module_exit(i2c_pmac_smu_cleanup);
/*
i2c Support for Apple SMU Controller
Copyright (c) 2005 Benjamin Herrenschmidt, IBM Corp.
<benh@kernel.crashing.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <asm/prom.h>
#include <asm/pmac_low_i2c.h>
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("I2C driver for Apple PowerMac");
MODULE_LICENSE("GPL");
/*
* SMBUS-type transfer entrypoint
*/
static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap,
u16 addr,
unsigned short flags,
char read_write,
u8 command,
int size,
union i2c_smbus_data* data)
{
struct pmac_i2c_bus *bus = i2c_get_adapdata(adap);
int rc = 0;
int read = (read_write == I2C_SMBUS_READ);
int addrdir = (addr << 1) | read;
u8 local[2];
rc = pmac_i2c_open(bus, 0);
if (rc)
return rc;
switch (size) {
case I2C_SMBUS_QUICK:
rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std);
if (rc)
goto bail;
rc = pmac_i2c_xfer(bus, addrdir, 0, 0, NULL, 0);
break;
case I2C_SMBUS_BYTE:
rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std);
if (rc)
goto bail;
rc = pmac_i2c_xfer(bus, addrdir, 0, 0, &data->byte, 1);
break;
case I2C_SMBUS_BYTE_DATA:
rc = pmac_i2c_setmode(bus, read ?
pmac_i2c_mode_combined :
pmac_i2c_mode_stdsub);
if (rc)
goto bail;
rc = pmac_i2c_xfer(bus, addrdir, 1, command, &data->byte, 1);
break;
case I2C_SMBUS_WORD_DATA:
rc = pmac_i2c_setmode(bus, read ?
pmac_i2c_mode_combined :
pmac_i2c_mode_stdsub);
if (rc)
goto bail;
if (!read) {
local[0] = data->word & 0xff;
local[1] = (data->word >> 8) & 0xff;
}
rc = pmac_i2c_xfer(bus, addrdir, 1, command, local, 2);
if (rc == 0 && read) {
data->word = ((u16)local[1]) << 8;
data->word |= local[0];
}
break;
/* Note that these are broken vs. the expected smbus API where
* on reads, the lenght is actually returned from the function,
* but I think the current API makes no sense and I don't want
* any driver that I haven't verified for correctness to go
* anywhere near a pmac i2c bus anyway ...
*
* I'm also not completely sure what kind of phases to do between
* the actual command and the data (what I am _supposed_ to do that
* is). For now, I assume writes are a single stream and reads have
* a repeat start/addr phase (but not stop in between)
*/
case I2C_SMBUS_BLOCK_DATA:
rc = pmac_i2c_setmode(bus, read ?
pmac_i2c_mode_combined :
pmac_i2c_mode_stdsub);
if (rc)
goto bail;
rc = pmac_i2c_xfer(bus, addrdir, 1, command, data->block,
data->block[0] + 1);
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
rc = pmac_i2c_setmode(bus, read ?
pmac_i2c_mode_combined :
pmac_i2c_mode_stdsub);
if (rc)
goto bail;
rc = pmac_i2c_xfer(bus, addrdir, 1, command,
read ? data->block : &data->block[1],
data->block[0]);
break;
default:
rc = -EINVAL;
}
bail:
pmac_i2c_close(bus);
return rc;
}
/*
* Generic i2c master transfer entrypoint. This driver only support single
* messages (for "lame i2c" transfers). Anything else should use the smbus
* entry point
*/
static int i2c_powermac_master_xfer( struct i2c_adapter *adap,
struct i2c_msg *msgs,
int num)
{
struct pmac_i2c_bus *bus = i2c_get_adapdata(adap);
int rc = 0;
int read;
int addrdir;
if (num != 1)
return -EINVAL;
if (msgs->flags & I2C_M_TEN)
return -EINVAL;
read = (msgs->flags & I2C_M_RD) != 0;
addrdir = (msgs->addr << 1) | read;
if (msgs->flags & I2C_M_REV_DIR_ADDR)
addrdir ^= 1;
rc = pmac_i2c_open(bus, 0);
if (rc)
return rc;
rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std);
if (rc)
goto bail;
rc = pmac_i2c_xfer(bus, addrdir, 0, 0, msgs->buf, msgs->len);
bail:
pmac_i2c_close(bus);
return rc < 0 ? rc : msgs->len;
}
static u32 i2c_powermac_func(struct i2c_adapter * adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_I2C;
}
/* For now, we only handle smbus */
static struct i2c_algorithm i2c_powermac_algorithm = {
.smbus_xfer = i2c_powermac_smbus_xfer,
.master_xfer = i2c_powermac_master_xfer,
.functionality = i2c_powermac_func,
};
static int i2c_powermac_remove(struct device *dev)
{
struct i2c_adapter *adapter = dev_get_drvdata(dev);
struct pmac_i2c_bus *bus = i2c_get_adapdata(adapter);
int rc;
rc = i2c_del_adapter(adapter);
pmac_i2c_detach_adapter(bus, adapter);
i2c_set_adapdata(adapter, NULL);
/* We aren't that prepared to deal with this... */
if (rc)
printk("i2c-powermac.c: Failed to remove bus %s !\n",
adapter->name);
dev_set_drvdata(dev, NULL);
kfree(adapter);
return 0;
}
static int i2c_powermac_probe(struct device *dev)
{
struct pmac_i2c_bus *bus = dev->platform_data;
struct device_node *parent = NULL;
struct i2c_adapter *adapter;
char name[32], *basename;
int rc;
if (bus == NULL)
return -EINVAL;
/* Ok, now we need to make up a name for the interface that will
* match what we used to do in the past, that is basically the
* controller's parent device node for keywest. PMU didn't have a
* naming convention and SMU has a different one
*/
switch(pmac_i2c_get_type(bus)) {
case pmac_i2c_bus_keywest:
parent = of_get_parent(pmac_i2c_get_controller(bus));
if (parent == NULL)
return -EINVAL;
basename = parent->name;
break;
case pmac_i2c_bus_pmu:
basename = "pmu";
break;
case pmac_i2c_bus_smu:
/* This is not what we used to do but I'm fixing drivers at
* the same time as this change
*/
basename = "smu";
break;
default:
return -EINVAL;
}
snprintf(name, 32, "%s %d", basename, pmac_i2c_get_channel(bus));
of_node_put(parent);
adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
if (adapter == NULL) {
printk(KERN_ERR "i2c-powermac: can't allocate inteface !\n");
return -ENOMEM;
}
dev_set_drvdata(dev, adapter);
strcpy(adapter->name, name);
adapter->algo = &i2c_powermac_algorithm;
i2c_set_adapdata(adapter, bus);
adapter->dev.parent = dev;
pmac_i2c_attach_adapter(bus, adapter);
rc = i2c_add_adapter(adapter);
if (rc) {
printk(KERN_ERR "i2c-powermac: Adapter %s registration "
"failed\n", name);
i2c_set_adapdata(adapter, NULL);
pmac_i2c_detach_adapter(bus, adapter);
}
printk(KERN_INFO "PowerMac i2c bus %s registered\n", name);
return rc;
}
static struct device_driver i2c_powermac_driver = {
.name = "i2c-powermac",
.bus = &platform_bus_type,
.probe = i2c_powermac_probe,
.remove = i2c_powermac_remove,
};
static int __init i2c_powermac_init(void)
{
driver_register(&i2c_powermac_driver);
return 0;
}
static void __exit i2c_powermac_cleanup(void)
{
driver_unregister(&i2c_powermac_driver);
}
module_init(i2c_powermac_init);
module_exit(i2c_powermac_cleanup);
......@@ -149,14 +149,14 @@ config MAC_EMUMOUSEBTN
config THERM_WINDTUNNEL
tristate "Support for thermal management on Windtunnel G4s"
depends on I2C && I2C_KEYWEST && PPC_PMAC && !PPC_PMAC64
depends on I2C && I2C_POWERMAC && PPC_PMAC && !PPC_PMAC64
help
This driver provides some thermostat and fan control for the desktop
G4 "Windtunnel"
config THERM_ADT746X
tristate "Support for thermal mgmnt on laptops with ADT 746x chipset"
depends on I2C && I2C_KEYWEST && PPC_PMAC && !PPC_PMAC64
depends on I2C && I2C_POWERMAC && PPC_PMAC && !PPC_PMAC64
help
This driver provides some thermostat and fan control for the
iBook G4, and the ATI based aluminium PowerBooks, allowing slighlty
......@@ -164,7 +164,7 @@ config THERM_ADT746X
config THERM_PM72
tristate "Support for thermal management on PowerMac G5"
depends on I2C && I2C_KEYWEST && PPC_PMAC64
depends on I2C && I2C_POWERMAC && PPC_PMAC64
help
This driver provides thermostat and fan control for the desktop
G5 machines.
......@@ -175,14 +175,14 @@ config WINDFARM
config WINDFARM_PM81
tristate "Support for thermal management on iMac G5"
depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && PMAC_SMU
select I2C_PMAC_SMU
select I2C_POWERMAC
help
This driver provides thermal control for the iMacG5
config WINDFARM_PM91
tristate "Support for thermal management on PowerMac9,1"
depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && PMAC_SMU
select I2C_PMAC_SMU
select I2C_POWERMAC
help
This driver provides thermal control for the PowerMac9,1
which is the recent (SMU based) single CPU desktop G5
......
......@@ -584,34 +584,14 @@ core_initcall(smu_late_init);
* sysfs visibility
*/
static void smu_create_i2c(struct device_node *np)
{
char name[32];
u32 *reg = (u32 *)get_property(np, "reg", NULL);
if (reg != NULL) {
sprintf(name, "smu-i2c-%02x", *reg);
of_platform_device_create(np, name, &smu->of_dev->dev);
}
}
static void smu_expose_childs(void *unused)
{
struct device_node *np, *gp;
for (np = NULL; (np = of_get_next_child(smu->of_node, np)) != NULL;) {
if (device_is_compatible(np, "smu-i2c-control")) {
gp = NULL;
while ((gp = of_get_next_child(np, gp)) != NULL)
if (device_is_compatible(gp, "i2c-bus"))
smu_create_i2c(gp);
} else if (device_is_compatible(np, "smu-i2c"))
smu_create_i2c(np);
struct device_node *np;
for (np = NULL; (np = of_get_next_child(smu->of_node, np)) != NULL;)
if (device_is_compatible(np, "smu-sensors"))
of_platform_device_create(np, "smu-sensors",
&smu->of_dev->dev);
}
}
static DECLARE_WORK(smu_expose_childs_work, smu_expose_childs, NULL);
......
......@@ -21,6 +21,7 @@
#include <asm/io.h>
#include <asm/system.h>
#include <asm/sections.h>
#include <asm/pmac_low_i2c.h>
#include "windfarm.h"
......@@ -157,53 +158,21 @@ static struct wf_lm75_sensor *wf_lm75_create(struct i2c_adapter *adapter,
static int wf_lm75_attach(struct i2c_adapter *adapter)
{
u8 bus_id;
struct device_node *smu, *bus, *dev;
/* We currently only deal with LM75's hanging off the SMU
* i2c busses. If we extend that driver to other/older
* machines, we should split this function into SMU-i2c,
* keywest-i2c, PMU-i2c, ...
*/
struct device_node *busnode, *dev;
struct pmac_i2c_bus *bus;
DBG("wf_lm75: adapter %s detected\n", adapter->name);
if (strncmp(adapter->name, "smu-i2c-", 8) != 0)
return 0;
smu = of_find_node_by_type(NULL, "smu");
if (smu == NULL)
return 0;
/* Look for the bus in the device-tree */
bus_id = (u8)simple_strtoul(adapter->name + 8, NULL, 16);
DBG("wf_lm75: bus ID is %x\n", bus_id);
/* Look for sensors subdir */
for (bus = NULL;
(bus = of_get_next_child(smu, bus)) != NULL;) {
u32 *reg;
if (strcmp(bus->name, "i2c"))
continue;
reg = (u32 *)get_property(bus, "reg", NULL);
if (reg == NULL)
continue;
if (bus_id == *reg)
break;
}
of_node_put(smu);
if (bus == NULL) {
printk(KERN_WARNING "windfarm: SMU i2c bus 0x%x not found"
" in device-tree !\n", bus_id);
return 0;
}
bus = pmac_i2c_adapter_to_bus(adapter);
if (bus == NULL)
return -ENODEV;
busnode = pmac_i2c_get_bus_node(bus);
DBG("wf_lm75: bus found, looking for device...\n");
/* Now look for lm75(s) in there */
for (dev = NULL;
(dev = of_get_next_child(bus, dev)) != NULL;) {
(dev = of_get_next_child(busnode, dev)) != NULL;) {
const char *loc =
get_property(dev, "hwsensor-location", NULL);
u32 *reg = (u32 *)get_property(dev, "reg", NULL);
......@@ -217,9 +186,6 @@ static int wf_lm75_attach(struct i2c_adapter *adapter)
else if (device_is_compatible(dev, "ds1775"))
wf_lm75_create(adapter, *reg, 1, loc);
}
of_node_put(bus);
return 0;
}
......
......@@ -70,6 +70,7 @@ extern struct device_node *pmac_i2c_get_controller(struct pmac_i2c_bus *bus);
extern struct device_node *pmac_i2c_get_bus_node(struct pmac_i2c_bus *bus);
extern int pmac_i2c_get_type(struct pmac_i2c_bus *bus);
extern int pmac_i2c_get_flags(struct pmac_i2c_bus *bus);
extern int pmac_i2c_get_channel(struct pmac_i2c_bus *bus);
/* i2c layer adapter attach/detach */
extern void pmac_i2c_attach_adapter(struct pmac_i2c_bus *bus,
......@@ -77,6 +78,7 @@ extern void pmac_i2c_attach_adapter(struct pmac_i2c_bus *bus,
extern void pmac_i2c_detach_adapter(struct pmac_i2c_bus *bus,
struct i2c_adapter *adapter);
extern struct i2c_adapter *pmac_i2c_get_adapter(struct pmac_i2c_bus *bus);
extern struct pmac_i2c_bus *pmac_i2c_adapter_to_bus(struct i2c_adapter *adapter);
/* March a device or bus with an i2c adapter structure, to be used by drivers
* to match device-tree nodes with i2c adapters during adapter discovery
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment