forked from Minki/linux
a28d3af2a2
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: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
225 lines
5.1 KiB
C
225 lines
5.1 KiB
C
/*
|
|
* Windfarm PowerMac thermal control. LM75 sensor
|
|
*
|
|
* (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
|
|
* <benh@kernel.crashing.org>
|
|
*
|
|
* Released under the term of the GNU GPL v2.
|
|
*/
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/init.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/i2c-dev.h>
|
|
#include <asm/prom.h>
|
|
#include <asm/machdep.h>
|
|
#include <asm/io.h>
|
|
#include <asm/system.h>
|
|
#include <asm/sections.h>
|
|
#include <asm/pmac_low_i2c.h>
|
|
|
|
#include "windfarm.h"
|
|
|
|
#define VERSION "0.1"
|
|
|
|
#undef DEBUG
|
|
|
|
#ifdef DEBUG
|
|
#define DBG(args...) printk(args)
|
|
#else
|
|
#define DBG(args...) do { } while(0)
|
|
#endif
|
|
|
|
struct wf_lm75_sensor {
|
|
int ds1775 : 1;
|
|
int inited : 1;
|
|
struct i2c_client i2c;
|
|
struct wf_sensor sens;
|
|
};
|
|
#define wf_to_lm75(c) container_of(c, struct wf_lm75_sensor, sens)
|
|
#define i2c_to_lm75(c) container_of(c, struct wf_lm75_sensor, i2c)
|
|
|
|
static int wf_lm75_attach(struct i2c_adapter *adapter);
|
|
static int wf_lm75_detach(struct i2c_client *client);
|
|
|
|
static struct i2c_driver wf_lm75_driver = {
|
|
.driver = {
|
|
.name = "wf_lm75",
|
|
},
|
|
.attach_adapter = wf_lm75_attach,
|
|
.detach_client = wf_lm75_detach,
|
|
};
|
|
|
|
static int wf_lm75_get(struct wf_sensor *sr, s32 *value)
|
|
{
|
|
struct wf_lm75_sensor *lm = wf_to_lm75(sr);
|
|
s32 data;
|
|
|
|
if (lm->i2c.adapter == NULL)
|
|
return -ENODEV;
|
|
|
|
/* Init chip if necessary */
|
|
if (!lm->inited) {
|
|
u8 cfg_new, cfg = (u8)i2c_smbus_read_byte_data(&lm->i2c, 1);
|
|
|
|
DBG("wf_lm75: Initializing %s, cfg was: %02x\n",
|
|
sr->name, cfg);
|
|
|
|
/* clear shutdown bit, keep other settings as left by
|
|
* the firmware for now
|
|
*/
|
|
cfg_new = cfg & ~0x01;
|
|
i2c_smbus_write_byte_data(&lm->i2c, 1, cfg_new);
|
|
lm->inited = 1;
|
|
|
|
/* If we just powered it up, let's wait 200 ms */
|
|
msleep(200);
|
|
}
|
|
|
|
/* Read temperature register */
|
|
data = (s32)le16_to_cpu(i2c_smbus_read_word_data(&lm->i2c, 0));
|
|
data <<= 8;
|
|
*value = data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void wf_lm75_release(struct wf_sensor *sr)
|
|
{
|
|
struct wf_lm75_sensor *lm = wf_to_lm75(sr);
|
|
|
|
/* check if client is registered and detach from i2c */
|
|
if (lm->i2c.adapter) {
|
|
i2c_detach_client(&lm->i2c);
|
|
lm->i2c.adapter = NULL;
|
|
}
|
|
|
|
kfree(lm);
|
|
}
|
|
|
|
static struct wf_sensor_ops wf_lm75_ops = {
|
|
.get_value = wf_lm75_get,
|
|
.release = wf_lm75_release,
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
static struct wf_lm75_sensor *wf_lm75_create(struct i2c_adapter *adapter,
|
|
u8 addr, int ds1775,
|
|
const char *loc)
|
|
{
|
|
struct wf_lm75_sensor *lm;
|
|
|
|
DBG("wf_lm75: creating %s device at address 0x%02x\n",
|
|
ds1775 ? "ds1775" : "lm75", addr);
|
|
|
|
lm = kmalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL);
|
|
if (lm == NULL)
|
|
return NULL;
|
|
memset(lm, 0, sizeof(struct wf_lm75_sensor));
|
|
|
|
/* Usual rant about sensor names not beeing very consistent in
|
|
* the device-tree, oh well ...
|
|
* Add more entries below as you deal with more setups
|
|
*/
|
|
if (!strcmp(loc, "Hard drive") || !strcmp(loc, "DRIVE BAY"))
|
|
lm->sens.name = "hd-temp";
|
|
else
|
|
goto fail;
|
|
|
|
lm->inited = 0;
|
|
lm->sens.ops = &wf_lm75_ops;
|
|
lm->ds1775 = ds1775;
|
|
lm->i2c.addr = (addr >> 1) & 0x7f;
|
|
lm->i2c.adapter = adapter;
|
|
lm->i2c.driver = &wf_lm75_driver;
|
|
strncpy(lm->i2c.name, lm->sens.name, I2C_NAME_SIZE-1);
|
|
|
|
if (i2c_attach_client(&lm->i2c)) {
|
|
printk(KERN_ERR "windfarm: failed to attach %s %s to i2c\n",
|
|
ds1775 ? "ds1775" : "lm75", lm->i2c.name);
|
|
goto fail;
|
|
}
|
|
|
|
if (wf_register_sensor(&lm->sens)) {
|
|
i2c_detach_client(&lm->i2c);
|
|
goto fail;
|
|
}
|
|
|
|
return lm;
|
|
fail:
|
|
kfree(lm);
|
|
return NULL;
|
|
}
|
|
|
|
static int wf_lm75_attach(struct i2c_adapter *adapter)
|
|
{
|
|
struct device_node *busnode, *dev;
|
|
struct pmac_i2c_bus *bus;
|
|
|
|
DBG("wf_lm75: adapter %s detected\n", adapter->name);
|
|
|
|
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(busnode, dev)) != NULL;) {
|
|
const char *loc =
|
|
get_property(dev, "hwsensor-location", NULL);
|
|
u32 *reg = (u32 *)get_property(dev, "reg", NULL);
|
|
DBG(" dev: %s... (loc: %p, reg: %p)\n", dev->name, loc, reg);
|
|
if (loc == NULL || reg == NULL)
|
|
continue;
|
|
/* real lm75 */
|
|
if (device_is_compatible(dev, "lm75"))
|
|
wf_lm75_create(adapter, *reg, 0, loc);
|
|
/* ds1775 (compatible, better resolution */
|
|
else if (device_is_compatible(dev, "ds1775"))
|
|
wf_lm75_create(adapter, *reg, 1, loc);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int wf_lm75_detach(struct i2c_client *client)
|
|
{
|
|
struct wf_lm75_sensor *lm = i2c_to_lm75(client);
|
|
|
|
DBG("wf_lm75: i2c detatch called for %s\n", lm->sens.name);
|
|
|
|
/* Mark client detached */
|
|
lm->i2c.adapter = NULL;
|
|
|
|
/* release sensor */
|
|
wf_unregister_sensor(&lm->sens);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __init wf_lm75_sensor_init(void)
|
|
{
|
|
return i2c_add_driver(&wf_lm75_driver);
|
|
}
|
|
|
|
static void __exit wf_lm75_sensor_exit(void)
|
|
{
|
|
i2c_del_driver(&wf_lm75_driver);
|
|
}
|
|
|
|
|
|
module_init(wf_lm75_sensor_init);
|
|
module_exit(wf_lm75_sensor_exit);
|
|
|
|
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
|
|
MODULE_DESCRIPTION("LM75 sensor objects for PowerMacs thermal control");
|
|
MODULE_LICENSE("GPL");
|
|
|