| bast-pmu-v1.patch | | Files affected: | drivers/i2c/chips/Kconfig | 8 8 + 0 - 0 ! | drivers/i2c/chips/Makefile | 1 1 + 0 - 0 ! | drivers/i2c/chips/bast-pmu.c | 221 221 + 0 - 0 ! | 3 files changed, 230 insertions(+) | | Ben Dooks, Mon, 25 Oct 2004 12:41:05 +0100 diff -urN -X ../dontdiff linux-2.6.9-rc3/drivers/i2c/chips/Kconfig linux-2.6.9-rc3-work2/drivers/i2c/chips/Kconfig --- linux-2.6.9-rc3/drivers/i2c/chips/Kconfig 2004-10-04 22:57:07.000000000 +0100 +++ linux-2.6.9-rc3-work2/drivers/i2c/chips/Kconfig 2004-10-08 19:56:11.000000000 +0100 @@ -53,6 +53,14 @@ This driver can also be built as a module. If so, the module will be called asb100. +config SENSORS_BASTPMU + tristate "Simtec BAST PMU Controller" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for the Simtec Electronics + BAST Power Management Unit, which allows control over wake-on + events, and machine-wide reset + config SENSORS_DS1621 tristate "Dallas Semiconductor DS1621 and DS1625" depends on I2C && EXPERIMENTAL diff -urN -X ../dontdiff linux-2.6.9-rc3/drivers/i2c/chips/Makefile linux-2.6.9-rc3-work2/drivers/i2c/chips/Makefile --- linux-2.6.9-rc3/drivers/i2c/chips/Makefile 2004-10-04 22:57:07.000000000 +0100 +++ linux-2.6.9-rc3-work2/drivers/i2c/chips/Makefile 2004-10-08 19:50:40.000000000 +0100 @@ -10,6 +10,7 @@ obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o +obj-$(CONFIG_SENSORS_BASTPMU) += bast-pmu.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o obj-$(CONFIG_SENSORS_FSCHER) += fscher.o diff -urN -X ../dontdiff linux-2.6.9-rc3/drivers/i2c/chips/bast-pmu.c linux-2.6.9-rc3-work2/drivers/i2c/chips/bast-pmu.c --- linux-2.6.9-rc3/drivers/i2c/chips/bast-pmu.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9-rc3-work2/drivers/i2c/chips/bast-pmu.c 2004-10-08 21:19:04.000000000 +0100 @@ -0,0 +1,221 @@ +/* linux/drivers/i2c/chips/bast-pmu.c + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks + * + * 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 + * + * Changelog + * 08-Oct-2004 BJD Initial version +*/ + +#include +#include +#include +#include +#include +#include + +#include + +#define PFX "bast-pmu: " + +#define DBG(x...) printk(KERN_INFO x) + +struct bast_pmu_client { + struct i2c_client i2c_client; + unsigned char version; + unsigned char uid[6]; +}; + + +static struct i2c_driver bast_pmu_driver; + +static unsigned short ignore[] = { I2C_CLIENT_END }; +static unsigned short normal_addr[] = { 0xD6 >> 1, I2C_CLIENT_END }; + +static struct i2c_client_address_data addr_data = { + .normal_i2c = normal_addr, + .normal_i2c_range = ignore, + .probe = ignore, + .probe_range = ignore, + .ignore = ignore, + .ignore_range = ignore, + .force = ignore, +}; + +static int bast_pmu_read(struct bast_pmu_client *cli, int reg, void *buff, int len) +{ + struct i2c_msg msgs[2] = { + { cli->i2c_client.addr, 0, 1, (void *)® }, + { cli->i2c_client.addr, I2C_M_RD, len, buff }, + }; + int ret; + + ret = i2c_transfer(cli->i2c_client.adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + return 0; +} + +static struct bast_pmu_client *to_bast_client(struct device *dev) +{ + return container_of(dev, struct bast_pmu_client, i2c_client.dev); +} + +static int bast_pmu_show_version(struct device *dev, char *buf) +{ + struct bast_pmu_client *client = to_bast_client(dev); + int vers = client->version+100; + + return snprintf(buf, PAGE_SIZE, "%d.%02d\n", vers / 100, vers % 100); +} + +static int bast_pmu_show_uid(struct device *dev, char *buf) +{ + struct bast_pmu_client *client = to_bast_client(dev); + unsigned char *uid = client->uid; + + return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n", + uid[0], uid[1], uid[2], uid[3], uid[4], uid[5]); +} + +static int bast_pmu_store_reset(struct device *dev, const char *buf, size_t len) +{ + if (strnicmp(buf, "reset", 5) != 0) + return -EINVAL; + + printk(KERN_INFO PFX "reset guard seen, resetting system...\n"); + + // should never get here(tm) // + return -EINVAL; +} + +static DEVICE_ATTR(version, 0444, bast_pmu_show_version, NULL); +static DEVICE_ATTR(uid, 0444, bast_pmu_show_uid, NULL); +static DEVICE_ATTR(reset, 0200, NULL, bast_pmu_store_reset); + +static int bast_pmu_attach(struct i2c_adapter *adap, int addr, int kind) +{ + struct bast_pmu_client *client; + char buf[8]; + int ret = 0; + + client = kmalloc(sizeof(struct bast_pmu_client), GFP_KERNEL); + if (client == NULL) { + printk(KERN_ERR PFX "no memory for client\n"); + return -ENOMEM; + } + + memset(client, 0, sizeof(*client)); + + strlcpy(client->i2c_client.name, "bast-pmu", I2C_NAME_SIZE); + i2c_set_clientdata(&client->i2c_client, client); + + client->i2c_client.id = bast_pmu_driver.id; + client->i2c_client.addr = addr; + client->i2c_client.flags = I2C_CLIENT_ALLOW_USE | I2C_DF_NOTIFY; + client->i2c_client.adapter = adap; + client->i2c_client.driver = &bast_pmu_driver; + + if (bast_pmu_read(client, BASTPMU_REG_IDENT, buf, 4) < 0) { + DBG(PFX "failed to read ident register\n"); + } + + DBG(PFX "ident read %02x,%02x,%02x,%02x\n", + buf[0], buf[1], buf[2], buf[3]); + + if (buf[0] != BASTPMU_IDENT_0 || + buf[1] != BASTPMU_IDENT_1 || + buf[2] != BASTPMU_IDENT_2 || + buf[3] != BASTPMU_IDENT_3) { + printk(KERN_ERR PFX "did not find at addr 0x%02x\n", addr); + ret = -ENOENT; + goto out_err; + } + + /* ok, we got ourselves a genuine bast power management unit! */ + + if (bast_pmu_read(client, BASTPMU_REG_VERSION, buf, 1) < 0 || + bast_pmu_read(client, BASTPMU_REG_UID, buf+1, 6) < 0) { + printk(KERN_ERR PFX "failed to read ident info\n"); + ret = -EIO; + goto out_err; + } + + printk(KERN_INFO PFX "version %d, uid %02x:%02x:%02x:%02x:%02x:%02x\n", + buf[0]+100, buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); + + client->version = buf[0]; + memcpy(client->uid, buf+1, 6); + + /* attach new client */ + + ret = i2c_attach_client(&client->i2c_client); + if (ret < 0) + goto out_err; + + /* attach some user controllable things */ + + device_create_file(&client->i2c_client.dev, &dev_attr_version); + device_create_file(&client->i2c_client.dev, &dev_attr_uid); + device_create_file(&client->i2c_client.dev, &dev_attr_reset); + + return 0; + + out_err: + // cleanup + + return ret; +} + +static int bast_pmu_probe(struct i2c_adapter *adap) +{ + return i2c_probe(adap, &addr_data, bast_pmu_attach); +} + +static int bast_pmu_detach(struct i2c_client *client) +{ + i2c_detach_client(client); + kfree(i2c_get_clientdata(client)); + return 0; +} + +static struct i2c_driver bast_pmu_driver = { + .owner = THIS_MODULE, + .name = "bast-pmu", + .id = I2C_DRIVERID_EXP1, + .flags = I2C_DF_NOTIFY, + .attach_adapter = bast_pmu_probe, + .detach_client = bast_pmu_detach, +}; + +static __init int bast_pmu_init(void) +{ + return i2c_add_driver(&bast_pmu_driver); +} + +static __exit void bast_pmu_exit(void) +{ + i2c_del_driver(&bast_pmu_driver); +} + +module_init(bast_pmu_init); +module_exit(bast_pmu_exit); + +MODULE_DESCRIPTION("Simtec BAST (EB2410ITX) PMU Interface"); +MODULE_AUTHOR("Ben Dooks, Simtec Electronics, "); +MODULE_LICENSE("GPL");