tps65010.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 513 行
C
513 行
/* * Copyright (C) 2004 Texas Instruments * * 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. */#include <linux/config.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/device.h>#include <linux/i2c.h>#include <linux/delay.h>#include <asm/irq.h>#include <asm/mach-types.h>#include <asm/arch/gpio.h>#include <asm/arch/mux.h>/*-------------------------------------------------------------------------*/#define DRIVER_VERSION __DATE__ __TIME__#define DRIVER_NAME (tps65010_driver.name)MODULE_DESCRIPTION("TPS65010 Power Managment Driver");MODULE_LICENSE("GPL");/* only two addresses possible */#define TPS_BASE 0x48static unsigned short normal_i2c[] = { TPS_BASE, I2C_CLIENT_END };static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };I2C_CLIENT_INSMOD;static struct i2c_driver tps65010_driver;/*-------------------------------------------------------------------------*//* registers, all 8 bits */#define TPS_CHGSTATUS 0x01#define TPS_REGSTATUS 0x02#define TPS_MASK1 0x03#define TPS_MASK2 0x04#define TPS_ACKINT1 0x05#define TPS_ACKINT2 0x06#define TPS_CHGCONFIG 0x07# define TPS_CHARGE_ENABLE (1 << 0)# define TPS_VBUS_CHARGING (1 << 1)# define TPS_VBUS_500MA (1 << 2)#define TPS_LED1_ON 0x08#define TPS_LED1_PER 0x09#define TPS_LED2_ON 0x0a#define TPS_LED2_PER 0x0b#define TPS_VDCDC1 0x0c# define TPS_ENABLE_LP (1 << 3)#define TPS_VDCDC2 0x0d#define TPS_REGS1 0x0e#define TPS_MASK3 0x0f#define TPS_DEFGPIO 0x10#define LED1 1#define LED2 2#define OFF 0#define ON 1#define BLINK 2#define GPIO1 1#define GPIO2 2#define GPIO3 3#define GPIO4 4#define LOW 0#define HIGH 1/* operational registers *//*-------------------------------------------------------------------------*/struct tps65010 { struct i2c_client client; struct semaphore lock; int irq; /* plus four GPIOs, probably used to switch power */};/*-------------------------------------------------------------------------*/static irqreturn_t tps65010_irq(int irq, void *_tps, struct pt_regs *regs){ struct tps65010 *tps = _tps; dev_dbg(&tps->client.dev, "irq!\n"); /* FIXME do this right */ return IRQ_HANDLED;}/*-------------------------------------------------------------------------*/static struct tps65010 *the_tps;static int tps65010_detach_client(struct i2c_client *client){ struct tps65010 *tps; tps = container_of(client, struct tps65010, client);#ifdef CONFIG_ARCH_OMAP if (machine_is_omap_h2()) omap_free_gpio(58);#endif free_irq(tps->irq, tps); if (i2c_detach_client(client) == 0) kfree(tps); the_tps = 0; return 0;}/* no error returns, they'd just make bus scanning stop */static int tps65010_probe(struct i2c_adapter *bus, int address, int kind){ static int tps65010_id; struct tps65010 *tps; int status; if (the_tps) { dev_dbg(&bus->dev, "only one %s for now\n", DRIVER_NAME); return 0; } tps = kmalloc(sizeof *tps, GFP_KERNEL); if (!tps) return 0; memset(tps, 0, sizeof *tps); init_MUTEX(&tps->lock); tps->irq = -1; tps->client.addr = address; i2c_set_clientdata(&tps->client, tps); tps->client.adapter = bus; tps->client.id = tps65010_id++; tps->client.driver = &tps65010_driver; strlcpy(tps->client.name, DRIVER_NAME, I2C_NAME_SIZE); status = i2c_attach_client(&tps->client); if (status < 0) { dev_dbg(&bus->dev, "can't attach %s to device %d, err %d\n", DRIVER_NAME, address, status);fail1: kfree(tps); return 0; }#ifdef CONFIG_ARCH_OMAP if (machine_is_omap_h2() || machine_is_omap_osk()) {// FIXME this W4 MUX is not yet implemented // omap_cfg_reg(W4_GPIO58); tps->irq = OMAP_GPIO_IRQ(58); omap_request_gpio(58); omap_set_gpio_direction(58, 1); omap_set_gpio_edge_ctrl(58, OMAP_GPIO_FALLING_EDGE); status = request_irq(tps->irq, tps65010_irq, SA_SAMPLE_RANDOM, DRIVER_NAME, tps); if (status < 0) { dev_dbg(&tps->client.dev, "can't get IRQ %d, err %d\n", tps->irq, status); i2c_detach_client(&tps->client); goto fail1; } /* FIXME what IRQs should we enable? */ dev_dbg(&tps->client.dev, "chgstatus 0x%02x\n", i2c_smbus_read_byte_data(&tps->client, TPS_CHGSTATUS)); dev_dbg(&tps->client.dev, "regstatus 0x%02x\n", i2c_smbus_read_byte_data(&tps->client, TPS_REGSTATUS)); dev_dbg(&tps->client.dev, "mask1 0x%02x\n", i2c_smbus_read_byte_data(&tps->client, TPS_MASK1)); dev_dbg(&tps->client.dev, "mask2 0x%02x\n", i2c_smbus_read_byte_data(&tps->client, TPS_MASK2)); dev_dbg(&tps->client.dev, "mask3 0x%02x\n", i2c_smbus_read_byte_data(&tps->client, TPS_MASK3)); dev_dbg(&tps->client.dev, "ackint1 0x%02x\n", i2c_smbus_read_byte_data(&tps->client, TPS_ACKINT1)); dev_dbg(&tps->client.dev, "ackint2 0x%02x\n", i2c_smbus_read_byte_data(&tps->client, TPS_ACKINT2)); dev_dbg(&tps->client.dev, "chgconf 0x%02x\n", i2c_smbus_read_byte_data(&tps->client, TPS_CHGCONFIG)); }#endif // FIXME publish this handle the_tps = tps; return 0;}static int tps65010_scan_bus(struct i2c_adapter *bus){ if (!i2c_check_functionality(bus, I2C_FUNC_SMBUS_BYTE_DATA)) return -EINVAL; return i2c_probe(bus, &addr_data, tps65010_probe);}static struct i2c_driver tps65010_driver = { .owner = THIS_MODULE, .name = "tps65010", .id = 888, /* FIXME assign "official" value */ .flags = I2C_DF_NOTIFY, .attach_adapter = tps65010_scan_bus, .detach_client = tps65010_detach_client,};/*-------------------------------------------------------------------------*//* Draw from VBUS: * 0 mA -- DON'T DRAW (might supply power instead) * 100 mA -- usb unit load (slowest charge rate) * 500 mA -- usb high power (fast battery charge) */int tps65010_set_vbus_draw(unsigned mA){ int status; unsigned chgconfig, tmp; if (mA > 500) return -EINVAL; down(&the_tps->lock); dev_dbg (&the_tps->client.dev, "chgconfig 0x%02x\n", i2c_smbus_read_byte_data(&the_tps->client, TPS_CHGCONFIG)); /* don't draw current until configured for at least 100 mA */ chgconfig = i2c_smbus_read_byte_data(&the_tps->client, TPS_CHGCONFIG); chgconfig &= ~(TPS_VBUS_500MA | TPS_VBUS_CHARGING); if (mA == 500) chgconfig |= TPS_VBUS_500MA | TPS_VBUS_CHARGING; else if (mA >= 100) chgconfig |= TPS_VBUS_CHARGING; status = i2c_smbus_write_byte_data(&the_tps->client, TPS_CHGCONFIG, chgconfig); tmp = i2c_smbus_read_byte_data(&the_tps->client, TPS_CHGCONFIG); dev_dbg (&the_tps->client.dev, "%d mA from vbus %d: 0x%02x%s\n", mA, status, tmp, (tmp != status) ? " (BAD)" : ""); up(&the_tps->lock); return status;}EXPORT_SYMBOL(tps65010_set_vbus_draw);/*-------------------------------------------------------------------------*//* tps65010_set_gpio_out_value parameter: * gpio: GPIO1, GPIO2, GPIO3 or GPIO4 * value: LOW or HIGH */int tps65010_set_gpio_out_value(unsigned gpio, unsigned value){ int status; unsigned defgpio; if ((gpio < GPIO1) || (gpio > GPIO4)) return -EINVAL; down(&the_tps->lock); dev_dbg (&the_tps->client.dev, "defconfig 0x%02x\n", i2c_smbus_read_byte_data(&the_tps->client, TPS_DEFGPIO)); defgpio = i2c_smbus_read_byte_data(&the_tps->client, TPS_DEFGPIO); /* Configure GPIO for output */ defgpio |= 1 << (gpio + 3); /* Writing 1 forces a logic 0 on that GPIO and vice versa */ switch (value) { case LOW: defgpio |= 1 << (gpio - 1); /* set GPIO low by writing 1 */ break; case HIGH: defgpio &= ~(1 << (gpio - 1)); /* set GPIO high by writing 0 */ break; default: printk(KERN_ERR "%s: Wrong value parameter for tps65010_set_gpio_out_value()\n", DRIVER_NAME); up(&the_tps->lock); return -EINVAL; } status = i2c_smbus_write_byte_data(&the_tps->client, TPS_DEFGPIO, defgpio); dev_dbg (&the_tps->client.dev, "defconfig 0x%02x\n", i2c_smbus_read_byte_data(&the_tps->client, TPS_DEFGPIO)); up(&the_tps->lock); return status;}EXPORT_SYMBOL(tps65010_set_gpio_out_value);/*-------------------------------------------------------------------------*//* tps65010_set_led parameter: * led: LED1 or LED2 * mode: ON, OFF or BLINK */int tps65010_set_led(unsigned led, unsigned mode){ int status; unsigned led_on, led_per, offs; if(led == LED1) offs = 0; else { offs = 2; led = LED2; } down(&the_tps->lock); dev_dbg (&the_tps->client.dev, "led%i_on 0x%02x\n", led, i2c_smbus_read_byte_data(&the_tps->client, TPS_LED1_ON + offs)); dev_dbg (&the_tps->client.dev, "led%i_per 0x%02x\n", led, i2c_smbus_read_byte_data(&the_tps->client, TPS_LED1_PER + offs)); switch (mode) { case OFF: led_on = 1 << 7; led_per = 0 << 7; break; case ON: led_on = 1 << 7; led_per = 1 << 7; break; case BLINK: led_on = 0x30 | (0 << 7); led_per = 0x08 | (1 << 7); break; default: printk(KERN_ERR "%s: Wrong mode parameter for tps65010_set_led()\n", DRIVER_NAME); up(&the_tps->lock); return -EINVAL; } status = i2c_smbus_write_byte_data(&the_tps->client, TPS_LED1_ON + offs, led_on); if (status != 0) { printk(KERN_ERR "%s: Failed to write led%i_on register\n", DRIVER_NAME, led); up(&the_tps->lock); return status; } dev_dbg (&the_tps->client.dev, "led%i_on 0x%02x\n", led, i2c_smbus_read_byte_data(&the_tps->client, TPS_LED1_ON + offs)); status = i2c_smbus_write_byte_data(&the_tps->client, TPS_LED1_PER + offs, led_per); if (status != 0) { printk(KERN_ERR "%s: Failed to write led%i_per register\n", DRIVER_NAME, led); up(&the_tps->lock); return status; } dev_dbg (&the_tps->client.dev, "led%i_per 0x%02x\n", led, i2c_smbus_read_byte_data(&the_tps->client, TPS_LED1_PER + offs)); up(&the_tps->lock); return status;}EXPORT_SYMBOL(tps65010_set_led);/*-------------------------------------------------------------------------*//* tps65010_set_low_pwr parameter: * mode: ON or OFF */int tps65010_set_low_pwr(unsigned mode){ int status; unsigned vdcdc1; down(&the_tps->lock); dev_dbg (&the_tps->client.dev, "vdcdc1 0x%02x\n", i2c_smbus_read_byte_data(&the_tps->client, TPS_VDCDC1)); vdcdc1 = i2c_smbus_read_byte_data(&the_tps->client, TPS_VDCDC1); switch (mode) { case OFF: vdcdc1 &= ~TPS_ENABLE_LP; /* disable ENABLE_LP bit */ break; case ON: vdcdc1 |= TPS_ENABLE_LP; /* enable ENABLE_LP bit */ break; default: printk(KERN_ERR "%s: Wrong mode parameter for tps65010_set_low_pwr()\n", DRIVER_NAME); up(&the_tps->lock); return -EINVAL; } status = i2c_smbus_write_byte_data(&the_tps->client, TPS_VDCDC1, vdcdc1); if (status != 0) { printk(KERN_ERR "%s: Failed to write vdcdc1 register\n", DRIVER_NAME); up(&the_tps->lock); return status; } dev_dbg (&the_tps->client.dev, "vdcdc1 0x%02x\n", i2c_smbus_read_byte_data(&the_tps->client, TPS_VDCDC1)); up(&the_tps->lock); return status;}EXPORT_SYMBOL(tps65010_set_low_pwr);/*-------------------------------------------------------------------------*/static int __init tps_init(void){ u32 tries = 3; int status; printk(KERN_INFO "%s: version %s\n", DRIVER_NAME, DRIVER_VERSION); /* some boards have startup glitches */ while (tries--) { status = i2c_add_driver(&tps65010_driver); if (the_tps) break; i2c_del_driver(&tps65010_driver); if (!tries) { printk(KERN_ERR "%s: no chip?\n", DRIVER_NAME); return -ENODEV; } pr_debug("%s: re-probe ...\n", DRIVER_NAME); msleep(10); }#if defined(CONFIG_ARM) if (machine_is_omap_osk()) { // FIXME: This should be placed in the initialization code // of the submodules (USB, ethernet, power management, // board-osk.c). But because I2C is initialized so // late in boot process no I2C access is available where // this code really belongs to. /* Let LED1 (D9) blink */ tps65010_set_led(LED1, BLINK); /* Disable LED 2 (D2) */ tps65010_set_led(LED2, OFF); /* Set GPIO 1 high to enable USB */ tps65010_set_gpio_out_value(GPIO1, HIGH); /* Disable LED on GPIO2 (D3) */ tps65010_set_gpio_out_value(GPIO2, HIGH); /* Set GPIO 3 low to enable ethernet */ tps65010_set_gpio_out_value(GPIO3, LOW); /* Enable LOW_PWR */ tps65010_set_low_pwr(ON); } else if (machine_is_omap_h2()) { /* Enable LOW_PWR */ tps65010_set_low_pwr(ON); }#endif return status;}subsys_initcall(tps_init);static void __exit tps_exit(void){ i2c_del_driver(&tps65010_driver);}module_exit(tps_exit);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?