/* mic74.c - Intended to become part of lm_sensors, Linux kernel modules for hardware monitoring 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. */ /* Supports Micrel MIC74 */ /* A couple notes about the MIC74: */ #include #include #include #include #include #include #include #include #include #include #define LM_DATE "20011118" #define LM_VERSION "2.6.2" #include #include #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) #define init_MUTEX(s) do { *(s) = MUTEX; } while(0) #endif #ifndef THIS_MODULE #define THIS_MODULE NULL #endif /* Addresses to scan */ static unsigned short normal_i2c[] = { SENSORS_I2C_END }; static unsigned short normal_i2c_range[] = { 0x20, 0x27, SENSORS_I2C_END }; static unsigned int normal_isa[] = { SENSORS_ISA_END }; static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; /* Insmod parameters */ SENSORS_INSMOD_1(mic74); /* The mic74 registers */ #define MIC74_REG_CONFIG 0x00 #define MIC74_REG_DATA_DIR 0x01 #define MIC74_REG_OUT_CFG 0x02 #define MIC74_REG_STATUS 0x03 #define MIC74_REG_INT_MASK 0x04 #define MIC74_REG_DATA 0x05 #define MIC74_REG_FAN_SPEED 0x06 /* Initial values */ /* All registers are 0, except for MIC74_REG_DATA which is 0xFF */ #ifdef MODULE extern int init_module(void); extern int cleanup_module(void); #endif /* MODULE */ /* For each registered MIC74, we need to keep some data in memory. That data is pointed to by mic74_list[NR]->data. The structure itself is dynamically allocated, at the same time when a new mic74 client is allocated. */ struct mic74_data { int sysctl_id; enum chips type; struct semaphore update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ u8 config; /* Register value */ u8 dataDir; /* Register value */ u8 outCfg; /* Register value */ u8 status; /* Register value */ u8 intMask; /* Register value */ u8 data; /* Register value */ u8 fanSpeed; /* Register value */ }; #ifdef MODULE static #else extern #endif int __init sensors_mic74_init(void); static int __init mic74_cleanup(void); static int mic74_attach_adapter(struct i2c_adapter *adapter); static int mic74_detect(struct i2c_adapter *adapter, int address, unsigned short flags, int kind); static int mic74_detach_client(struct i2c_client *client); static int mic74_command(struct i2c_client *client, unsigned int cmd, void *arg); static void mic74_inc_use(struct i2c_client *client); static void mic74_dec_use(struct i2c_client *client); static int mic74_read_value(struct i2c_client *client, u8 register); static int mic74_write_value(struct i2c_client *client, u8 register, u8 value); static void mic74_update_client(struct i2c_client *client); static void mic74_init_client(struct i2c_client *client); static void mic74_reg_rw(struct i2c_client *client, int operation, int ctl_name, int *nrels_mag, long *results); static void mic74_reg_ro(struct i2c_client *client, int operation, int ctl_name, int *nrels_mag, long *results); /* I choose here for semi-static MIC74 allocation. Complete dynamic allocation could also be used; the code needed for this would probably take more memory than the datastructure takes now. */ static int mic74_id = 0; static struct i2c_driver mic74_driver = { /* name */ "MIC74 sensor driver", /* id */ I2C_DRIVERID_MIC74, /* flags */ I2C_DF_NOTIFY, /* attach_adapter */ &mic74_attach_adapter, /* detach_client */ &mic74_detach_client, /* command */ &mic74_command, /* inc_use */ &mic74_inc_use, /* dec_use */ &mic74_dec_use }; /* Used by mic74_init/cleanup */ static int __initdata mic74_initialized = 0; /* The /proc/sys entries */ /* These files are created for each detected MIC74. This is just a template; though at first sight, you might think we could use a statically allocated list, we need some way to get back to the parent - which is done through one of the 'extra' fields which are initialized when a new copy is allocated. */ static ctl_table mic74_dir_table_template[] = { {MIC74_SYSCTL_REG0, /* ctl_name */ "dev_cfg", /* procname */ NULL, /* data */ 0, /* maxlen */ 0644, /* mode */ NULL, /* child */ &i2c_proc_real, /* proc_handler */ &i2c_sysctl_real, /* strategy */ NULL, /* de */ &mic74_reg_rw }, /* extra1, extra2 is by default NULL? */ {MIC74_SYSCTL_REG1, "dir", NULL, 0, 0644, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &mic74_reg_rw }, {MIC74_SYSCTL_REG2, "out_cfg", NULL, 0, 0644, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &mic74_reg_rw }, {MIC74_SYSCTL_REG3, "status", NULL, 0, 0444, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &mic74_reg_ro }, {MIC74_SYSCTL_REG4, "int_mask", NULL, 0, 0644, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &mic74_reg_rw }, {MIC74_SYSCTL_REG5, "data", NULL, 0, 0644, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &mic74_reg_rw }, {MIC74_SYSCTL_REG6, "fan_speed", NULL, 0, 0644, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &mic74_reg_rw }, {0} }; int mic74_attach_adapter(struct i2c_adapter *adapter) { return i2c_detect(adapter, &addr_data, mic74_detect); } static int mic74_detect(struct i2c_adapter *adapter, int address, unsigned short flags, int kind) { int i; struct i2c_client *new_client; struct mic74_data *data; int err = 0; const char *type_name = ""; const char *client_name = ""; /* Make sure we aren't probing the ISA bus!! This is just a safety check at this moment; i2c_detect really won't call us. */ #ifdef DEBUG if (i2c_is_isa_adapter(adapter)) { printk ("mic74.o: mic74_detect called for an ISA bus adapter?!?\n"); return 0; } #endif if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) goto ERROR0; /* OK. For now, we presume we have a valid client. We now create the client structure, even though we cannot fill it completely yet. But it allows us to access mic74_{read,write}_value. */ if (!(new_client = kmalloc(sizeof(struct i2c_client) + sizeof(struct mic74_data), GFP_KERNEL))) { err = -ENOMEM; goto ERROR0; } data = (struct mic74_data *) (new_client + 1); new_client->addr = address; new_client->data = data; new_client->adapter = adapter; new_client->driver = &mic74_driver; new_client->flags = 0; #ifdef CONFIG_AMAZON /* make sure we can search for the client later */ new_client->flags |= I2C_CLIENT_ALLOW_USE; #endif /* Determine the chip type - only one kind */ if (kind <= 0) kind = mic74; if (kind == mic74) { type_name = "mic74"; client_name = "MIC74 chip"; } else { #ifdef DEBUG printk("mic74.o: Internal error: unknown kind (%d)?!?", kind); #endif goto ERROR1; } /* Fill in the remaining client fields and put it into the global list */ strcpy(new_client->name, client_name); data->type = kind; new_client->id = mic74_id++; data->valid = 0; init_MUTEX(&data->update_lock); /* Tell the I2C layer a new client has arrived */ if ((err = i2c_attach_client(new_client))) goto ERROR3; /* Register a new directory entry with module sensors */ if ((i = i2c_register_entry(new_client, type_name, mic74_dir_table_template, THIS_MODULE)) < 0) { err = i; goto ERROR4; } data->sysctl_id = i; /* Initialize the MIC74 chip */ mic74_init_client(new_client); return 0; /* OK, this is not exactly good programming practice, usually. But it is very code-efficient in this case. */ ERROR4: i2c_detach_client(new_client); ERROR3: ERROR1: kfree(new_client); ERROR0: printk("mic74.o: mic74_detect failed\n"); return err; } int mic74_detach_client(struct i2c_client *client) { int err; i2c_deregister_entry(((struct mic74_data *) (client->data))-> sysctl_id); if ((err = i2c_detach_client(client))) { printk ("mic74.o: Client deregistration failed, client not detached.\n"); return err; } kfree(client); return 0; } /* No commands defined yet */ int mic74_command(struct i2c_client *client, unsigned int cmd, void *arg) { return 0; } void mic74_inc_use(struct i2c_client *client) { #ifdef MODULE MOD_INC_USE_COUNT; #endif } void mic74_dec_use(struct i2c_client *client) { #ifdef MODULE MOD_DEC_USE_COUNT; #endif } int mic74_read_value(struct i2c_client *client, u8 reg) { return 0xFF & i2c_smbus_read_byte_data(client, reg); } int mic74_write_value(struct i2c_client *client, u8 reg, u8 value) { return i2c_smbus_write_byte_data(client, reg, value); } /* Called when we have found a new MIC74. It should set limits, etc. */ void mic74_init_client(struct i2c_client *client) { /* don't init, defaults are fine */ } void mic74_update_client(struct i2c_client *client) { struct mic74_data *data = client->data; down(&data->update_lock); if ((jiffies - data->last_updated > HZ) || (jiffies < data->last_updated) || !data->valid) { #ifdef DEBUG printk("Starting mic74 update\n"); #endif data->config = mic74_read_value(client, MIC74_REG_CONFIG); data->dataDir = mic74_read_value(client, MIC74_REG_DATA_DIR); data->outCfg = mic74_read_value(client, MIC74_REG_OUT_CFG); data->status = mic74_read_value(client, MIC74_REG_STATUS); data->intMask = mic74_read_value(client, MIC74_REG_INT_MASK); data->data = mic74_read_value(client, MIC74_REG_DATA); data->fanSpeed = mic74_read_value(client, MIC74_REG_FAN_SPEED); data->last_updated = jiffies; data->valid = 1; } up(&data->update_lock); } /* The next couple functions are the call-back functions of the /proc/sys and sysctl files. Which function is used is defined in the ctl_table in the extra1 field. Each function must return the magnitude (power of 10 to divide the data with) if it is called with operation==SENSORS_PROC_REAL_INFO. It is always 0. It must put a maximum of *nrels elements in results reflecting the data of this file, and set *nrels to the number it actually put in it, if operation== SENSORS_PROC_REAL_READ. This is always 1. Finally, it must get upto *nrels elements from results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. This is also always 1 regardless of how many are sent. Note that on SENSORS_PROC_REAL_READ, I do not check whether results is large enough (by checking the incoming value of *nrels). This is not very good practice, but as long as you put less than about 5 values in results, you can assume it is large enough. */ void mic74_reg_rw(struct i2c_client *client, int operation, int ctl_name, int *nrels_mag, long *results) { struct mic74_data *data = client->data; int nr = ctl_name - MIC74_SYSCTL_REG0; if (operation == SENSORS_PROC_REAL_INFO) *nrels_mag = 0; else if (operation == SENSORS_PROC_REAL_READ) { mic74_update_client(client); switch (nr) { case 0: results[0] = data->config; break; case 1: results[0] = data->dataDir; break; case 2: results[0] = data->outCfg; break; case 3: results[0] = data->status; break; case 4: results[0] = data->intMask; break; case 5: results[0] = data->data; break; case 6: results[0] = data->fanSpeed; break; } *nrels_mag = 1; } else if (operation == SENSORS_PROC_REAL_WRITE) { if (*nrels_mag >= 1) mic74_write_value(client, (u8)nr, (u8)(results[0] & 0xFF)); } } /* This is identical to the above except that it has now WRITE code and it calls the _rw routine above for a READ */ void mic74_reg_ro(struct i2c_client *client, int operation, int ctl_name, int *nrels_mag, long *results) { if (operation == SENSORS_PROC_REAL_INFO) *nrels_mag = 0; else if (operation == SENSORS_PROC_REAL_READ) { mic74_reg_rw(client, operation, ctl_name, nrels_mag, results); *nrels_mag = 1; } } int __init sensors_mic74_init(void) { int res; printk("mic74.o version %s (%s)\n", LM_VERSION, LM_DATE); mic74_initialized = 0; if ((res = i2c_add_driver(&mic74_driver))) { printk ("mic74.o: Driver registration failed, module not inserted.\n"); mic74_cleanup(); return res; } mic74_initialized++; return 0; } int __init mic74_cleanup(void) { int res; if (mic74_initialized >= 1) { if ((res = i2c_del_driver(&mic74_driver))) { printk ("mic74.o: Driver deregistration failed, module not removed.\n"); return res; } mic74_initialized--; } return 0; } EXPORT_NO_SYMBOLS; #ifdef MODULE MODULE_AUTHOR("no one willing to take credit"); MODULE_DESCRIPTION("MIC74 driver"); int init_module(void) { return sensors_mic74_init(); } int cleanup_module(void) { return mic74_cleanup(); } #endif /* MODULE */