diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index 583a7e3751ac..3d75e1e2d4a1 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -123,11 +123,34 @@ int tegra_powergate_remove_clamping(int id) return 0; } +/* Must be called with clk disabled, and returns with clk enabled */ +static int tegra_powergate_reset_module(struct clk *clk) +{ + int ret; + + tegra_periph_reset_assert(clk); + + udelay(10); + + ret = clk_enable(clk); + if (ret) + return ret; + + udelay(10); + + tegra_periph_reset_deassert(clk); + + return 0; +} + /* Must be called with clk disabled, and returns with clk enabled */ int tegra_powergate_sequence_power_up(int id, struct clk *clk) { int ret; + if (tegra_powergate_is_powered(id)) + return tegra_powergate_reset_module(clk); + tegra_periph_reset_assert(clk); ret = tegra_powergate_power_on(id); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 0c273c04a306..c0377ca064ae 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -340,6 +340,14 @@ config SENSORS_AK8975 If you say yes here you get support for Asahi Kasei's orientation sensor AK8975. +config SENSORS_NCT1008 + tristate "ON Semiconductor Temperature Sensor" + default n + depends on I2C + help + Say yes here if you wish to include the ON Semiconductor + NCT1008 Temperature sensor. + config EP93XX_PWM tristate "EP93xx PWM support" depends on ARCH_EP93XX diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 249ac2683aa5..8edb90117929 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -41,3 +41,4 @@ obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o obj-$(CONFIG_WL127X_RFKILL) += wl127x-rfkill.o obj-$(CONFIG_APANIC) += apanic.o obj-$(CONFIG_SENSORS_AK8975) += akm8975.o +obj-$(CONFIG_SENSORS_NCT1008) += nct1008.o diff --git a/drivers/misc/nct1008.c b/drivers/misc/nct1008.c new file mode 100755 index 000000000000..4ae48e4003aa --- /dev/null +++ b/drivers/misc/nct1008.c @@ -0,0 +1,291 @@ +/* + * drivers/misc/nct1008.c + * + * Driver for NCT1008, temperature monitoring device from ON Semiconductors + * + * Copyright (c) 2010, NVIDIA 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRIVER_NAME "nct1008" + +/* Register Addresses */ +#define LOCAL_TEMP_RD 0x00 +#define STATUS_RD 0x02 +#define CONFIG_RD 0x03 + +#define CONFIG_WR 0x09 +#define CONV_RATE_WR 0x0A +#define LOCAL_TEMP_HI_LIMIT_WR 0x0B +#define EXT_TEMP_HI_LIMIT_HI_BYTE 0x0D +#define OFFSET_WR 0x11 +#define EXT_THERM_LIMIT_WR 0x19 +#define LOCAL_THERM_LIMIT_WR 0x20 +#define THERM_HYSTERESIS_WR 0x21 + +/* Configuration Register Bits */ +#define EXTENDED_RANGE_BIT (0x1 << 2) +#define THERM2_BIT (0x1 << 5) +#define STANDBY_BIT (0x1 << 6) + +/* Max Temperature Measurements */ +#define EXTENDED_RANGE_OFFSET 64U +#define STANDARD_RANGE_MAX 127U +#define EXTENDED_RANGE_MAX (150U + EXTENDED_RANGE_OFFSET) + +struct nct1008_data { + struct work_struct work; + struct i2c_client *client; + struct mutex mutex; + u8 config; + void (*alarm_fn)(bool raised); +}; + +static void nct1008_enable(struct i2c_client *client) +{ + struct nct1008_data *data = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, CONFIG_WR, + data->config & ~STANDBY_BIT); +} + +static void nct1008_disable(struct i2c_client *client) +{ + struct nct1008_data *data = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, CONFIG_WR, + data->config | STANDBY_BIT); +} + + +static void nct1008_work_func(struct work_struct *work) +{ + struct nct1008_data *data = container_of(work, struct nct1008_data, work); + int irq = data->client->irq; + + mutex_lock(&data->mutex); + + if (data->alarm_fn) { + /* Therm2 line is active low */ + data->alarm_fn(!gpio_get_value(irq_to_gpio(irq))); + } + + mutex_unlock(&data->mutex); +} + +static irqreturn_t nct1008_irq(int irq, void *dev_id) +{ + struct nct1008_data *data = dev_id; + schedule_work(&data->work); + + return IRQ_HANDLED; +} + +static inline u8 value_to_temperature(bool extended, u8 value) +{ + return (extended ? (u8)(value - EXTENDED_RANGE_OFFSET) : value); +} + +static inline u8 temperature_to_value(bool extended, u8 temp) +{ + return (extended ? (u8)(temp + EXTENDED_RANGE_OFFSET) : temp); +} + +static int __devinit nct1008_configure_sensor(struct nct1008_data* data) +{ + struct i2c_client *client = data->client; + struct nct1008_platform_data *pdata = client->dev.platform_data; + u8 value; + int err; + + if (!pdata || !pdata->supported_hwrev) + return -ENODEV; + + /* + * Initial Configuration - device is placed in standby and + * ALERT/THERM2 pin is configured as THERM2 + */ + data->config = value = pdata->ext_range ? + (STANDBY_BIT | THERM2_BIT | EXTENDED_RANGE_BIT) : + (STANDBY_BIT | THERM2_BIT); + + err = i2c_smbus_write_byte_data(client, CONFIG_WR, value); + if (err < 0) + goto error; + + /* Temperature conversion rate */ + err = i2c_smbus_write_byte_data(client, CONV_RATE_WR, pdata->conv_rate); + if (err < 0) + goto error; + + /* External temperature h/w shutdown limit */ + value = temperature_to_value(pdata->ext_range, pdata->shutdown_ext_limit); + err = i2c_smbus_write_byte_data(client, EXT_THERM_LIMIT_WR, value); + if (err < 0) + goto error; + + /* Local temperature h/w shutdown limit */ + value = temperature_to_value(pdata->ext_range, pdata->shutdown_local_limit); + err = i2c_smbus_write_byte_data(client, LOCAL_THERM_LIMIT_WR, value); + if (err < 0) + goto error; + + /* External Temperature Throttling limit */ + value = temperature_to_value(pdata->ext_range, pdata->throttling_ext_limit); + err = i2c_smbus_write_byte_data(client, EXT_TEMP_HI_LIMIT_HI_BYTE, value); + if (err < 0) + goto error; + + /* Local Temperature Throttling limit */ + value = pdata->ext_range ? EXTENDED_RANGE_MAX : STANDARD_RANGE_MAX; + err = i2c_smbus_write_byte_data(client, LOCAL_TEMP_HI_LIMIT_WR, value); + if (err < 0) + goto error; + + /* Remote channel offset */ + err = i2c_smbus_write_byte_data(client, OFFSET_WR, pdata->offset); + if (err < 0) + goto error; + + /* THERM hysteresis */ + err = i2c_smbus_write_byte_data(client, THERM_HYSTERESIS_WR, pdata->hysteresis); + if (err < 0) + goto error; + + data->alarm_fn = pdata->alarm_fn; + return 0; +error: + return err; +} + +static int __devinit nct1008_configure_irq(struct nct1008_data *data) +{ + INIT_WORK(&data->work, nct1008_work_func); + + return request_irq(data->client->irq, nct1008_irq, IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, DRIVER_NAME, data); +} + +static int __devinit nct1008_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct nct1008_data *data; + int err; + + data = kzalloc(sizeof(struct nct1008_data), GFP_KERNEL); + + if (!data) + return -ENOMEM; + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->mutex); + + err = nct1008_configure_sensor(data); /* sensor is in standby */ + if (err < 0) + goto error; + + err = nct1008_configure_irq(data); + if (err < 0) + goto error; + + nct1008_enable(client); /* sensor is running */ + + schedule_work(&data->work); /* check initial state */ + + return 0; + +error: + kfree(data); + return err; +} + +static int __devexit nct1008_remove(struct i2c_client *client) +{ + struct nct1008_data *data = i2c_get_clientdata(client); + + free_irq(data->client->irq, data); + cancel_work_sync(&data->work); + kfree(data); + + return 0; +} + +#ifdef CONFIG_PM +static int nct1008_suspend(struct i2c_client *client, pm_message_t state) +{ + disable_irq(client->irq); + nct1008_disable(client); + + return 0; +} + +static int nct1008_resume(struct i2c_client *client) +{ + struct nct1008_data *data = i2c_get_clientdata(client); + + nct1008_enable(client); + enable_irq(client->irq); + schedule_work(&data->work); + + return 0; +} +#endif + +static const struct i2c_device_id nct1008_id[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nct1008_id); + +static struct i2c_driver nct1008_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = nct1008_probe, + .remove = __devexit_p(nct1008_remove), + .id_table = nct1008_id, +#ifdef CONFIG_PM + .suspend = nct1008_suspend, + .resume = nct1008_resume, +#endif +}; + +static int __init nct1008_init(void) +{ + return i2c_add_driver(&nct1008_driver); +} + +static void __exit nct1008_exit(void) +{ + i2c_del_driver(&nct1008_driver); +} + +MODULE_DESCRIPTION("Temperature sensor driver for OnSemi NCT1008"); +MODULE_LICENSE("GPL"); + +module_init (nct1008_init); +module_exit (nct1008_exit); diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index 2819a93d8442..16789c21c649 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -119,6 +119,7 @@ static void _dump_regs(struct tegra_dc *dc, void *data, char buff[256]; tegra_dc_io_start(dc); + clk_enable(dc->clk); DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0); DUMP_REG(DC_CMD_DISPLAY_COMMAND); @@ -256,6 +257,7 @@ static void _dump_regs(struct tegra_dc *dc, void *data, DUMP_REG(DC_WIN_CSC_KVB); } + clk_disable(dc->clk); tegra_dc_io_end(dc); } diff --git a/include/linux/nct1008.h b/include/linux/nct1008.h new file mode 100644 index 000000000000..d65693507299 --- /dev/null +++ b/include/linux/nct1008.h @@ -0,0 +1,40 @@ +/* + * include/linux/nct1008.h + * + * NCT1008, temperature monitoring device from ON Semiconductors + * + * Copyright (c) 2010, NVIDIA 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _LINUX_NCT1008_H +#define _LINUX_NCT1008_H + +#include + +struct nct1008_platform_data { + bool supported_hwrev; + bool ext_range; + u8 conv_rate; + u8 offset; + u8 hysteresis; + u8 shutdown_ext_limit; + u8 shutdown_local_limit; + u8 throttling_ext_limit; + void (*alarm_fn)(bool raised); +}; + +#endif /* _LINUX_NCT1008_H */