blob: 3449e59ac8143f4f1bc9c94a6b738a522a1262b1 [file] [log] [blame]
/*
* Copyright (C) 2001,2002,2003 Broadcom Corporation
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* SMBus/I2C device driver for the MAX1617 temperature sensor
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-sibyte.h>
#define IF_NAME "max1617"
#define MAX1617_SMBUS_DEV 0x2A
#define MAX1617_LOCAL 0
#define MAX1617_REMOTE 1
#define MAX1617_STATUS 2
#define MAX1617_POLL_PERIOD 10
static int max1617_verbose = 0;
static int max1617_polling = 1;
/* Addresses to scan */
static unsigned short normal_i2c[] = {MAX1617_SMBUS_DEV, I2C_CLIENT_END};
static unsigned short normal_i2c_range[] = {I2C_CLIENT_END};
static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
normal_i2c, normal_i2c_range,
probe, probe_range,
ignore, ignore_range,
force
};
struct max1617_info {
struct i2c_client *client;
struct timer_list timer;
int local;
int remote;
};
static int max1617_probe(struct i2c_adapter *adap);
static int max1617_detach(struct i2c_client *device);
static int max1617_command(struct i2c_client *device, unsigned int cmd, void *arg);
static void max1617_inc_use(struct i2c_client *device);
static void max1617_dec_use(struct i2c_client *device);
struct i2c_driver i2c_driver_max1617 = {
name: IF_NAME,
id: I2C_DRIVERID_MAX1617,
flags: I2C_DF_NOTIFY,
attach_adapter: max1617_probe,
detach_client: max1617_detach,
command: max1617_command,
inc_use: max1617_inc_use,
dec_use: max1617_dec_use
};
\
static int max1617_read(struct i2c_client *client, unsigned char subaddr)
{
return i2c_smbus_read_byte_data(client, subaddr);
}
/* poll the device, check for temperature/status changes */
static void max1617_update(unsigned long arg)
{
struct max1617_info *m = (struct max1617_info *)arg;
int status, remote, local;
char statstr[50];
status = max1617_read(m->client, MAX1617_STATUS);
remote = max1617_read(m->client, MAX1617_REMOTE);
local = max1617_read(m->client, MAX1617_LOCAL);
if (status < 0 || remote < 0 || local < 0) {
printk(KERN_ERR IF_NAME ": sensor device did not respond.\n");
} else {
statstr[0] = 0;
if (status & 0x80) strcat(statstr,"Busy ");
if (status & 0x40) strcat(statstr,"HiTempLcl ");
if (status & 0x20) strcat(statstr,"LoTempLcl ");
if (status & 0x10) strcat(statstr,"HiTempRem ");
if (status & 0x08) strcat(statstr,"LoTempRem ");
if (status & 0x04) strcat(statstr,"Fault ");
if (max1617_verbose || (local != m->local) || (remote != m->remote)) {
printk(KERN_DEBUG IF_NAME ": Temperature - CPU: %dC Board: %dC Status:%02X [ %s]\n",
remote, local, status, statstr);
}
m->local = local;
m->remote = remote;
mod_timer(&m->timer, jiffies + (HZ * MAX1617_POLL_PERIOD));
}
}
/* attach to an instance of the device that was probed on a bus */
static int max1617_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind)
{
struct max1617_info *m;
struct i2c_client *client;
int err;
client = kmalloc(sizeof(*client), GFP_KERNEL);
if (client == NULL)
return -ENOMEM;
client->adapter = adap;
client->addr = addr;
client->driver = &i2c_driver_max1617;
sprintf(client->name, "%s-%x", IF_NAME, addr);
if ((err = i2c_attach_client(client)) < 0) {
kfree(client);
return err;
}
m = kmalloc(sizeof(*m), GFP_KERNEL);
if (m == NULL) {
i2c_detach_client(client);
kfree(client);
return -ENOMEM;
}
m->client = client;
m->remote = m->local = 0;
init_timer(&m->timer);
m->timer.data = (unsigned long)m;
m->timer.function = max1617_update;
if (max1617_polling) {
m->timer.expires = jiffies + (HZ * MAX1617_POLL_PERIOD);
add_timer(&m->timer);
}
client->data = m;
return 0;
}
/* initiate probing on a particular bus */
static int max1617_probe(struct i2c_adapter *adap)
{
/* Look for this device on the given adapter (bus) */
if (adap->id == (I2C_ALGO_SIBYTE | I2C_HW_SIBYTE))
return i2c_probe(adap, &addr_data, &max1617_attach);
else
return 0;
}
static int max1617_detach(struct i2c_client *device)
{
struct max1617_info *m = (struct max1617_info *)device->data;
int rc = 0;
if ((rc = i2c_detach_client(device)) != 0) {
printk(IF_NAME "detach failed: %d\n", rc);
} else {
kfree(device);
if (max1617_polling)
del_timer(&m->timer);
kfree(m);
}
return rc;
}
static int max1617_command(struct i2c_client *device, unsigned int cmd, void *arg)
{
return 0;
}
static void max1617_inc_use(struct i2c_client *client)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void max1617_dec_use(struct i2c_client *client)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
void i2c_max1617_init(void)
{
i2c_add_driver(&i2c_driver_max1617);
}
EXPORT_NO_SYMBOLS;
#ifdef MODULE
MODULE_AUTHOR("Kip Walker, Broadcom Corp.");
MODULE_DESCRIPTION("Max 1617 temperature sensor for SiByte SOC boards");
MODULE_LICENSE("GPL");
int init_module(void)
{
i2c_max1617_init();
return 0;
}
void cleanup_module(void)
{
i2c_del_driver(&i2c_driver_max1617);
}
#endif