/* * 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 #include #include #include #include #include #include #include #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