mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 20:51:44 +00:00
Merge branch 'for-next' of git://git.o-hand.com/linux-mfd
* 'for-next' of git://git.o-hand.com/linux-mfd: (30 commits) mfd: Fix section mismatch in da903x mfd: move drivers/i2c/chips/menelaus.c to drivers/mfd mfd: move drivers/i2c/chips/tps65010.c to drivers/mfd mfd: dm355evm msp430 driver mfd: Add missing break from wm3850-core mfd: Add WM8351 support mfd: Support configurable numbers of DCDCs and ISINKs on WM8350 mfd: Handle missing WM8350 platform data mfd: Add WM8352 support mfd: Use irq_to_desc in twl4030 code power_supply: Add Dialog DA9030 battery charger driver mfd: Dialog DA9030 battery charger MFD driver mfd: Register WM8400 codec device mfd: Pass driver_data onto child devices mfd: Fix twl4030-core.c build error mfd: twl4030 regulator bug fixes mfd: twl4030: create some regulator devices mfd: twl4030: cleanup symbols and OMAP dependency mfd: twl4030: simplified child creation code power_supply: Add battery health reporting for WM8350 ...
This commit is contained in:
commit
8e128ce331
@ -126,19 +126,6 @@ config ISP1301_OMAP
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called isp1301_omap.
|
||||
|
||||
config TPS65010
|
||||
tristate "TPS6501x Power Management chips"
|
||||
depends on GPIOLIB
|
||||
default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK
|
||||
help
|
||||
If you say yes here you get support for the TPS6501x series of
|
||||
Power Management chips. These include voltage regulators,
|
||||
lithium ion/polymer battery charging, and other features that
|
||||
are often used in portable devices like cell phones and cameras.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tps65010.
|
||||
|
||||
config SENSORS_MAX6875
|
||||
tristate "Maxim MAX6875 Power supply supervisor"
|
||||
depends on EXPERIMENTAL
|
||||
@ -164,16 +151,6 @@ config SENSORS_TSL2550
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tsl2550.
|
||||
|
||||
config MENELAUS
|
||||
bool "TWL92330/Menelaus PM chip"
|
||||
depends on I2C=y && ARCH_OMAP24XX
|
||||
help
|
||||
If you say yes here you get support for the Texas Instruments
|
||||
TWL92330/Menelaus Power Management chip. This include voltage
|
||||
regulators, Dual slot memory card tranceivers, real-time clock
|
||||
and other features that are often used in portable devices like
|
||||
cell phones and PDAs.
|
||||
|
||||
config MCU_MPC8349EMITX
|
||||
tristate "MPC8349E-mITX MCU driver"
|
||||
depends on I2C && PPC_83xx
|
||||
|
@ -19,8 +19,6 @@ obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
|
||||
obj-$(CONFIG_PCF8575) += pcf8575.o
|
||||
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
|
||||
obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
|
||||
obj-$(CONFIG_TPS65010) += tps65010.o
|
||||
obj-$(CONFIG_MENELAUS) += menelaus.o
|
||||
obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
|
||||
obj-$(CONFIG_MCU_MPC8349EMITX) += mcu_mpc8349emitx.o
|
||||
|
||||
|
@ -34,6 +34,14 @@ config MFD_ASIC3
|
||||
This driver supports the ASIC3 multifunction chip found on many
|
||||
PDAs (mainly iPAQ and HTC based ones)
|
||||
|
||||
config MFD_DM355EVM_MSP
|
||||
bool "DaVinci DM355 EVM microcontroller"
|
||||
depends on I2C && MACH_DAVINCI_DM355_EVM
|
||||
help
|
||||
This driver supports the MSP430 microcontroller used on these
|
||||
boards. MSP430 firmware manages resets and power sequencing,
|
||||
inputs from buttons and the IR remote, LEDs, an RTC, and more.
|
||||
|
||||
config HTC_EGPIO
|
||||
bool "HTC EGPIO support"
|
||||
depends on GENERIC_HARDIRQS && GPIOLIB && ARM
|
||||
@ -61,9 +69,32 @@ config UCB1400_CORE
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ucb1400_core.
|
||||
|
||||
config TPS65010
|
||||
tristate "TPS6501x Power Management chips"
|
||||
depends on I2C && GPIOLIB
|
||||
default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK
|
||||
help
|
||||
If you say yes here you get support for the TPS6501x series of
|
||||
Power Management chips. These include voltage regulators,
|
||||
lithium ion/polymer battery charging, and other features that
|
||||
are often used in portable devices like cell phones and cameras.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tps65010.
|
||||
|
||||
config MENELAUS
|
||||
bool "Texas Instruments TWL92330/Menelaus PM chip"
|
||||
depends on I2C=y && ARCH_OMAP24XX
|
||||
help
|
||||
If you say yes here you get support for the Texas Instruments
|
||||
TWL92330/Menelaus Power Management chip. This include voltage
|
||||
regulators, Dual slot memory card tranceivers, real-time clock
|
||||
and other features that are often used in portable devices like
|
||||
cell phones and PDAs.
|
||||
|
||||
config TWL4030_CORE
|
||||
bool "Texas Instruments TWL4030/TPS659x0 Support"
|
||||
depends on I2C=y && GENERIC_HARDIRQS && (ARCH_OMAP2 || ARCH_OMAP3)
|
||||
depends on I2C=y && GENERIC_HARDIRQS
|
||||
help
|
||||
Say yes here if you have TWL4030 family chip on your board.
|
||||
This core driver provides register access and IRQ handling
|
||||
@ -116,6 +147,7 @@ config PMIC_DA903X
|
||||
|
||||
config MFD_WM8400
|
||||
tristate "Support Wolfson Microelectronics WM8400"
|
||||
select MFD_CORE
|
||||
depends on I2C
|
||||
help
|
||||
Support for the Wolfson Microelecronics WM8400 PMIC and audio
|
||||
@ -142,6 +174,38 @@ config MFD_WM8350_CONFIG_MODE_3
|
||||
bool
|
||||
depends on MFD_WM8350
|
||||
|
||||
config MFD_WM8351_CONFIG_MODE_0
|
||||
bool
|
||||
depends on MFD_WM8350
|
||||
|
||||
config MFD_WM8351_CONFIG_MODE_1
|
||||
bool
|
||||
depends on MFD_WM8350
|
||||
|
||||
config MFD_WM8351_CONFIG_MODE_2
|
||||
bool
|
||||
depends on MFD_WM8350
|
||||
|
||||
config MFD_WM8351_CONFIG_MODE_3
|
||||
bool
|
||||
depends on MFD_WM8350
|
||||
|
||||
config MFD_WM8352_CONFIG_MODE_0
|
||||
bool
|
||||
depends on MFD_WM8350
|
||||
|
||||
config MFD_WM8352_CONFIG_MODE_1
|
||||
bool
|
||||
depends on MFD_WM8350
|
||||
|
||||
config MFD_WM8352_CONFIG_MODE_2
|
||||
bool
|
||||
depends on MFD_WM8350
|
||||
|
||||
config MFD_WM8352_CONFIG_MODE_3
|
||||
bool
|
||||
depends on MFD_WM8350
|
||||
|
||||
config MFD_WM8350_I2C
|
||||
tristate "Support Wolfson Microelectronics WM8350 with I2C"
|
||||
select MFD_WM8350
|
||||
|
@ -8,6 +8,8 @@ obj-$(CONFIG_MFD_ASIC3) += asic3.o
|
||||
obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
|
||||
obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
|
||||
|
||||
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
|
||||
|
||||
obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o
|
||||
obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o
|
||||
obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o
|
||||
@ -17,6 +19,9 @@ wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o
|
||||
obj-$(CONFIG_MFD_WM8350) += wm8350.o
|
||||
obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
|
||||
|
||||
obj-$(CONFIG_TPS65010) += tps65010.o
|
||||
obj-$(CONFIG_MENELAUS) += menelaus.o
|
||||
|
||||
obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o
|
||||
|
||||
obj-$(CONFIG_MFD_CORE) += mfd-core.o
|
||||
@ -31,4 +36,4 @@ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o
|
||||
endif
|
||||
obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o
|
||||
|
||||
obj-$(CONFIG_PMIC_DA903X) += da903x.o
|
||||
obj-$(CONFIG_PMIC_DA903X) += da903x.o
|
||||
|
@ -151,12 +151,24 @@ int da903x_write(struct device *dev, int reg, uint8_t val)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(da903x_write);
|
||||
|
||||
int da903x_writes(struct device *dev, int reg, int len, uint8_t *val)
|
||||
{
|
||||
return __da903x_writes(to_i2c_client(dev), reg, len, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(da903x_writes);
|
||||
|
||||
int da903x_read(struct device *dev, int reg, uint8_t *val)
|
||||
{
|
||||
return __da903x_read(to_i2c_client(dev), reg, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(da903x_read);
|
||||
|
||||
int da903x_reads(struct device *dev, int reg, int len, uint8_t *val)
|
||||
{
|
||||
return __da903x_reads(to_i2c_client(dev), reg, len, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(da903x_reads);
|
||||
|
||||
int da903x_set_bits(struct device *dev, int reg, uint8_t bit_mask)
|
||||
{
|
||||
struct da903x_chip *chip = dev_get_drvdata(dev);
|
||||
@ -435,13 +447,13 @@ static const struct i2c_device_id da903x_id_table[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, da903x_id_table);
|
||||
|
||||
static int __devexit __remove_subdev(struct device *dev, void *unused)
|
||||
static int __remove_subdev(struct device *dev, void *unused)
|
||||
{
|
||||
platform_device_unregister(to_platform_device(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit da903x_remove_subdevs(struct da903x_chip *chip)
|
||||
static int da903x_remove_subdevs(struct da903x_chip *chip)
|
||||
{
|
||||
return device_for_each_child(chip->dev, NULL, __remove_subdev);
|
||||
}
|
||||
|
420
drivers/mfd/dm355evm_msp.c
Normal file
420
drivers/mfd/dm355evm_msp.c
Normal file
@ -0,0 +1,420 @@
|
||||
/*
|
||||
* dm355evm_msp.c - driver for MSP430 firmware on DM355EVM board
|
||||
*
|
||||
* Copyright (C) 2008 David Brownell
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/dm355evm_msp.h>
|
||||
|
||||
|
||||
/*
|
||||
* The DM355 is a DaVinci chip with video support but no C64+ DSP. Its
|
||||
* EVM board has an MSP430 programmed with firmware for various board
|
||||
* support functions. This driver exposes some of them directly, and
|
||||
* supports other drivers (e.g. RTC, input) for more complex access.
|
||||
*
|
||||
* Because this firmware is entirely board-specific, this file embeds
|
||||
* knowledge that would be passed as platform_data in a generic driver.
|
||||
*
|
||||
* This driver was tested with firmware revision A4.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_KEYBOARD_DM355EVM) \
|
||||
|| defined(CONFIG_KEYBOARD_DM355EVM_MODULE)
|
||||
#define msp_has_keyboard() true
|
||||
#else
|
||||
#define msp_has_keyboard() false
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
|
||||
#define msp_has_leds() true
|
||||
#else
|
||||
#define msp_has_leds() false
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_RTC_DRV_DM355EVM) || defined(CONFIG_RTC_DRV_DM355EVM_MODULE)
|
||||
#define msp_has_rtc() true
|
||||
#else
|
||||
#define msp_has_rtc() false
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_VIDEO_TVP514X) || defined(CONFIG_VIDEO_TVP514X_MODULE)
|
||||
#define msp_has_tvp() true
|
||||
#else
|
||||
#define msp_has_tvp() false
|
||||
#endif
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/* REVISIT for paranoia's sake, retry reads/writes on error */
|
||||
|
||||
static struct i2c_client *msp430;
|
||||
|
||||
/**
|
||||
* dm355evm_msp_write - Writes a register in dm355evm_msp
|
||||
* @value: the value to be written
|
||||
* @reg: register address
|
||||
*
|
||||
* Returns result of operation - 0 is success, else negative errno
|
||||
*/
|
||||
int dm355evm_msp_write(u8 value, u8 reg)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(msp430, reg, value);
|
||||
}
|
||||
EXPORT_SYMBOL(dm355evm_msp_write);
|
||||
|
||||
/**
|
||||
* dm355evm_msp_read - Reads a register from dm355evm_msp
|
||||
* @reg: register address
|
||||
*
|
||||
* Returns result of operation - value, or negative errno
|
||||
*/
|
||||
int dm355evm_msp_read(u8 reg)
|
||||
{
|
||||
return i2c_smbus_read_byte_data(msp430, reg);
|
||||
}
|
||||
EXPORT_SYMBOL(dm355evm_msp_read);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Many of the msp430 pins are just used as fixed-direction GPIOs.
|
||||
* We could export a few more of them this way, if we wanted.
|
||||
*/
|
||||
#define MSP_GPIO(bit,reg) ((DM355EVM_MSP_ ## reg) << 3 | (bit))
|
||||
|
||||
static const u8 msp_gpios[] = {
|
||||
/* eight leds */
|
||||
MSP_GPIO(0, LED), MSP_GPIO(1, LED),
|
||||
MSP_GPIO(2, LED), MSP_GPIO(3, LED),
|
||||
MSP_GPIO(4, LED), MSP_GPIO(5, LED),
|
||||
MSP_GPIO(6, LED), MSP_GPIO(7, LED),
|
||||
/* SW6 and the NTSC/nPAL jumper */
|
||||
MSP_GPIO(0, SWITCH1), MSP_GPIO(1, SWITCH1),
|
||||
MSP_GPIO(2, SWITCH1), MSP_GPIO(3, SWITCH1),
|
||||
MSP_GPIO(4, SWITCH1),
|
||||
};
|
||||
|
||||
#define MSP_GPIO_REG(offset) (msp_gpios[(offset)] >> 3)
|
||||
#define MSP_GPIO_MASK(offset) BIT(msp_gpios[(offset)] & 0x07)
|
||||
|
||||
static int msp_gpio_in(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
switch (MSP_GPIO_REG(offset)) {
|
||||
case DM355EVM_MSP_SWITCH1:
|
||||
case DM355EVM_MSP_SWITCH2:
|
||||
case DM355EVM_MSP_SDMMC:
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static u8 msp_led_cache;
|
||||
|
||||
static int msp_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
int reg, status;
|
||||
|
||||
reg = MSP_GPIO_REG(offset);
|
||||
status = dm355evm_msp_read(reg);
|
||||
if (status < 0)
|
||||
return status;
|
||||
if (reg == DM355EVM_MSP_LED)
|
||||
msp_led_cache = status;
|
||||
return status & MSP_GPIO_MASK(offset);
|
||||
}
|
||||
|
||||
static int msp_gpio_out(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
int mask, bits;
|
||||
|
||||
/* NOTE: there are some other signals that could be
|
||||
* packaged as output GPIOs, but they aren't as useful
|
||||
* as the LEDs ... so for now we don't.
|
||||
*/
|
||||
if (MSP_GPIO_REG(offset) != DM355EVM_MSP_LED)
|
||||
return -EINVAL;
|
||||
|
||||
mask = MSP_GPIO_MASK(offset);
|
||||
bits = msp_led_cache;
|
||||
|
||||
bits &= ~mask;
|
||||
if (value)
|
||||
bits |= mask;
|
||||
msp_led_cache = bits;
|
||||
|
||||
return dm355evm_msp_write(bits, DM355EVM_MSP_LED);
|
||||
}
|
||||
|
||||
static void msp_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
msp_gpio_out(chip, offset, value);
|
||||
}
|
||||
|
||||
static struct gpio_chip dm355evm_msp_gpio = {
|
||||
.label = "dm355evm_msp",
|
||||
.owner = THIS_MODULE,
|
||||
.direction_input = msp_gpio_in,
|
||||
.get = msp_gpio_get,
|
||||
.direction_output = msp_gpio_out,
|
||||
.set = msp_gpio_set,
|
||||
.base = -EINVAL, /* dynamic assignment */
|
||||
.ngpio = ARRAY_SIZE(msp_gpios),
|
||||
.can_sleep = true,
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static struct device *add_child(struct i2c_client *client, const char *name,
|
||||
void *pdata, unsigned pdata_len,
|
||||
bool can_wakeup, int irq)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
int status;
|
||||
|
||||
pdev = platform_device_alloc(name, -1);
|
||||
if (!pdev) {
|
||||
dev_dbg(&client->dev, "can't alloc dev\n");
|
||||
status = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, can_wakeup);
|
||||
pdev->dev.parent = &client->dev;
|
||||
|
||||
if (pdata) {
|
||||
status = platform_device_add_data(pdev, pdata, pdata_len);
|
||||
if (status < 0) {
|
||||
dev_dbg(&pdev->dev, "can't add platform_data\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (irq) {
|
||||
struct resource r = {
|
||||
.start = irq,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
};
|
||||
|
||||
status = platform_device_add_resources(pdev, &r, 1);
|
||||
if (status < 0) {
|
||||
dev_dbg(&pdev->dev, "can't add irq\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
status = platform_device_add(pdev);
|
||||
|
||||
err:
|
||||
if (status < 0) {
|
||||
platform_device_put(pdev);
|
||||
dev_err(&client->dev, "can't add %s dev\n", name);
|
||||
return ERR_PTR(status);
|
||||
}
|
||||
return &pdev->dev;
|
||||
}
|
||||
|
||||
static int add_children(struct i2c_client *client)
|
||||
{
|
||||
static const struct {
|
||||
int offset;
|
||||
char *label;
|
||||
} config_inputs[] = {
|
||||
/* 8 == right after the LEDs */
|
||||
{ 8 + 0, "sw6_1", },
|
||||
{ 8 + 1, "sw6_2", },
|
||||
{ 8 + 2, "sw6_3", },
|
||||
{ 8 + 3, "sw6_4", },
|
||||
{ 8 + 4, "NTSC/nPAL", },
|
||||
};
|
||||
|
||||
struct device *child;
|
||||
int status;
|
||||
int i;
|
||||
|
||||
/* GPIO-ish stuff */
|
||||
dm355evm_msp_gpio.dev = &client->dev;
|
||||
status = gpiochip_add(&dm355evm_msp_gpio);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
/* LED output */
|
||||
if (msp_has_leds()) {
|
||||
#define GPIO_LED(l) .name = l, .active_low = true
|
||||
static struct gpio_led evm_leds[] = {
|
||||
{ GPIO_LED("dm355evm::ds14"),
|
||||
.default_trigger = "heartbeat", },
|
||||
{ GPIO_LED("dm355evm::ds15"),
|
||||
.default_trigger = "mmc0", },
|
||||
{ GPIO_LED("dm355evm::ds16"),
|
||||
/* could also be a CE-ATA drive */
|
||||
.default_trigger = "mmc1", },
|
||||
{ GPIO_LED("dm355evm::ds17"),
|
||||
.default_trigger = "nand-disk", },
|
||||
{ GPIO_LED("dm355evm::ds18"), },
|
||||
{ GPIO_LED("dm355evm::ds19"), },
|
||||
{ GPIO_LED("dm355evm::ds20"), },
|
||||
{ GPIO_LED("dm355evm::ds21"), },
|
||||
};
|
||||
#undef GPIO_LED
|
||||
|
||||
struct gpio_led_platform_data evm_led_data = {
|
||||
.num_leds = ARRAY_SIZE(evm_leds),
|
||||
.leds = evm_leds,
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(evm_leds); i++)
|
||||
evm_leds[i].gpio = i + dm355evm_msp_gpio.base;
|
||||
|
||||
/* NOTE: these are the only fully programmable LEDs
|
||||
* on the board, since GPIO-61/ds22 (and many signals
|
||||
* going to DC7) must be used for AEMIF address lines
|
||||
* unless the top 1 GB of NAND is unused...
|
||||
*/
|
||||
child = add_child(client, "leds-gpio",
|
||||
&evm_led_data, sizeof(evm_led_data),
|
||||
false, 0);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
}
|
||||
|
||||
/* configuration inputs */
|
||||
for (i = 0; i < ARRAY_SIZE(config_inputs); i++) {
|
||||
int gpio = dm355evm_msp_gpio.base + config_inputs[i].offset;
|
||||
|
||||
gpio_request(gpio, config_inputs[i].label);
|
||||
gpio_direction_input(gpio);
|
||||
|
||||
/* make it easy for userspace to see these */
|
||||
gpio_export(gpio, false);
|
||||
}
|
||||
|
||||
/* RTC is a 32 bit counter, no alarm */
|
||||
if (msp_has_rtc()) {
|
||||
child = add_child(client, "rtc-dm355evm",
|
||||
NULL, 0, false, 0);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
}
|
||||
|
||||
/* input from buttons and IR remote (uses the IRQ) */
|
||||
if (msp_has_keyboard()) {
|
||||
child = add_child(client, "dm355evm_keys",
|
||||
NULL, 0, true, client->irq);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static void dm355evm_command(unsigned command)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = dm355evm_msp_write(command, DM355EVM_MSP_COMMAND);
|
||||
if (status < 0)
|
||||
dev_err(&msp430->dev, "command %d failure %d\n",
|
||||
command, status);
|
||||
}
|
||||
|
||||
static void dm355evm_power_off(void)
|
||||
{
|
||||
dm355evm_command(MSP_COMMAND_POWEROFF);
|
||||
}
|
||||
|
||||
static int dm355evm_msp_remove(struct i2c_client *client)
|
||||
{
|
||||
pm_power_off = NULL;
|
||||
msp430 = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dm355evm_msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
int status;
|
||||
const char *video = msp_has_tvp() ? "TVP5146" : "imager";
|
||||
|
||||
if (msp430)
|
||||
return -EBUSY;
|
||||
msp430 = client;
|
||||
|
||||
/* display revision status; doubles as sanity check */
|
||||
status = dm355evm_msp_read(DM355EVM_MSP_FIRMREV);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
dev_info(&client->dev, "firmware v.%02X, %s as video-in\n",
|
||||
status, video);
|
||||
|
||||
/* mux video input: either tvp5146 or some external imager */
|
||||
status = dm355evm_msp_write(msp_has_tvp() ? 0 : MSP_VIDEO_IMAGER,
|
||||
DM355EVM_MSP_VIDEO_IN);
|
||||
if (status < 0)
|
||||
dev_warn(&client->dev, "error %d muxing %s as video-in\n",
|
||||
status, video);
|
||||
|
||||
/* init LED cache, and turn off the LEDs */
|
||||
msp_led_cache = 0xff;
|
||||
dm355evm_msp_write(msp_led_cache, DM355EVM_MSP_LED);
|
||||
|
||||
/* export capabilities we support */
|
||||
status = add_children(client);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
|
||||
/* PM hookup */
|
||||
pm_power_off = dm355evm_power_off;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
/* FIXME remove children ... */
|
||||
dm355evm_msp_remove(client);
|
||||
return status;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id dm355evm_msp_ids[] = {
|
||||
{ "dm355evm_msp", 0 },
|
||||
{ /* end of list */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, dm355evm_msp_ids);
|
||||
|
||||
static struct i2c_driver dm355evm_msp_driver = {
|
||||
.driver.name = "dm355evm_msp",
|
||||
.id_table = dm355evm_msp_ids,
|
||||
.probe = dm355evm_msp_probe,
|
||||
.remove = dm355evm_msp_remove,
|
||||
};
|
||||
|
||||
static int __init dm355evm_msp_init(void)
|
||||
{
|
||||
return i2c_add_driver(&dm355evm_msp_driver);
|
||||
}
|
||||
subsys_initcall(dm355evm_msp_init);
|
||||
|
||||
static void __exit dm355evm_msp_exit(void)
|
||||
{
|
||||
i2c_del_driver(&dm355evm_msp_driver);
|
||||
}
|
||||
module_exit(dm355evm_msp_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Interface to MSP430 firmware on DM355EVM");
|
||||
MODULE_LICENSE("GPL");
|
@ -34,6 +34,7 @@ static int mfd_add_device(struct device *parent, int id,
|
||||
goto fail_device;
|
||||
|
||||
pdev->dev.parent = parent;
|
||||
platform_set_drvdata(pdev, cell->driver_data);
|
||||
|
||||
ret = platform_device_add_data(pdev,
|
||||
cell->platform_data, cell->data_size);
|
||||
|
@ -33,6 +33,8 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/regulator/machine.h>
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/twl4030.h>
|
||||
|
||||
@ -71,6 +73,13 @@
|
||||
#define twl_has_gpio() false
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_REGULATOR_TWL4030) \
|
||||
|| defined(CONFIG_REGULATOR_TWL4030_MODULE)
|
||||
#define twl_has_regulator() true
|
||||
#else
|
||||
#define twl_has_regulator() false
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_TWL4030_MADC) || defined(CONFIG_TWL4030_MADC_MODULE)
|
||||
#define twl_has_madc() true
|
||||
#else
|
||||
@ -149,6 +158,10 @@
|
||||
#define HIGH_PERF_SQ (1 << 3)
|
||||
|
||||
|
||||
/* chip-specific feature flags, for i2c_device_id.driver_data */
|
||||
#define TWL4030_VAUX2 BIT(0) /* pre-5030 voltage ranges */
|
||||
#define TPS_SUBSET BIT(1) /* tps659[23]0 have fewer LDOs */
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/* is driver active, bound to a chip? */
|
||||
@ -225,7 +238,7 @@ static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
|
||||
*
|
||||
* Returns the result of operation - 0 is success
|
||||
*/
|
||||
int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, u8 num_bytes)
|
||||
int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
|
||||
{
|
||||
int ret;
|
||||
int sid;
|
||||
@ -274,7 +287,7 @@ EXPORT_SYMBOL(twl4030_i2c_write);
|
||||
*
|
||||
* Returns result of operation - num_bytes is success else failure.
|
||||
*/
|
||||
int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, u8 num_bytes)
|
||||
int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
|
||||
{
|
||||
int ret;
|
||||
u8 val;
|
||||
@ -352,258 +365,258 @@ EXPORT_SYMBOL(twl4030_i2c_read_u8);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static struct device *
|
||||
add_numbered_child(unsigned chip, const char *name, int num,
|
||||
void *pdata, unsigned pdata_len,
|
||||
bool can_wakeup, int irq0, int irq1)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct twl4030_client *twl = &twl4030_modules[chip];
|
||||
int status;
|
||||
|
||||
pdev = platform_device_alloc(name, num);
|
||||
if (!pdev) {
|
||||
dev_dbg(&twl->client->dev, "can't alloc dev\n");
|
||||
status = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, can_wakeup);
|
||||
pdev->dev.parent = &twl->client->dev;
|
||||
|
||||
if (pdata) {
|
||||
status = platform_device_add_data(pdev, pdata, pdata_len);
|
||||
if (status < 0) {
|
||||
dev_dbg(&pdev->dev, "can't add platform_data\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (irq0) {
|
||||
struct resource r[2] = {
|
||||
{ .start = irq0, .flags = IORESOURCE_IRQ, },
|
||||
{ .start = irq1, .flags = IORESOURCE_IRQ, },
|
||||
};
|
||||
|
||||
status = platform_device_add_resources(pdev, r, irq1 ? 2 : 1);
|
||||
if (status < 0) {
|
||||
dev_dbg(&pdev->dev, "can't add irqs\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
status = platform_device_add(pdev);
|
||||
|
||||
err:
|
||||
if (status < 0) {
|
||||
platform_device_put(pdev);
|
||||
dev_err(&twl->client->dev, "can't add %s dev\n", name);
|
||||
return ERR_PTR(status);
|
||||
}
|
||||
return &pdev->dev;
|
||||
}
|
||||
|
||||
static inline struct device *add_child(unsigned chip, const char *name,
|
||||
void *pdata, unsigned pdata_len,
|
||||
bool can_wakeup, int irq0, int irq1)
|
||||
{
|
||||
return add_numbered_child(chip, name, -1, pdata, pdata_len,
|
||||
can_wakeup, irq0, irq1);
|
||||
}
|
||||
|
||||
static struct device *
|
||||
add_regulator_linked(int num, struct regulator_init_data *pdata,
|
||||
struct regulator_consumer_supply *consumers,
|
||||
unsigned num_consumers)
|
||||
{
|
||||
/* regulator framework demands init_data ... */
|
||||
if (!pdata)
|
||||
return NULL;
|
||||
|
||||
if (consumers) {
|
||||
pdata->consumer_supplies = consumers;
|
||||
pdata->num_consumer_supplies = num_consumers;
|
||||
}
|
||||
|
||||
/* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */
|
||||
return add_numbered_child(3, "twl4030_reg", num,
|
||||
pdata, sizeof(*pdata), false, 0, 0);
|
||||
}
|
||||
|
||||
static struct device *
|
||||
add_regulator(int num, struct regulator_init_data *pdata)
|
||||
{
|
||||
return add_regulator_linked(num, pdata, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: We know the first 8 IRQs after pdata->base_irq are
|
||||
* for the PIH, and the next are for the PWR_INT SIH, since
|
||||
* that's how twl_init_irq() sets things up.
|
||||
*/
|
||||
|
||||
static int add_children(struct twl4030_platform_data *pdata)
|
||||
static int
|
||||
add_children(struct twl4030_platform_data *pdata, unsigned long features)
|
||||
{
|
||||
struct platform_device *pdev = NULL;
|
||||
struct twl4030_client *twl = NULL;
|
||||
int status = 0;
|
||||
struct device *child;
|
||||
struct device *usb_transceiver = NULL;
|
||||
|
||||
if (twl_has_bci() && pdata->bci) {
|
||||
twl = &twl4030_modules[3];
|
||||
|
||||
pdev = platform_device_alloc("twl4030_bci", -1);
|
||||
if (!pdev) {
|
||||
pr_debug("%s: can't alloc bci dev\n", DRIVER_NAME);
|
||||
status = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
pdev->dev.parent = &twl->client->dev;
|
||||
status = platform_device_add_data(pdev, pdata->bci,
|
||||
sizeof(*pdata->bci));
|
||||
if (status < 0) {
|
||||
dev_dbg(&twl->client->dev,
|
||||
"can't add bci data, %d\n",
|
||||
status);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
struct resource r = {
|
||||
.start = pdata->irq_base + 8 + 1,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
};
|
||||
|
||||
status = platform_device_add_resources(pdev, &r, 1);
|
||||
}
|
||||
|
||||
if (status == 0)
|
||||
status = platform_device_add(pdev);
|
||||
|
||||
if (status < 0) {
|
||||
platform_device_put(pdev);
|
||||
dev_dbg(&twl->client->dev,
|
||||
"can't create bci dev, %d\n",
|
||||
status);
|
||||
goto err;
|
||||
}
|
||||
if (twl_has_bci() && pdata->bci && !(features & TPS_SUBSET)) {
|
||||
child = add_child(3, "twl4030_bci",
|
||||
pdata->bci, sizeof(*pdata->bci),
|
||||
false,
|
||||
/* irq0 = CHG_PRES, irq1 = BCI */
|
||||
pdata->irq_base + 8 + 1, pdata->irq_base + 2);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
}
|
||||
|
||||
if (twl_has_gpio() && pdata->gpio) {
|
||||
twl = &twl4030_modules[1];
|
||||
|
||||
pdev = platform_device_alloc("twl4030_gpio", -1);
|
||||
if (!pdev) {
|
||||
pr_debug("%s: can't alloc gpio dev\n", DRIVER_NAME);
|
||||
status = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* more driver model init */
|
||||
if (status == 0) {
|
||||
pdev->dev.parent = &twl->client->dev;
|
||||
/* device_init_wakeup(&pdev->dev, 1); */
|
||||
|
||||
status = platform_device_add_data(pdev, pdata->gpio,
|
||||
sizeof(*pdata->gpio));
|
||||
if (status < 0) {
|
||||
dev_dbg(&twl->client->dev,
|
||||
"can't add gpio data, %d\n",
|
||||
status);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
/* GPIO module IRQ */
|
||||
if (status == 0) {
|
||||
struct resource r = {
|
||||
.start = pdata->irq_base + 0,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
};
|
||||
|
||||
status = platform_device_add_resources(pdev, &r, 1);
|
||||
}
|
||||
|
||||
if (status == 0)
|
||||
status = platform_device_add(pdev);
|
||||
|
||||
if (status < 0) {
|
||||
platform_device_put(pdev);
|
||||
dev_dbg(&twl->client->dev,
|
||||
"can't create gpio dev, %d\n",
|
||||
status);
|
||||
goto err;
|
||||
}
|
||||
child = add_child(1, "twl4030_gpio",
|
||||
pdata->gpio, sizeof(*pdata->gpio),
|
||||
false, pdata->irq_base + 0, 0);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
}
|
||||
|
||||
if (twl_has_keypad() && pdata->keypad) {
|
||||
pdev = platform_device_alloc("twl4030_keypad", -1);
|
||||
if (pdev) {
|
||||
twl = &twl4030_modules[2];
|
||||
pdev->dev.parent = &twl->client->dev;
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
status = platform_device_add_data(pdev, pdata->keypad,
|
||||
sizeof(*pdata->keypad));
|
||||
if (status < 0) {
|
||||
dev_dbg(&twl->client->dev,
|
||||
"can't add keypad data, %d\n",
|
||||
status);
|
||||
platform_device_put(pdev);
|
||||
goto err;
|
||||
}
|
||||
status = platform_device_add(pdev);
|
||||
if (status < 0) {
|
||||
platform_device_put(pdev);
|
||||
dev_dbg(&twl->client->dev,
|
||||
"can't create keypad dev, %d\n",
|
||||
status);
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
pr_debug("%s: can't alloc keypad dev\n", DRIVER_NAME);
|
||||
status = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
child = add_child(2, "twl4030_keypad",
|
||||
pdata->keypad, sizeof(*pdata->keypad),
|
||||
true, pdata->irq_base + 1, 0);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
}
|
||||
|
||||
if (twl_has_madc() && pdata->madc) {
|
||||
pdev = platform_device_alloc("twl4030_madc", -1);
|
||||
if (pdev) {
|
||||
twl = &twl4030_modules[2];
|
||||
pdev->dev.parent = &twl->client->dev;
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
status = platform_device_add_data(pdev, pdata->madc,
|
||||
sizeof(*pdata->madc));
|
||||
if (status < 0) {
|
||||
platform_device_put(pdev);
|
||||
dev_dbg(&twl->client->dev,
|
||||
"can't add madc data, %d\n",
|
||||
status);
|
||||
goto err;
|
||||
}
|
||||
status = platform_device_add(pdev);
|
||||
if (status < 0) {
|
||||
platform_device_put(pdev);
|
||||
dev_dbg(&twl->client->dev,
|
||||
"can't create madc dev, %d\n",
|
||||
status);
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
pr_debug("%s: can't alloc madc dev\n", DRIVER_NAME);
|
||||
status = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
child = add_child(2, "twl4030_madc",
|
||||
pdata->madc, sizeof(*pdata->madc),
|
||||
true, pdata->irq_base + 3, 0);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
}
|
||||
|
||||
if (twl_has_rtc()) {
|
||||
twl = &twl4030_modules[3];
|
||||
|
||||
pdev = platform_device_alloc("twl4030_rtc", -1);
|
||||
if (!pdev) {
|
||||
pr_debug("%s: can't alloc rtc dev\n", DRIVER_NAME);
|
||||
status = -ENOMEM;
|
||||
} else {
|
||||
pdev->dev.parent = &twl->client->dev;
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* REVISIT platform_data here currently might use of
|
||||
* REVISIT platform_data here currently might expose the
|
||||
* "msecure" line ... but for now we just expect board
|
||||
* setup to tell the chip "we are secure" at all times.
|
||||
* setup to tell the chip "it's always ok to SET_TIME".
|
||||
* Eventually, Linux might become more aware of such
|
||||
* HW security concerns, and "least privilege".
|
||||
*/
|
||||
|
||||
/* RTC module IRQ */
|
||||
if (status == 0) {
|
||||
struct resource r = {
|
||||
.start = pdata->irq_base + 8 + 3,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
};
|
||||
|
||||
status = platform_device_add_resources(pdev, &r, 1);
|
||||
}
|
||||
|
||||
if (status == 0)
|
||||
status = platform_device_add(pdev);
|
||||
|
||||
if (status < 0) {
|
||||
platform_device_put(pdev);
|
||||
dev_dbg(&twl->client->dev,
|
||||
"can't create rtc dev, %d\n",
|
||||
status);
|
||||
goto err;
|
||||
}
|
||||
child = add_child(3, "twl4030_rtc",
|
||||
NULL, 0,
|
||||
true, pdata->irq_base + 8 + 3, 0);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
}
|
||||
|
||||
if (twl_has_usb() && pdata->usb) {
|
||||
twl = &twl4030_modules[0];
|
||||
child = add_child(0, "twl4030_usb",
|
||||
pdata->usb, sizeof(*pdata->usb),
|
||||
true,
|
||||
/* irq0 = USB_PRES, irq1 = USB */
|
||||
pdata->irq_base + 8 + 2, pdata->irq_base + 4);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
|
||||
pdev = platform_device_alloc("twl4030_usb", -1);
|
||||
if (!pdev) {
|
||||
pr_debug("%s: can't alloc usb dev\n", DRIVER_NAME);
|
||||
status = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
pdev->dev.parent = &twl->client->dev;
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
status = platform_device_add_data(pdev, pdata->usb,
|
||||
sizeof(*pdata->usb));
|
||||
if (status < 0) {
|
||||
platform_device_put(pdev);
|
||||
dev_dbg(&twl->client->dev,
|
||||
"can't add usb data, %d\n",
|
||||
status);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
struct resource r = {
|
||||
.start = pdata->irq_base + 8 + 2,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
};
|
||||
|
||||
status = platform_device_add_resources(pdev, &r, 1);
|
||||
}
|
||||
|
||||
if (status == 0)
|
||||
status = platform_device_add(pdev);
|
||||
|
||||
if (status < 0) {
|
||||
platform_device_put(pdev);
|
||||
dev_dbg(&twl->client->dev,
|
||||
"can't create usb dev, %d\n",
|
||||
status);
|
||||
}
|
||||
/* we need to connect regulators to this transceiver */
|
||||
usb_transceiver = child;
|
||||
}
|
||||
|
||||
err:
|
||||
if (status)
|
||||
pr_err("failed to add twl4030's children (status %d)\n", status);
|
||||
return status;
|
||||
if (twl_has_regulator()) {
|
||||
/*
|
||||
child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
*/
|
||||
|
||||
child = add_regulator(TWL4030_REG_VMMC1, pdata->vmmc1);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
|
||||
child = add_regulator(TWL4030_REG_VDAC, pdata->vdac);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
|
||||
child = add_regulator((features & TWL4030_VAUX2)
|
||||
? TWL4030_REG_VAUX2_4030
|
||||
: TWL4030_REG_VAUX2,
|
||||
pdata->vaux2);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
}
|
||||
|
||||
if (twl_has_regulator() && usb_transceiver) {
|
||||
static struct regulator_consumer_supply usb1v5 = {
|
||||
.supply = "usb1v5",
|
||||
};
|
||||
static struct regulator_consumer_supply usb1v8 = {
|
||||
.supply = "usb1v8",
|
||||
};
|
||||
static struct regulator_consumer_supply usb3v1 = {
|
||||
.supply = "usb3v1",
|
||||
};
|
||||
|
||||
/* this is a template that gets copied */
|
||||
struct regulator_init_data usb_fixed = {
|
||||
.constraints.valid_modes_mask =
|
||||
REGULATOR_MODE_NORMAL
|
||||
| REGULATOR_MODE_STANDBY,
|
||||
.constraints.valid_ops_mask =
|
||||
REGULATOR_CHANGE_MODE
|
||||
| REGULATOR_CHANGE_STATUS,
|
||||
};
|
||||
|
||||
usb1v5.dev = usb_transceiver;
|
||||
usb1v8.dev = usb_transceiver;
|
||||
usb3v1.dev = usb_transceiver;
|
||||
|
||||
child = add_regulator_linked(TWL4030_REG_VUSB1V5, &usb_fixed,
|
||||
&usb1v5, 1);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
|
||||
child = add_regulator_linked(TWL4030_REG_VUSB1V8, &usb_fixed,
|
||||
&usb1v8, 1);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
|
||||
child = add_regulator_linked(TWL4030_REG_VUSB3V1, &usb_fixed,
|
||||
&usb3v1, 1);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
}
|
||||
|
||||
/* maybe add LDOs that are omitted on cost-reduced parts */
|
||||
if (twl_has_regulator() && !(features & TPS_SUBSET)) {
|
||||
/*
|
||||
child = add_regulator(TWL4030_REG_VPLL2, pdata->vpll2);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
*/
|
||||
|
||||
child = add_regulator(TWL4030_REG_VMMC2, pdata->vmmc2);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
|
||||
child = add_regulator(TWL4030_REG_VSIM, pdata->vsim);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
|
||||
child = add_regulator(TWL4030_REG_VAUX1, pdata->vaux1);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
|
||||
child = add_regulator(TWL4030_REG_VAUX3, pdata->vaux3);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
|
||||
child = add_regulator(TWL4030_REG_VAUX4, pdata->vaux4);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
@ -645,12 +658,7 @@ static void __init clocks_init(void)
|
||||
osc = clk_get(NULL, "osc_ck");
|
||||
else
|
||||
osc = clk_get(NULL, "osc_sys_ck");
|
||||
#else
|
||||
/* REVISIT for non-OMAP systems, pass the clock rate from
|
||||
* board init code, using platform_data.
|
||||
*/
|
||||
osc = ERR_PTR(-EIO);
|
||||
#endif
|
||||
|
||||
if (IS_ERR(osc)) {
|
||||
printk(KERN_WARNING "Skipping twl4030 internal clock init and "
|
||||
"using bootloader value (unknown osc rate)\n");
|
||||
@ -660,6 +668,18 @@ static void __init clocks_init(void)
|
||||
rate = clk_get_rate(osc);
|
||||
clk_put(osc);
|
||||
|
||||
#else
|
||||
/* REVISIT for non-OMAP systems, pass the clock rate from
|
||||
* board init code, using platform_data.
|
||||
*/
|
||||
osc = ERR_PTR(-EIO);
|
||||
|
||||
printk(KERN_WARNING "Skipping twl4030 internal clock init and "
|
||||
"using bootloader value (unknown osc rate)\n");
|
||||
|
||||
return;
|
||||
#endif
|
||||
|
||||
switch (rate) {
|
||||
case 19200000:
|
||||
ctrl = HFCLK_FREQ_19p2_MHZ;
|
||||
@ -764,7 +784,7 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
status = add_children(pdata);
|
||||
status = add_children(pdata, id->driver_data);
|
||||
fail:
|
||||
if (status < 0)
|
||||
twl4030_remove(client);
|
||||
@ -772,11 +792,11 @@ fail:
|
||||
}
|
||||
|
||||
static const struct i2c_device_id twl4030_ids[] = {
|
||||
{ "twl4030", 0 }, /* "Triton 2" */
|
||||
{ "tps65950", 0 }, /* catalog version of twl4030 */
|
||||
{ "tps65930", 0 }, /* fewer LDOs and DACs; no charger */
|
||||
{ "tps65920", 0 }, /* fewer LDOs; no codec or charger */
|
||||
{ "twl5030", 0 }, /* T2 updated */
|
||||
{ "twl4030", TWL4030_VAUX2 }, /* "Triton 2" */
|
||||
{ "twl5030", 0 }, /* T2 updated */
|
||||
{ "tps65950", 0 }, /* catalog version of twl5030 */
|
||||
{ "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */
|
||||
{ "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */
|
||||
{ /* end of list */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, twl4030_ids);
|
||||
|
@ -180,10 +180,15 @@ static struct completion irq_event;
|
||||
static int twl4030_irq_thread(void *data)
|
||||
{
|
||||
long irq = (long)data;
|
||||
irq_desc_t *desc = irq_desc + irq;
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
static unsigned i2c_errors;
|
||||
const static unsigned max_i2c_errors = 100;
|
||||
|
||||
if (!desc) {
|
||||
pr_err("twl4030: Invalid IRQ: %ld\n", irq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
current->flags |= PF_NOFREEZE;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
@ -215,7 +220,13 @@ static int twl4030_irq_thread(void *data)
|
||||
pih_isr;
|
||||
pih_isr >>= 1, module_irq++) {
|
||||
if (pih_isr & 0x1) {
|
||||
irq_desc_t *d = irq_desc + module_irq;
|
||||
struct irq_desc *d = irq_to_desc(module_irq);
|
||||
|
||||
if (!d) {
|
||||
pr_err("twl4030: Invalid SIH IRQ: %d\n",
|
||||
module_irq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* These can't be masked ... always warn
|
||||
* if we get any surprises.
|
||||
@ -452,10 +463,16 @@ static void twl4030_sih_do_edge(struct work_struct *work)
|
||||
/* Modify only the bits we know must change */
|
||||
while (edge_change) {
|
||||
int i = fls(edge_change) - 1;
|
||||
struct irq_desc *d = irq_desc + i + agent->irq_base;
|
||||
struct irq_desc *d = irq_to_desc(i + agent->irq_base);
|
||||
int byte = 1 + (i >> 2);
|
||||
int off = (i & 0x3) * 2;
|
||||
|
||||
if (!d) {
|
||||
pr_err("twl4030: Invalid IRQ: %d\n",
|
||||
i + agent->irq_base);
|
||||
return;
|
||||
}
|
||||
|
||||
bytes[byte] &= ~(0x03 << off);
|
||||
|
||||
spin_lock_irq(&d->lock);
|
||||
@ -512,9 +529,14 @@ static void twl4030_sih_unmask(unsigned irq)
|
||||
static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
|
||||
{
|
||||
struct sih_agent *sih = get_irq_chip_data(irq);
|
||||
struct irq_desc *desc = irq_desc + irq;
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
unsigned long flags;
|
||||
|
||||
if (!desc) {
|
||||
pr_err("twl4030: Invalid IRQ: %d\n", irq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -63,7 +63,6 @@
|
||||
*/
|
||||
static DEFINE_MUTEX(io_mutex);
|
||||
static DEFINE_MUTEX(reg_lock_mutex);
|
||||
static DEFINE_MUTEX(auxadc_mutex);
|
||||
|
||||
/* Perform a physical read from the device.
|
||||
*/
|
||||
@ -299,6 +298,13 @@ int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8350_block_write);
|
||||
|
||||
/**
|
||||
* wm8350_reg_lock()
|
||||
*
|
||||
* The WM8350 has a hardware lock which can be used to prevent writes to
|
||||
* some registers (generally those which can cause particularly serious
|
||||
* problems if misused). This function enables that lock.
|
||||
*/
|
||||
int wm8350_reg_lock(struct wm8350 *wm8350)
|
||||
{
|
||||
u16 key = WM8350_LOCK_KEY;
|
||||
@ -314,6 +320,15 @@ int wm8350_reg_lock(struct wm8350 *wm8350)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8350_reg_lock);
|
||||
|
||||
/**
|
||||
* wm8350_reg_unlock()
|
||||
*
|
||||
* The WM8350 has a hardware lock which can be used to prevent writes to
|
||||
* some registers (generally those which can cause particularly serious
|
||||
* problems if misused). This function disables that lock so updates
|
||||
* can be performed. For maximum safety this should be done only when
|
||||
* required.
|
||||
*/
|
||||
int wm8350_reg_unlock(struct wm8350 *wm8350)
|
||||
{
|
||||
u16 key = WM8350_UNLOCK_KEY;
|
||||
@ -1066,38 +1081,158 @@ int wm8350_unmask_irq(struct wm8350 *wm8350, int irq)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8350_unmask_irq);
|
||||
|
||||
int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref)
|
||||
{
|
||||
u16 reg, result = 0;
|
||||
int tries = 5;
|
||||
|
||||
if (channel < WM8350_AUXADC_AUX1 || channel > WM8350_AUXADC_TEMP)
|
||||
return -EINVAL;
|
||||
if (channel >= WM8350_AUXADC_USB && channel <= WM8350_AUXADC_TEMP
|
||||
&& (scale != 0 || vref != 0))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&wm8350->auxadc_mutex);
|
||||
|
||||
/* Turn on the ADC */
|
||||
reg = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5);
|
||||
wm8350_reg_write(wm8350, WM8350_POWER_MGMT_5, reg | WM8350_AUXADC_ENA);
|
||||
|
||||
if (scale || vref) {
|
||||
reg = scale << 13;
|
||||
reg |= vref << 12;
|
||||
wm8350_reg_write(wm8350, WM8350_AUX1_READBACK + channel, reg);
|
||||
}
|
||||
|
||||
reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1);
|
||||
reg |= 1 << channel | WM8350_AUXADC_POLL;
|
||||
wm8350_reg_write(wm8350, WM8350_DIGITISER_CONTROL_1, reg);
|
||||
|
||||
do {
|
||||
schedule_timeout_interruptible(1);
|
||||
reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1);
|
||||
} while (tries-- && (reg & WM8350_AUXADC_POLL));
|
||||
|
||||
if (!tries)
|
||||
dev_err(wm8350->dev, "adc chn %d read timeout\n", channel);
|
||||
else
|
||||
result = wm8350_reg_read(wm8350,
|
||||
WM8350_AUX1_READBACK + channel);
|
||||
|
||||
/* Turn off the ADC */
|
||||
reg = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5);
|
||||
wm8350_reg_write(wm8350, WM8350_POWER_MGMT_5,
|
||||
reg & ~WM8350_AUXADC_ENA);
|
||||
|
||||
mutex_unlock(&wm8350->auxadc_mutex);
|
||||
|
||||
return result & WM8350_AUXADC_DATA1_MASK;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8350_read_auxadc);
|
||||
|
||||
/*
|
||||
* Cache is always host endian.
|
||||
*/
|
||||
static int wm8350_create_cache(struct wm8350 *wm8350, int mode)
|
||||
static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode)
|
||||
{
|
||||
int i, ret = 0;
|
||||
u16 value;
|
||||
const u16 *reg_map;
|
||||
|
||||
switch (mode) {
|
||||
#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0
|
||||
switch (type) {
|
||||
case 0:
|
||||
reg_map = wm8350_mode0_defaults;
|
||||
break;
|
||||
switch (mode) {
|
||||
#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0
|
||||
case 0:
|
||||
reg_map = wm8350_mode0_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1
|
||||
case 1:
|
||||
reg_map = wm8350_mode1_defaults;
|
||||
break;
|
||||
case 1:
|
||||
reg_map = wm8350_mode1_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2
|
||||
case 2:
|
||||
reg_map = wm8350_mode2_defaults;
|
||||
break;
|
||||
case 2:
|
||||
reg_map = wm8350_mode2_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3
|
||||
case 3:
|
||||
reg_map = wm8350_mode3_defaults;
|
||||
break;
|
||||
case 3:
|
||||
reg_map = wm8350_mode3_defaults;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
dev_err(wm8350->dev,
|
||||
"WM8350 configuration mode %d not supported\n",
|
||||
mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
switch (mode) {
|
||||
#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0
|
||||
case 0:
|
||||
reg_map = wm8351_mode0_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1
|
||||
case 1:
|
||||
reg_map = wm8351_mode1_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2
|
||||
case 2:
|
||||
reg_map = wm8351_mode2_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3
|
||||
case 3:
|
||||
reg_map = wm8351_mode3_defaults;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
dev_err(wm8350->dev,
|
||||
"WM8351 configuration mode %d not supported\n",
|
||||
mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
switch (mode) {
|
||||
#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0
|
||||
case 0:
|
||||
reg_map = wm8352_mode0_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1
|
||||
case 1:
|
||||
reg_map = wm8352_mode1_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2
|
||||
case 2:
|
||||
reg_map = wm8352_mode2_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3
|
||||
case 3:
|
||||
reg_map = wm8352_mode3_defaults;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
dev_err(wm8350->dev,
|
||||
"WM8352 configuration mode %d not supported\n",
|
||||
mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(wm8350->dev, "Configuration mode %d not supported\n",
|
||||
dev_err(wm8350->dev,
|
||||
"WM835x configuration mode %d not supported\n",
|
||||
mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -1163,53 +1298,113 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
|
||||
struct wm8350_platform_data *pdata)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
u16 id1, id2, mask, mode;
|
||||
u16 id1, id2, mask_rev;
|
||||
u16 cust_id, mode, chip_rev;
|
||||
|
||||
/* get WM8350 revision and config mode */
|
||||
wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1);
|
||||
wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2);
|
||||
wm8350->read_dev(wm8350, WM8350_REVISION, sizeof(mask_rev), &mask_rev);
|
||||
|
||||
id1 = be16_to_cpu(id1);
|
||||
id2 = be16_to_cpu(id2);
|
||||
mask_rev = be16_to_cpu(mask_rev);
|
||||
|
||||
if (id1 == 0x6143) {
|
||||
switch ((id2 & WM8350_CHIP_REV_MASK) >> 12) {
|
||||
case WM8350_REV_E:
|
||||
dev_info(wm8350->dev, "Found Rev E device\n");
|
||||
wm8350->rev = WM8350_REV_E;
|
||||
break;
|
||||
case WM8350_REV_F:
|
||||
dev_info(wm8350->dev, "Found Rev F device\n");
|
||||
wm8350->rev = WM8350_REV_F;
|
||||
break;
|
||||
case WM8350_REV_G:
|
||||
dev_info(wm8350->dev, "Found Rev G device\n");
|
||||
wm8350->rev = WM8350_REV_G;
|
||||
break;
|
||||
default:
|
||||
/* For safety we refuse to run on unknown hardware */
|
||||
dev_info(wm8350->dev, "Found unknown rev\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
dev_info(wm8350->dev, "Device with ID %x is not a WM8350\n",
|
||||
id1);
|
||||
if (id1 != 0x6143) {
|
||||
dev_err(wm8350->dev,
|
||||
"Device with ID %x is not a WM8350\n", id1);
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
mode = id2 & WM8350_CONF_STS_MASK >> 10;
|
||||
mask = id2 & WM8350_CUST_ID_MASK;
|
||||
dev_info(wm8350->dev, "Config mode %d, ROM mask %d\n", mode, mask);
|
||||
cust_id = id2 & WM8350_CUST_ID_MASK;
|
||||
chip_rev = (id2 & WM8350_CHIP_REV_MASK) >> 12;
|
||||
dev_info(wm8350->dev,
|
||||
"CONF_STS %d, CUST_ID %d, MASK_REV %d, CHIP_REV %d\n",
|
||||
mode, cust_id, mask_rev, chip_rev);
|
||||
|
||||
ret = wm8350_create_cache(wm8350, mode);
|
||||
if (cust_id != 0) {
|
||||
dev_err(wm8350->dev, "Unsupported CUST_ID\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch (mask_rev) {
|
||||
case 0:
|
||||
wm8350->pmic.max_dcdc = WM8350_DCDC_6;
|
||||
wm8350->pmic.max_isink = WM8350_ISINK_B;
|
||||
|
||||
switch (chip_rev) {
|
||||
case WM8350_REV_E:
|
||||
dev_info(wm8350->dev, "WM8350 Rev E\n");
|
||||
break;
|
||||
case WM8350_REV_F:
|
||||
dev_info(wm8350->dev, "WM8350 Rev F\n");
|
||||
break;
|
||||
case WM8350_REV_G:
|
||||
dev_info(wm8350->dev, "WM8350 Rev G\n");
|
||||
wm8350->power.rev_g_coeff = 1;
|
||||
break;
|
||||
case WM8350_REV_H:
|
||||
dev_info(wm8350->dev, "WM8350 Rev H\n");
|
||||
wm8350->power.rev_g_coeff = 1;
|
||||
break;
|
||||
default:
|
||||
/* For safety we refuse to run on unknown hardware */
|
||||
dev_err(wm8350->dev, "Unknown WM8350 CHIP_REV\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
wm8350->pmic.max_dcdc = WM8350_DCDC_4;
|
||||
wm8350->pmic.max_isink = WM8350_ISINK_A;
|
||||
|
||||
switch (chip_rev) {
|
||||
case 0:
|
||||
dev_info(wm8350->dev, "WM8351 Rev A\n");
|
||||
wm8350->power.rev_g_coeff = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(wm8350->dev, "Unknown WM8351 CHIP_REV\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
wm8350->pmic.max_dcdc = WM8350_DCDC_6;
|
||||
wm8350->pmic.max_isink = WM8350_ISINK_B;
|
||||
|
||||
switch (chip_rev) {
|
||||
case 0:
|
||||
dev_info(wm8350->dev, "WM8352 Rev A\n");
|
||||
wm8350->power.rev_g_coeff = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(wm8350->dev, "Unknown WM8352 CHIP_REV\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(wm8350->dev, "Unknown MASK_REV\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = wm8350_create_cache(wm8350, mask_rev, mode);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "wm8350: failed to create register cache\n");
|
||||
dev_err(wm8350->dev, "Failed to create register cache\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (pdata->init) {
|
||||
if (pdata && pdata->init) {
|
||||
ret = pdata->init(wm8350);
|
||||
if (ret != 0) {
|
||||
dev_err(wm8350->dev, "Platform init() failed: %d\n",
|
||||
@ -1218,6 +1413,7 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
|
||||
}
|
||||
}
|
||||
|
||||
mutex_init(&wm8350->auxadc_mutex);
|
||||
mutex_init(&wm8350->irq_mutex);
|
||||
INIT_WORK(&wm8350->irq_work, wm8350_irq_worker);
|
||||
if (irq) {
|
||||
|
@ -1,8 +1,6 @@
|
||||
/*
|
||||
* wm8350-i2c.c -- Generic I2C driver for Wolfson WM8350 PMIC
|
||||
*
|
||||
* This driver defines and configures the WM8350 for the Freescale i.MX32ADS.
|
||||
*
|
||||
* Copyright 2007, 2008 Wolfson Microelectronics PLC.
|
||||
*
|
||||
* Author: Liam Girdwood
|
||||
@ -99,6 +97,8 @@ static int wm8350_i2c_remove(struct i2c_client *i2c)
|
||||
|
||||
static const struct i2c_device_id wm8350_i2c_id[] = {
|
||||
{ "wm8350", 0 },
|
||||
{ "wm8351", 0 },
|
||||
{ "wm8352", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, wm8350_i2c_id);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -15,6 +15,7 @@
|
||||
#include <linux/bug.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/wm8400-private.h>
|
||||
#include <linux/mfd/wm8400-audio.h>
|
||||
|
||||
@ -239,6 +240,16 @@ void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache);
|
||||
|
||||
static int wm8400_register_codec(struct wm8400 *wm8400)
|
||||
{
|
||||
struct mfd_cell cell = {
|
||||
.name = "wm8400-codec",
|
||||
.driver_data = wm8400,
|
||||
};
|
||||
|
||||
return mfd_add_devices(wm8400->dev, -1, &cell, 1, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* wm8400_init - Generic initialisation
|
||||
*
|
||||
@ -296,24 +307,32 @@ static int wm8400_init(struct wm8400 *wm8400,
|
||||
reg = (reg & WM8400_CHIP_REV_MASK) >> WM8400_CHIP_REV_SHIFT;
|
||||
dev_info(wm8400->dev, "WM8400 revision %x\n", reg);
|
||||
|
||||
ret = wm8400_register_codec(wm8400);
|
||||
if (ret != 0) {
|
||||
dev_err(wm8400->dev, "Failed to register codec\n");
|
||||
goto err_children;
|
||||
}
|
||||
|
||||
if (pdata && pdata->platform_init) {
|
||||
ret = pdata->platform_init(wm8400->dev);
|
||||
if (ret != 0)
|
||||
if (ret != 0) {
|
||||
dev_err(wm8400->dev, "Platform init failed: %d\n",
|
||||
ret);
|
||||
goto err_children;
|
||||
}
|
||||
} else
|
||||
dev_warn(wm8400->dev, "No platform initialisation supplied\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_children:
|
||||
mfd_remove_devices(wm8400->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wm8400_release(struct wm8400 *wm8400)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wm8400->regulators); i++)
|
||||
if (wm8400->regulators[i].name)
|
||||
platform_device_unregister(&wm8400->regulators[i]);
|
||||
mfd_remove_devices(wm8400->dev);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
|
@ -29,6 +29,13 @@ config APM_POWER
|
||||
Say Y here to enable support APM status emulation using
|
||||
battery class devices.
|
||||
|
||||
config WM8350_POWER
|
||||
tristate "WM8350 PMU support"
|
||||
depends on MFD_WM8350
|
||||
help
|
||||
Say Y here to enable support for the power management unit
|
||||
provided by the Wolfson Microelectronics WM8350 PMIC.
|
||||
|
||||
config BATTERY_DS2760
|
||||
tristate "DS2760 battery driver (HP iPAQ & others)"
|
||||
select W1
|
||||
@ -68,4 +75,11 @@ config BATTERY_BQ27x00
|
||||
help
|
||||
Say Y here to enable support for batteries with BQ27200(I2C) chip.
|
||||
|
||||
config BATTERY_DA9030
|
||||
tristate "DA9030 battery driver"
|
||||
depends on PMIC_DA903X
|
||||
help
|
||||
Say Y here to enable support for batteries charger integrated into
|
||||
DA9030 PMIC.
|
||||
|
||||
endif # POWER_SUPPLY
|
||||
|
@ -16,6 +16,7 @@ obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
|
||||
|
||||
obj-$(CONFIG_PDA_POWER) += pda_power.o
|
||||
obj-$(CONFIG_APM_POWER) += apm_power.o
|
||||
obj-$(CONFIG_WM8350_POWER) += wm8350_power.o
|
||||
|
||||
obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
|
||||
obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
|
||||
@ -23,3 +24,4 @@ obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
|
||||
obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o
|
||||
obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
|
||||
obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
|
||||
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
|
||||
|
600
drivers/power/da9030_battery.c
Normal file
600
drivers/power/da9030_battery.c
Normal file
@ -0,0 +1,600 @@
|
||||
/*
|
||||
* Battery charger driver for Dialog Semiconductor DA9030
|
||||
*
|
||||
* Copyright (C) 2008 Compulab, Ltd.
|
||||
* Mike Rapoport <mike@compulab.co.il>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/mfd/da903x.h>
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#define DA9030_STATUS_CHDET (1 << 3)
|
||||
|
||||
#define DA9030_FAULT_LOG 0x0a
|
||||
#define DA9030_FAULT_LOG_OVER_TEMP (1 << 7)
|
||||
#define DA9030_FAULT_LOG_VBAT_OVER (1 << 4)
|
||||
|
||||
#define DA9030_CHARGE_CONTROL 0x28
|
||||
#define DA9030_CHRG_CHARGER_ENABLE (1 << 7)
|
||||
|
||||
#define DA9030_ADC_MAN_CONTROL 0x30
|
||||
#define DA9030_ADC_TBATREF_ENABLE (1 << 5)
|
||||
#define DA9030_ADC_LDO_INT_ENABLE (1 << 4)
|
||||
|
||||
#define DA9030_ADC_AUTO_CONTROL 0x31
|
||||
#define DA9030_ADC_TBAT_ENABLE (1 << 5)
|
||||
#define DA9030_ADC_VBAT_IN_TXON (1 << 4)
|
||||
#define DA9030_ADC_VCH_ENABLE (1 << 3)
|
||||
#define DA9030_ADC_ICH_ENABLE (1 << 2)
|
||||
#define DA9030_ADC_VBAT_ENABLE (1 << 1)
|
||||
#define DA9030_ADC_AUTO_SLEEP_ENABLE (1 << 0)
|
||||
|
||||
#define DA9030_VBATMON 0x32
|
||||
#define DA9030_VBATMONTXON 0x33
|
||||
#define DA9030_TBATHIGHP 0x34
|
||||
#define DA9030_TBATHIGHN 0x35
|
||||
#define DA9030_TBATLOW 0x36
|
||||
|
||||
#define DA9030_VBAT_RES 0x41
|
||||
#define DA9030_VBATMIN_RES 0x42
|
||||
#define DA9030_VBATMINTXON_RES 0x43
|
||||
#define DA9030_ICHMAX_RES 0x44
|
||||
#define DA9030_ICHMIN_RES 0x45
|
||||
#define DA9030_ICHAVERAGE_RES 0x46
|
||||
#define DA9030_VCHMAX_RES 0x47
|
||||
#define DA9030_VCHMIN_RES 0x48
|
||||
#define DA9030_TBAT_RES 0x49
|
||||
|
||||
struct da9030_adc_res {
|
||||
uint8_t vbat_res;
|
||||
uint8_t vbatmin_res;
|
||||
uint8_t vbatmintxon;
|
||||
uint8_t ichmax_res;
|
||||
uint8_t ichmin_res;
|
||||
uint8_t ichaverage_res;
|
||||
uint8_t vchmax_res;
|
||||
uint8_t vchmin_res;
|
||||
uint8_t tbat_res;
|
||||
uint8_t adc_in4_res;
|
||||
uint8_t adc_in5_res;
|
||||
};
|
||||
|
||||
struct da9030_battery_thresholds {
|
||||
int tbat_low;
|
||||
int tbat_high;
|
||||
int tbat_restart;
|
||||
|
||||
int vbat_low;
|
||||
int vbat_crit;
|
||||
int vbat_charge_start;
|
||||
int vbat_charge_stop;
|
||||
int vbat_charge_restart;
|
||||
|
||||
int vcharge_min;
|
||||
int vcharge_max;
|
||||
};
|
||||
|
||||
struct da9030_charger {
|
||||
struct power_supply psy;
|
||||
|
||||
struct device *master;
|
||||
|
||||
struct da9030_adc_res adc;
|
||||
struct delayed_work work;
|
||||
unsigned int interval;
|
||||
|
||||
struct power_supply_info *battery_info;
|
||||
|
||||
struct da9030_battery_thresholds thresholds;
|
||||
|
||||
unsigned int charge_milliamp;
|
||||
unsigned int charge_millivolt;
|
||||
|
||||
/* charger status */
|
||||
bool chdet;
|
||||
uint8_t fault;
|
||||
int mA;
|
||||
int mV;
|
||||
bool is_on;
|
||||
|
||||
struct notifier_block nb;
|
||||
|
||||
/* platform callbacks for battery low and critical events */
|
||||
void (*battery_low)(void);
|
||||
void (*battery_critical)(void);
|
||||
|
||||
struct dentry *debug_file;
|
||||
};
|
||||
|
||||
static inline int da9030_reg_to_mV(int reg)
|
||||
{
|
||||
return ((reg * 2650) >> 8) + 2650;
|
||||
}
|
||||
|
||||
static inline int da9030_millivolt_to_reg(int mV)
|
||||
{
|
||||
return ((mV - 2650) << 8) / 2650;
|
||||
}
|
||||
|
||||
static inline int da9030_reg_to_mA(int reg)
|
||||
{
|
||||
return ((reg * 24000) >> 8) / 15;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int bat_debug_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct da9030_charger *charger = s->private;
|
||||
|
||||
seq_printf(s, "charger is %s\n", charger->is_on ? "on" : "off");
|
||||
if (charger->chdet) {
|
||||
seq_printf(s, "iset = %dmA, vset = %dmV\n",
|
||||
charger->mA, charger->mV);
|
||||
}
|
||||
|
||||
seq_printf(s, "vbat_res = %d (%dmV)\n",
|
||||
charger->adc.vbat_res,
|
||||
da9030_reg_to_mV(charger->adc.vbat_res));
|
||||
seq_printf(s, "vbatmin_res = %d (%dmV)\n",
|
||||
charger->adc.vbatmin_res,
|
||||
da9030_reg_to_mV(charger->adc.vbatmin_res));
|
||||
seq_printf(s, "vbatmintxon = %d (%dmV)\n",
|
||||
charger->adc.vbatmintxon,
|
||||
da9030_reg_to_mV(charger->adc.vbatmintxon));
|
||||
seq_printf(s, "ichmax_res = %d (%dmA)\n",
|
||||
charger->adc.ichmax_res,
|
||||
da9030_reg_to_mV(charger->adc.ichmax_res));
|
||||
seq_printf(s, "ichmin_res = %d (%dmA)\n",
|
||||
charger->adc.ichmin_res,
|
||||
da9030_reg_to_mA(charger->adc.ichmin_res));
|
||||
seq_printf(s, "ichaverage_res = %d (%dmA)\n",
|
||||
charger->adc.ichaverage_res,
|
||||
da9030_reg_to_mA(charger->adc.ichaverage_res));
|
||||
seq_printf(s, "vchmax_res = %d (%dmV)\n",
|
||||
charger->adc.vchmax_res,
|
||||
da9030_reg_to_mA(charger->adc.vchmax_res));
|
||||
seq_printf(s, "vchmin_res = %d (%dmV)\n",
|
||||
charger->adc.vchmin_res,
|
||||
da9030_reg_to_mV(charger->adc.vchmin_res));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, bat_debug_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations bat_debug_fops = {
|
||||
.open = debug_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
|
||||
{
|
||||
charger->debug_file = debugfs_create_file("charger", 0666, 0, charger,
|
||||
&bat_debug_fops);
|
||||
return charger->debug_file;
|
||||
}
|
||||
|
||||
static void da9030_bat_remove_debugfs(struct da9030_charger *charger)
|
||||
{
|
||||
debugfs_remove(charger->debug_file);
|
||||
}
|
||||
#else
|
||||
static inline struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline void da9030_bat_remove_debugfs(struct da9030_charger *charger)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void da9030_read_adc(struct da9030_charger *charger,
|
||||
struct da9030_adc_res *adc)
|
||||
{
|
||||
da903x_reads(charger->master, DA9030_VBAT_RES,
|
||||
sizeof(*adc), (uint8_t *)adc);
|
||||
}
|
||||
|
||||
static void da9030_charger_update_state(struct da9030_charger *charger)
|
||||
{
|
||||
uint8_t val;
|
||||
|
||||
da903x_read(charger->master, DA9030_CHARGE_CONTROL, &val);
|
||||
charger->is_on = (val & DA9030_CHRG_CHARGER_ENABLE) ? 1 : 0;
|
||||
charger->mA = ((val >> 3) & 0xf) * 100;
|
||||
charger->mV = (val & 0x7) * 50 + 4000;
|
||||
|
||||
da9030_read_adc(charger, &charger->adc);
|
||||
da903x_read(charger->master, DA9030_FAULT_LOG, &charger->fault);
|
||||
charger->chdet = da903x_query_status(charger->master,
|
||||
DA9030_STATUS_CHDET);
|
||||
}
|
||||
|
||||
static void da9030_set_charge(struct da9030_charger *charger, int on)
|
||||
{
|
||||
uint8_t val;
|
||||
|
||||
if (on) {
|
||||
val = DA9030_CHRG_CHARGER_ENABLE;
|
||||
val |= (charger->charge_milliamp / 100) << 3;
|
||||
val |= (charger->charge_millivolt - 4000) / 50;
|
||||
charger->is_on = 1;
|
||||
} else {
|
||||
val = 0;
|
||||
charger->is_on = 0;
|
||||
}
|
||||
|
||||
da903x_write(charger->master, DA9030_CHARGE_CONTROL, val);
|
||||
}
|
||||
|
||||
static void da9030_charger_check_state(struct da9030_charger *charger)
|
||||
{
|
||||
da9030_charger_update_state(charger);
|
||||
|
||||
/* we wake or boot with external power on */
|
||||
if (!charger->is_on) {
|
||||
if ((charger->chdet) &&
|
||||
(charger->adc.vbat_res <
|
||||
charger->thresholds.vbat_charge_start)) {
|
||||
da9030_set_charge(charger, 1);
|
||||
}
|
||||
} else {
|
||||
if (charger->adc.vbat_res >=
|
||||
charger->thresholds.vbat_charge_stop) {
|
||||
da9030_set_charge(charger, 0);
|
||||
da903x_write(charger->master, DA9030_VBATMON,
|
||||
charger->thresholds.vbat_charge_restart);
|
||||
} else if (charger->adc.vbat_res >
|
||||
charger->thresholds.vbat_low) {
|
||||
/* we are charging and passed LOW_THRESH,
|
||||
so upate DA9030 VBAT threshold
|
||||
*/
|
||||
da903x_write(charger->master, DA9030_VBATMON,
|
||||
charger->thresholds.vbat_low);
|
||||
}
|
||||
if (charger->adc.vchmax_res > charger->thresholds.vcharge_max ||
|
||||
charger->adc.vchmin_res < charger->thresholds.vcharge_min ||
|
||||
/* Tempreture readings are negative */
|
||||
charger->adc.tbat_res < charger->thresholds.tbat_high ||
|
||||
charger->adc.tbat_res > charger->thresholds.tbat_low) {
|
||||
/* disable charger */
|
||||
da9030_set_charge(charger, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void da9030_charging_monitor(struct work_struct *work)
|
||||
{
|
||||
struct da9030_charger *charger;
|
||||
|
||||
charger = container_of(work, struct da9030_charger, work.work);
|
||||
|
||||
da9030_charger_check_state(charger);
|
||||
|
||||
/* reschedule for the next time */
|
||||
schedule_delayed_work(&charger->work, charger->interval);
|
||||
}
|
||||
|
||||
static enum power_supply_property da9030_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_AVG,
|
||||
};
|
||||
|
||||
static void da9030_battery_check_status(struct da9030_charger *charger,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
if (charger->chdet) {
|
||||
if (charger->is_on)
|
||||
val->intval = POWER_SUPPLY_STATUS_CHARGING;
|
||||
else
|
||||
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
} else {
|
||||
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
}
|
||||
}
|
||||
|
||||
static void da9030_battery_check_health(struct da9030_charger *charger,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
if (charger->fault & DA9030_FAULT_LOG_OVER_TEMP)
|
||||
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
|
||||
else if (charger->fault & DA9030_FAULT_LOG_VBAT_OVER)
|
||||
val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
else
|
||||
val->intval = POWER_SUPPLY_HEALTH_GOOD;
|
||||
}
|
||||
|
||||
static int da9030_battery_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct da9030_charger *charger;
|
||||
charger = container_of(psy, struct da9030_charger, psy);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
da9030_battery_check_status(charger, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
da9030_battery_check_health(charger, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||
val->intval = charger->battery_info->technology;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
|
||||
val->intval = charger->battery_info->voltage_max_design;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
||||
val->intval = charger->battery_info->voltage_min_design;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
val->intval = da9030_reg_to_mV(charger->adc.vbat_res) * 1000;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CURRENT_AVG:
|
||||
val->intval =
|
||||
da9030_reg_to_mA(charger->adc.ichaverage_res) * 1000;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = charger->battery_info->name;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void da9030_battery_vbat_event(struct da9030_charger *charger)
|
||||
{
|
||||
da9030_read_adc(charger, &charger->adc);
|
||||
|
||||
if (charger->is_on)
|
||||
return;
|
||||
|
||||
if (charger->adc.vbat_res < charger->thresholds.vbat_low) {
|
||||
/* set VBAT threshold for critical */
|
||||
da903x_write(charger->master, DA9030_VBATMON,
|
||||
charger->thresholds.vbat_crit);
|
||||
if (charger->battery_low)
|
||||
charger->battery_low();
|
||||
} else if (charger->adc.vbat_res <
|
||||
charger->thresholds.vbat_crit) {
|
||||
/* notify the system of battery critical */
|
||||
if (charger->battery_critical)
|
||||
charger->battery_critical();
|
||||
}
|
||||
}
|
||||
|
||||
static int da9030_battery_event(struct notifier_block *nb, unsigned long event,
|
||||
void *data)
|
||||
{
|
||||
struct da9030_charger *charger =
|
||||
container_of(nb, struct da9030_charger, nb);
|
||||
int status;
|
||||
|
||||
switch (event) {
|
||||
case DA9030_EVENT_CHDET:
|
||||
status = da903x_query_status(charger->master,
|
||||
DA9030_STATUS_CHDET);
|
||||
da9030_set_charge(charger, status);
|
||||
break;
|
||||
case DA9030_EVENT_VBATMON:
|
||||
da9030_battery_vbat_event(charger);
|
||||
break;
|
||||
case DA9030_EVENT_CHIOVER:
|
||||
case DA9030_EVENT_TBAT:
|
||||
da9030_set_charge(charger, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void da9030_battery_convert_thresholds(struct da9030_charger *charger,
|
||||
struct da9030_battery_info *pdata)
|
||||
{
|
||||
charger->thresholds.tbat_low = pdata->tbat_low;
|
||||
charger->thresholds.tbat_high = pdata->tbat_high;
|
||||
charger->thresholds.tbat_restart = pdata->tbat_restart;
|
||||
|
||||
charger->thresholds.vbat_low =
|
||||
da9030_millivolt_to_reg(pdata->vbat_low);
|
||||
charger->thresholds.vbat_crit =
|
||||
da9030_millivolt_to_reg(pdata->vbat_crit);
|
||||
charger->thresholds.vbat_charge_start =
|
||||
da9030_millivolt_to_reg(pdata->vbat_charge_start);
|
||||
charger->thresholds.vbat_charge_stop =
|
||||
da9030_millivolt_to_reg(pdata->vbat_charge_stop);
|
||||
charger->thresholds.vbat_charge_restart =
|
||||
da9030_millivolt_to_reg(pdata->vbat_charge_restart);
|
||||
|
||||
charger->thresholds.vcharge_min =
|
||||
da9030_millivolt_to_reg(pdata->vcharge_min);
|
||||
charger->thresholds.vcharge_max =
|
||||
da9030_millivolt_to_reg(pdata->vcharge_max);
|
||||
}
|
||||
|
||||
static void da9030_battery_setup_psy(struct da9030_charger *charger)
|
||||
{
|
||||
struct power_supply *psy = &charger->psy;
|
||||
struct power_supply_info *info = charger->battery_info;
|
||||
|
||||
psy->name = info->name;
|
||||
psy->use_for_apm = info->use_for_apm;
|
||||
psy->type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
psy->get_property = da9030_battery_get_property;
|
||||
|
||||
psy->properties = da9030_battery_props;
|
||||
psy->num_properties = ARRAY_SIZE(da9030_battery_props);
|
||||
};
|
||||
|
||||
static int da9030_battery_charger_init(struct da9030_charger *charger)
|
||||
{
|
||||
char v[5];
|
||||
int ret;
|
||||
|
||||
v[0] = v[1] = charger->thresholds.vbat_low;
|
||||
v[2] = charger->thresholds.tbat_high;
|
||||
v[3] = charger->thresholds.tbat_restart;
|
||||
v[4] = charger->thresholds.tbat_low;
|
||||
|
||||
ret = da903x_writes(charger->master, DA9030_VBATMON, 5, v);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Enable reference voltage supply for ADC from the LDO_INTERNAL
|
||||
* regulator. Must be set before ADC measurements can be made.
|
||||
*/
|
||||
ret = da903x_write(charger->master, DA9030_ADC_MAN_CONTROL,
|
||||
DA9030_ADC_LDO_INT_ENABLE |
|
||||
DA9030_ADC_TBATREF_ENABLE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* enable auto ADC measuremnts */
|
||||
return da903x_write(charger->master, DA9030_ADC_AUTO_CONTROL,
|
||||
DA9030_ADC_TBAT_ENABLE | DA9030_ADC_VBAT_IN_TXON |
|
||||
DA9030_ADC_VCH_ENABLE | DA9030_ADC_ICH_ENABLE |
|
||||
DA9030_ADC_VBAT_ENABLE |
|
||||
DA9030_ADC_AUTO_SLEEP_ENABLE);
|
||||
}
|
||||
|
||||
static int da9030_battery_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct da9030_charger *charger;
|
||||
struct da9030_battery_info *pdata = pdev->dev.platform_data;
|
||||
int ret;
|
||||
|
||||
if (pdata == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (pdata->charge_milliamp >= 1500 ||
|
||||
pdata->charge_millivolt < 4000 ||
|
||||
pdata->charge_millivolt > 4350)
|
||||
return -EINVAL;
|
||||
|
||||
charger = kzalloc(sizeof(*charger), GFP_KERNEL);
|
||||
if (charger == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
charger->master = pdev->dev.parent;
|
||||
|
||||
/* 10 seconds between monotor runs unless platfrom defines other
|
||||
interval */
|
||||
charger->interval = msecs_to_jiffies(
|
||||
(pdata->batmon_interval ? : 10) * 1000);
|
||||
|
||||
charger->charge_milliamp = pdata->charge_milliamp;
|
||||
charger->charge_millivolt = pdata->charge_millivolt;
|
||||
charger->battery_info = pdata->battery_info;
|
||||
charger->battery_low = pdata->battery_low;
|
||||
charger->battery_critical = pdata->battery_critical;
|
||||
|
||||
da9030_battery_convert_thresholds(charger, pdata);
|
||||
|
||||
ret = da9030_battery_charger_init(charger);
|
||||
if (ret)
|
||||
goto err_charger_init;
|
||||
|
||||
INIT_DELAYED_WORK(&charger->work, da9030_charging_monitor);
|
||||
schedule_delayed_work(&charger->work, charger->interval);
|
||||
|
||||
charger->nb.notifier_call = da9030_battery_event;
|
||||
ret = da903x_register_notifier(charger->master, &charger->nb,
|
||||
DA9030_EVENT_CHDET |
|
||||
DA9030_EVENT_VBATMON |
|
||||
DA9030_EVENT_CHIOVER |
|
||||
DA9030_EVENT_TBAT);
|
||||
if (ret)
|
||||
goto err_notifier;
|
||||
|
||||
da9030_battery_setup_psy(charger);
|
||||
ret = power_supply_register(&pdev->dev, &charger->psy);
|
||||
if (ret)
|
||||
goto err_ps_register;
|
||||
|
||||
charger->debug_file = da9030_bat_create_debugfs(charger);
|
||||
platform_set_drvdata(pdev, charger);
|
||||
return 0;
|
||||
|
||||
err_ps_register:
|
||||
da903x_unregister_notifier(charger->master, &charger->nb,
|
||||
DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |
|
||||
DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);
|
||||
err_notifier:
|
||||
cancel_delayed_work(&charger->work);
|
||||
|
||||
err_charger_init:
|
||||
kfree(charger);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int da9030_battery_remove(struct platform_device *dev)
|
||||
{
|
||||
struct da9030_charger *charger = platform_get_drvdata(dev);
|
||||
|
||||
da9030_bat_remove_debugfs(charger);
|
||||
|
||||
da903x_unregister_notifier(charger->master, &charger->nb,
|
||||
DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |
|
||||
DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);
|
||||
cancel_delayed_work(&charger->work);
|
||||
power_supply_unregister(&charger->psy);
|
||||
|
||||
kfree(charger);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver da903x_battery_driver = {
|
||||
.driver = {
|
||||
.name = "da903x-battery",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = da9030_battery_probe,
|
||||
.remove = da9030_battery_remove,
|
||||
};
|
||||
|
||||
static int da903x_battery_init(void)
|
||||
{
|
||||
return platform_driver_register(&da903x_battery_driver);
|
||||
}
|
||||
|
||||
static void da903x_battery_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&da903x_battery_driver);
|
||||
}
|
||||
|
||||
module_init(da903x_battery_init);
|
||||
module_exit(da903x_battery_exit);
|
||||
|
||||
MODULE_DESCRIPTION("DA9030 battery charger driver");
|
||||
MODULE_AUTHOR("Mike Rapoport, CompuLab");
|
||||
MODULE_LICENSE("GPL");
|
@ -45,7 +45,7 @@ static ssize_t power_supply_show_property(struct device *dev,
|
||||
};
|
||||
static char *health_text[] = {
|
||||
"Unknown", "Good", "Overheat", "Dead", "Over voltage",
|
||||
"Unspecified failure"
|
||||
"Unspecified failure", "Cold",
|
||||
};
|
||||
static char *technology_text[] = {
|
||||
"Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd",
|
||||
|
532
drivers/power/wm8350_power.c
Normal file
532
drivers/power/wm8350_power.c
Normal file
@ -0,0 +1,532 @@
|
||||
/*
|
||||
* Battery driver for wm8350 PMIC
|
||||
*
|
||||
* Copyright 2007, 2008 Wolfson Microelectronics PLC.
|
||||
*
|
||||
* Based on OLPC Battery Driver
|
||||
*
|
||||
* Copyright 2006 David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/mfd/wm8350/supply.h>
|
||||
#include <linux/mfd/wm8350/core.h>
|
||||
#include <linux/mfd/wm8350/comparator.h>
|
||||
|
||||
static int wm8350_read_battery_uvolts(struct wm8350 *wm8350)
|
||||
{
|
||||
return wm8350_read_auxadc(wm8350, WM8350_AUXADC_BATT, 0, 0)
|
||||
* WM8350_AUX_COEFF;
|
||||
}
|
||||
|
||||
static int wm8350_read_line_uvolts(struct wm8350 *wm8350)
|
||||
{
|
||||
return wm8350_read_auxadc(wm8350, WM8350_AUXADC_LINE, 0, 0)
|
||||
* WM8350_AUX_COEFF;
|
||||
}
|
||||
|
||||
static int wm8350_read_usb_uvolts(struct wm8350 *wm8350)
|
||||
{
|
||||
return wm8350_read_auxadc(wm8350, WM8350_AUXADC_USB, 0, 0)
|
||||
* WM8350_AUX_COEFF;
|
||||
}
|
||||
|
||||
#define WM8350_BATT_SUPPLY 1
|
||||
#define WM8350_USB_SUPPLY 2
|
||||
#define WM8350_LINE_SUPPLY 4
|
||||
|
||||
static inline int wm8350_charge_time_min(struct wm8350 *wm8350, int min)
|
||||
{
|
||||
if (!wm8350->power.rev_g_coeff)
|
||||
return (((min - 30) / 15) & 0xf) << 8;
|
||||
else
|
||||
return (((min - 30) / 30) & 0xf) << 8;
|
||||
}
|
||||
|
||||
static int wm8350_get_supplies(struct wm8350 *wm8350)
|
||||
{
|
||||
u16 sm, ov, co, chrg;
|
||||
int supplies = 0;
|
||||
|
||||
sm = wm8350_reg_read(wm8350, WM8350_STATE_MACHINE_STATUS);
|
||||
ov = wm8350_reg_read(wm8350, WM8350_MISC_OVERRIDES);
|
||||
co = wm8350_reg_read(wm8350, WM8350_COMPARATOR_OVERRIDES);
|
||||
chrg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2);
|
||||
|
||||
/* USB_SM */
|
||||
sm = (sm & WM8350_USB_SM_MASK) >> WM8350_USB_SM_SHIFT;
|
||||
|
||||
/* CHG_ISEL */
|
||||
chrg &= WM8350_CHG_ISEL_MASK;
|
||||
|
||||
/* If the USB state machine is active then we're using that with or
|
||||
* without battery, otherwise check for wall supply */
|
||||
if (((sm == WM8350_USB_SM_100_SLV) ||
|
||||
(sm == WM8350_USB_SM_500_SLV) ||
|
||||
(sm == WM8350_USB_SM_STDBY_SLV))
|
||||
&& !(ov & WM8350_USB_LIMIT_OVRDE))
|
||||
supplies = WM8350_USB_SUPPLY;
|
||||
else if (((sm == WM8350_USB_SM_100_SLV) ||
|
||||
(sm == WM8350_USB_SM_500_SLV) ||
|
||||
(sm == WM8350_USB_SM_STDBY_SLV))
|
||||
&& (ov & WM8350_USB_LIMIT_OVRDE) && (chrg == 0))
|
||||
supplies = WM8350_USB_SUPPLY | WM8350_BATT_SUPPLY;
|
||||
else if (co & WM8350_WALL_FB_OVRDE)
|
||||
supplies = WM8350_LINE_SUPPLY;
|
||||
else
|
||||
supplies = WM8350_BATT_SUPPLY;
|
||||
|
||||
return supplies;
|
||||
}
|
||||
|
||||
static int wm8350_charger_config(struct wm8350 *wm8350,
|
||||
struct wm8350_charger_policy *policy)
|
||||
{
|
||||
u16 reg, eoc_mA, fast_limit_mA;
|
||||
|
||||
if (!policy) {
|
||||
dev_warn(wm8350->dev,
|
||||
"No charger policy, charger not configured.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* make sure USB fast charge current is not > 500mA */
|
||||
if (policy->fast_limit_USB_mA > 500) {
|
||||
dev_err(wm8350->dev, "USB fast charge > 500mA\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
eoc_mA = WM8350_CHG_EOC_mA(policy->eoc_mA);
|
||||
|
||||
wm8350_reg_unlock(wm8350);
|
||||
|
||||
reg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1)
|
||||
& WM8350_CHG_ENA_R168;
|
||||
wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1,
|
||||
reg | eoc_mA | policy->trickle_start_mV |
|
||||
WM8350_CHG_TRICKLE_TEMP_CHOKE |
|
||||
WM8350_CHG_TRICKLE_USB_CHOKE |
|
||||
WM8350_CHG_FAST_USB_THROTTLE);
|
||||
|
||||
if (wm8350_get_supplies(wm8350) & WM8350_USB_SUPPLY) {
|
||||
fast_limit_mA =
|
||||
WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_USB_mA);
|
||||
wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2,
|
||||
policy->charge_mV | policy->trickle_charge_USB_mA |
|
||||
fast_limit_mA | wm8350_charge_time_min(wm8350,
|
||||
policy->charge_timeout));
|
||||
|
||||
} else {
|
||||
fast_limit_mA =
|
||||
WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_mA);
|
||||
wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2,
|
||||
policy->charge_mV | policy->trickle_charge_mA |
|
||||
fast_limit_mA | wm8350_charge_time_min(wm8350,
|
||||
policy->charge_timeout));
|
||||
}
|
||||
|
||||
wm8350_reg_lock(wm8350);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8350_batt_status(struct wm8350 *wm8350)
|
||||
{
|
||||
u16 state;
|
||||
|
||||
state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2);
|
||||
state &= WM8350_CHG_STS_MASK;
|
||||
|
||||
switch (state) {
|
||||
case WM8350_CHG_STS_OFF:
|
||||
return POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
|
||||
case WM8350_CHG_STS_TRICKLE:
|
||||
case WM8350_CHG_STS_FAST:
|
||||
return POWER_SUPPLY_STATUS_CHARGING;
|
||||
|
||||
default:
|
||||
return POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t charger_state_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct wm8350 *wm8350 = dev_get_drvdata(dev);
|
||||
char *charge;
|
||||
int state;
|
||||
|
||||
state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2) &
|
||||
WM8350_CHG_STS_MASK;
|
||||
switch (state) {
|
||||
case WM8350_CHG_STS_OFF:
|
||||
charge = "Charger Off";
|
||||
break;
|
||||
case WM8350_CHG_STS_TRICKLE:
|
||||
charge = "Trickle Charging";
|
||||
break;
|
||||
case WM8350_CHG_STS_FAST:
|
||||
charge = "Fast Charging";
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%s\n", charge);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(charger_state, 0444, charger_state_show, NULL);
|
||||
|
||||
static void wm8350_charger_handler(struct wm8350 *wm8350, int irq, void *data)
|
||||
{
|
||||
struct wm8350_power *power = &wm8350->power;
|
||||
struct wm8350_charger_policy *policy = power->policy;
|
||||
|
||||
switch (irq) {
|
||||
case WM8350_IRQ_CHG_BAT_FAIL:
|
||||
dev_err(wm8350->dev, "battery failed\n");
|
||||
break;
|
||||
case WM8350_IRQ_CHG_TO:
|
||||
dev_err(wm8350->dev, "charger timeout\n");
|
||||
power_supply_changed(&power->battery);
|
||||
break;
|
||||
|
||||
case WM8350_IRQ_CHG_BAT_HOT:
|
||||
case WM8350_IRQ_CHG_BAT_COLD:
|
||||
case WM8350_IRQ_CHG_START:
|
||||
case WM8350_IRQ_CHG_END:
|
||||
power_supply_changed(&power->battery);
|
||||
break;
|
||||
|
||||
case WM8350_IRQ_CHG_FAST_RDY:
|
||||
dev_dbg(wm8350->dev, "fast charger ready\n");
|
||||
wm8350_charger_config(wm8350, policy);
|
||||
wm8350_reg_unlock(wm8350);
|
||||
wm8350_set_bits(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1,
|
||||
WM8350_CHG_FAST);
|
||||
wm8350_reg_lock(wm8350);
|
||||
break;
|
||||
|
||||
case WM8350_IRQ_CHG_VBATT_LT_3P9:
|
||||
dev_warn(wm8350->dev, "battery < 3.9V\n");
|
||||
break;
|
||||
case WM8350_IRQ_CHG_VBATT_LT_3P1:
|
||||
dev_warn(wm8350->dev, "battery < 3.1V\n");
|
||||
break;
|
||||
case WM8350_IRQ_CHG_VBATT_LT_2P85:
|
||||
dev_warn(wm8350->dev, "battery < 2.85V\n");
|
||||
break;
|
||||
|
||||
/* Supply change. We will overnotify but it should do
|
||||
* no harm. */
|
||||
case WM8350_IRQ_EXT_USB_FB:
|
||||
case WM8350_IRQ_EXT_WALL_FB:
|
||||
wm8350_charger_config(wm8350, policy);
|
||||
case WM8350_IRQ_EXT_BAT_FB: /* Fall through */
|
||||
power_supply_changed(&power->battery);
|
||||
power_supply_changed(&power->usb);
|
||||
power_supply_changed(&power->ac);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(wm8350->dev, "Unknown interrupt %d\n", irq);
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* AC Power
|
||||
*********************************************************************/
|
||||
static int wm8350_ac_get_prop(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct wm8350 *wm8350 = dev_get_drvdata(psy->dev->parent);
|
||||
int ret = 0;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
val->intval = !!(wm8350_get_supplies(wm8350) &
|
||||
WM8350_LINE_SUPPLY);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
val->intval = wm8350_read_line_uvolts(wm8350);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum power_supply_property wm8350_ac_props[] = {
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
};
|
||||
|
||||
/*********************************************************************
|
||||
* USB Power
|
||||
*********************************************************************/
|
||||
static int wm8350_usb_get_prop(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct wm8350 *wm8350 = dev_get_drvdata(psy->dev->parent);
|
||||
int ret = 0;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
val->intval = !!(wm8350_get_supplies(wm8350) &
|
||||
WM8350_USB_SUPPLY);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
val->intval = wm8350_read_usb_uvolts(wm8350);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum power_supply_property wm8350_usb_props[] = {
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
};
|
||||
|
||||
/*********************************************************************
|
||||
* Battery properties
|
||||
*********************************************************************/
|
||||
|
||||
static int wm8350_bat_check_health(struct wm8350 *wm8350)
|
||||
{
|
||||
u16 reg;
|
||||
|
||||
if (wm8350_read_battery_uvolts(wm8350) < 2850000)
|
||||
return POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
|
||||
|
||||
reg = wm8350_reg_read(wm8350, WM8350_CHARGER_OVERRIDES);
|
||||
if (reg & WM8350_CHG_BATT_HOT_OVRDE)
|
||||
return POWER_SUPPLY_HEALTH_OVERHEAT;
|
||||
|
||||
if (reg & WM8350_CHG_BATT_COLD_OVRDE)
|
||||
return POWER_SUPPLY_HEALTH_COLD;
|
||||
|
||||
return POWER_SUPPLY_HEALTH_GOOD;
|
||||
}
|
||||
|
||||
static int wm8350_bat_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct wm8350 *wm8350 = dev_get_drvdata(psy->dev->parent);
|
||||
int ret = 0;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
val->intval = wm8350_batt_status(wm8350);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
val->intval = !!(wm8350_get_supplies(wm8350) &
|
||||
WM8350_BATT_SUPPLY);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
val->intval = wm8350_read_battery_uvolts(wm8350);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
val->intval = wm8350_bat_check_health(wm8350);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum power_supply_property wm8350_bat_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
};
|
||||
|
||||
/*********************************************************************
|
||||
* Initialisation
|
||||
*********************************************************************/
|
||||
|
||||
static void wm8350_init_charger(struct wm8350 *wm8350)
|
||||
{
|
||||
/* register our interest in charger events */
|
||||
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT,
|
||||
wm8350_charger_handler, NULL);
|
||||
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT);
|
||||
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD,
|
||||
wm8350_charger_handler, NULL);
|
||||
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD);
|
||||
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL,
|
||||
wm8350_charger_handler, NULL);
|
||||
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
|
||||
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_TO,
|
||||
wm8350_charger_handler, NULL);
|
||||
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_TO);
|
||||
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_END,
|
||||
wm8350_charger_handler, NULL);
|
||||
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_END);
|
||||
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_START,
|
||||
wm8350_charger_handler, NULL);
|
||||
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_START);
|
||||
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY,
|
||||
wm8350_charger_handler, NULL);
|
||||
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY);
|
||||
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9,
|
||||
wm8350_charger_handler, NULL);
|
||||
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
|
||||
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1,
|
||||
wm8350_charger_handler, NULL);
|
||||
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
|
||||
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85,
|
||||
wm8350_charger_handler, NULL);
|
||||
wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
|
||||
|
||||
/* and supply change events */
|
||||
wm8350_register_irq(wm8350, WM8350_IRQ_EXT_USB_FB,
|
||||
wm8350_charger_handler, NULL);
|
||||
wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_USB_FB);
|
||||
wm8350_register_irq(wm8350, WM8350_IRQ_EXT_WALL_FB,
|
||||
wm8350_charger_handler, NULL);
|
||||
wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_WALL_FB);
|
||||
wm8350_register_irq(wm8350, WM8350_IRQ_EXT_BAT_FB,
|
||||
wm8350_charger_handler, NULL);
|
||||
wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_BAT_FB);
|
||||
}
|
||||
|
||||
static void free_charger_irq(struct wm8350 *wm8350)
|
||||
{
|
||||
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT);
|
||||
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT);
|
||||
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD);
|
||||
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD);
|
||||
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
|
||||
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
|
||||
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_TO);
|
||||
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO);
|
||||
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_END);
|
||||
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END);
|
||||
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_START);
|
||||
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START);
|
||||
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
|
||||
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
|
||||
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
|
||||
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
|
||||
wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
|
||||
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
|
||||
wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_USB_FB);
|
||||
wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB);
|
||||
wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_WALL_FB);
|
||||
wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB);
|
||||
wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_BAT_FB);
|
||||
wm8350_free_irq(wm8350, WM8350_IRQ_EXT_BAT_FB);
|
||||
}
|
||||
|
||||
static __devinit int wm8350_power_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct wm8350 *wm8350 = platform_get_drvdata(pdev);
|
||||
struct wm8350_power *power = &wm8350->power;
|
||||
struct wm8350_charger_policy *policy = power->policy;
|
||||
struct power_supply *usb = &power->usb;
|
||||
struct power_supply *battery = &power->battery;
|
||||
struct power_supply *ac = &power->ac;
|
||||
int ret;
|
||||
|
||||
ac->name = "wm8350-ac";
|
||||
ac->type = POWER_SUPPLY_TYPE_MAINS;
|
||||
ac->properties = wm8350_ac_props;
|
||||
ac->num_properties = ARRAY_SIZE(wm8350_ac_props);
|
||||
ac->get_property = wm8350_ac_get_prop;
|
||||
ret = power_supply_register(&pdev->dev, ac);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
battery->name = "wm8350-battery";
|
||||
battery->properties = wm8350_bat_props;
|
||||
battery->num_properties = ARRAY_SIZE(wm8350_bat_props);
|
||||
battery->get_property = wm8350_bat_get_property;
|
||||
battery->use_for_apm = 1;
|
||||
ret = power_supply_register(&pdev->dev, battery);
|
||||
if (ret)
|
||||
goto battery_failed;
|
||||
|
||||
usb->name = "wm8350-usb",
|
||||
usb->type = POWER_SUPPLY_TYPE_USB;
|
||||
usb->properties = wm8350_usb_props;
|
||||
usb->num_properties = ARRAY_SIZE(wm8350_usb_props);
|
||||
usb->get_property = wm8350_usb_get_prop;
|
||||
ret = power_supply_register(&pdev->dev, usb);
|
||||
if (ret)
|
||||
goto usb_failed;
|
||||
|
||||
ret = device_create_file(&pdev->dev, &dev_attr_charger_state);
|
||||
if (ret < 0)
|
||||
dev_warn(wm8350->dev, "failed to add charge sysfs: %d\n", ret);
|
||||
ret = 0;
|
||||
|
||||
wm8350_init_charger(wm8350);
|
||||
if (wm8350_charger_config(wm8350, policy) == 0) {
|
||||
wm8350_reg_unlock(wm8350);
|
||||
wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CHG_ENA);
|
||||
wm8350_reg_lock(wm8350);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
usb_failed:
|
||||
power_supply_unregister(battery);
|
||||
battery_failed:
|
||||
power_supply_unregister(ac);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __devexit int wm8350_power_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct wm8350 *wm8350 = platform_get_drvdata(pdev);
|
||||
struct wm8350_power *power = &wm8350->power;
|
||||
|
||||
free_charger_irq(wm8350);
|
||||
device_remove_file(&pdev->dev, &dev_attr_charger_state);
|
||||
power_supply_unregister(&power->battery);
|
||||
power_supply_unregister(&power->ac);
|
||||
power_supply_unregister(&power->usb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver wm8350_power_driver = {
|
||||
.probe = wm8350_power_probe,
|
||||
.remove = __devexit_p(wm8350_power_remove),
|
||||
.driver = {
|
||||
.name = "wm8350-power",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init wm8350_power_init(void)
|
||||
{
|
||||
return platform_driver_register(&wm8350_power_driver);
|
||||
}
|
||||
module_init(wm8350_power_init);
|
||||
|
||||
static void __exit wm8350_power_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&wm8350_power_driver);
|
||||
}
|
||||
module_exit(wm8350_power_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Power supply driver for WM8350");
|
||||
MODULE_ALIAS("platform:wm8350-power");
|
@ -1380,6 +1380,13 @@ int wm8350_register_regulator(struct wm8350 *wm8350, int reg,
|
||||
if (wm8350->pmic.pdev[reg])
|
||||
return -EBUSY;
|
||||
|
||||
if (reg >= WM8350_DCDC_1 && reg <= WM8350_DCDC_6 &&
|
||||
reg > wm8350->pmic.max_dcdc)
|
||||
return -ENODEV;
|
||||
if (reg >= WM8350_ISINK_A && reg <= WM8350_ISINK_B &&
|
||||
reg > wm8350->pmic.max_isink)
|
||||
return -ENODEV;
|
||||
|
||||
pdev = platform_device_alloc("wm8350-regulator", reg);
|
||||
if (!pdev)
|
||||
return -ENOMEM;
|
||||
|
79
include/linux/i2c/dm355evm_msp.h
Normal file
79
include/linux/i2c/dm355evm_msp.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* dm355evm_msp.h - support MSP430 microcontroller on DM355EVM board
|
||||
*/
|
||||
#ifndef __LINUX_I2C_DM355EVM_MSP
|
||||
#define __LINUX_I2C_DM355EVM_MSP
|
||||
|
||||
/*
|
||||
* Written against Spectrum's writeup for the A4 firmware revision,
|
||||
* and tweaked to match source and rev D2 schematics by removing CPLD
|
||||
* and NOR flash hooks (which were last appropriate in rev B boards).
|
||||
*
|
||||
* Note that the firmware supports a flavor of write posting ... to be
|
||||
* sure a write completes, issue another read or write.
|
||||
*/
|
||||
|
||||
/* utilities to access "registers" emulated by msp430 firmware */
|
||||
extern int dm355evm_msp_write(u8 value, u8 reg);
|
||||
extern int dm355evm_msp_read(u8 reg);
|
||||
|
||||
|
||||
/* command/control registers */
|
||||
#define DM355EVM_MSP_COMMAND 0x00
|
||||
# define MSP_COMMAND_NULL 0
|
||||
# define MSP_COMMAND_RESET_COLD 1
|
||||
# define MSP_COMMAND_RESET_WARM 2
|
||||
# define MSP_COMMAND_RESET_WARM_I 3
|
||||
# define MSP_COMMAND_POWEROFF 4
|
||||
# define MSP_COMMAND_IR_REINIT 5
|
||||
#define DM355EVM_MSP_STATUS 0x01
|
||||
# define MSP_STATUS_BAD_OFFSET BIT(0)
|
||||
# define MSP_STATUS_BAD_COMMAND BIT(1)
|
||||
# define MSP_STATUS_POWER_ERROR BIT(2)
|
||||
# define MSP_STATUS_RXBUF_OVERRUN BIT(3)
|
||||
#define DM355EVM_MSP_RESET 0x02 /* 0 bits == in reset */
|
||||
# define MSP_RESET_DC5 BIT(0)
|
||||
# define MSP_RESET_TVP5154 BIT(2)
|
||||
# define MSP_RESET_IMAGER BIT(3)
|
||||
# define MSP_RESET_ETHERNET BIT(4)
|
||||
# define MSP_RESET_SYS BIT(5)
|
||||
# define MSP_RESET_AIC33 BIT(7)
|
||||
|
||||
/* GPIO registers ... bit patterns mostly match the source MSP ports */
|
||||
#define DM355EVM_MSP_LED 0x03 /* active low (MSP P4) */
|
||||
#define DM355EVM_MSP_SWITCH1 0x04 /* (MSP P5, masked) */
|
||||
# define MSP_SWITCH1_SW6_1 BIT(0)
|
||||
# define MSP_SWITCH1_SW6_2 BIT(1)
|
||||
# define MSP_SWITCH1_SW6_3 BIT(2)
|
||||
# define MSP_SWITCH1_SW6_4 BIT(3)
|
||||
# define MSP_SWITCH1_J1 BIT(4) /* NTSC/PAL */
|
||||
# define MSP_SWITCH1_MSP_INT BIT(5) /* active low */
|
||||
#define DM355EVM_MSP_SWITCH2 0x05 /* (MSP P6, masked) */
|
||||
# define MSP_SWITCH2_SW10 BIT(3)
|
||||
# define MSP_SWITCH2_SW11 BIT(4)
|
||||
# define MSP_SWITCH2_SW12 BIT(5)
|
||||
# define MSP_SWITCH2_SW13 BIT(6)
|
||||
# define MSP_SWITCH2_SW14 BIT(7)
|
||||
#define DM355EVM_MSP_SDMMC 0x06 /* (MSP P2, masked) */
|
||||
# define MSP_SDMMC_0_WP BIT(1)
|
||||
# define MSP_SDMMC_0_CD BIT(2) /* active low */
|
||||
# define MSP_SDMMC_1_WP BIT(3)
|
||||
# define MSP_SDMMC_1_CD BIT(4) /* active low */
|
||||
#define DM355EVM_MSP_FIRMREV 0x07 /* not a GPIO (out of order) */
|
||||
#define DM355EVM_MSP_VIDEO_IN 0x08 /* (MSP P3, masked) */
|
||||
# define MSP_VIDEO_IMAGER BIT(7) /* low == tvp5146 */
|
||||
|
||||
/* power supply registers are currently omitted */
|
||||
|
||||
/* RTC registers */
|
||||
#define DM355EVM_MSP_RTC_0 0x12 /* LSB */
|
||||
#define DM355EVM_MSP_RTC_1 0x13
|
||||
#define DM355EVM_MSP_RTC_2 0x14
|
||||
#define DM355EVM_MSP_RTC_3 0x15 /* MSB */
|
||||
|
||||
/* input event queue registers; code == ((HIGH << 8) | LOW) */
|
||||
#define DM355EVM_MSP_INPUT_COUNT 0x16 /* decrement by reading LOW */
|
||||
#define DM355EVM_MSP_INPUT_HIGH 0x17
|
||||
#define DM355EVM_MSP_INPUT_LOW 0x18
|
||||
|
||||
#endif /* __LINUX_I2C_DM355EVM_MSP */
|
@ -78,8 +78,8 @@ int twl4030_i2c_read_u8(u8 mod_no, u8 *val, u8 reg);
|
||||
* IMPORTANT: For twl4030_i2c_write(), allocate num_bytes + 1
|
||||
* for the value, and populate your data starting at offset 1.
|
||||
*/
|
||||
int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, u8 num_bytes);
|
||||
int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, u8 num_bytes);
|
||||
int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
|
||||
int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
@ -278,6 +278,18 @@ struct twl4030_platform_data {
|
||||
struct twl4030_keypad_data *keypad;
|
||||
struct twl4030_usb_data *usb;
|
||||
|
||||
/* LDO regulators */
|
||||
struct regulator_init_data *vdac;
|
||||
struct regulator_init_data *vpll1;
|
||||
struct regulator_init_data *vpll2;
|
||||
struct regulator_init_data *vmmc1;
|
||||
struct regulator_init_data *vmmc2;
|
||||
struct regulator_init_data *vsim;
|
||||
struct regulator_init_data *vaux1;
|
||||
struct regulator_init_data *vaux2;
|
||||
struct regulator_init_data *vaux3;
|
||||
struct regulator_init_data *vaux4;
|
||||
|
||||
/* REVISIT more to come ... _nothing_ should be hard-wired */
|
||||
};
|
||||
|
||||
@ -285,33 +297,6 @@ struct twl4030_platform_data {
|
||||
|
||||
int twl4030_sih_setup(int module);
|
||||
|
||||
/*
|
||||
* FIXME completely stop using TWL4030_IRQ_BASE ... instead, pass the
|
||||
* IRQ data to subsidiary devices using platform device resources.
|
||||
*/
|
||||
|
||||
/* IRQ information-need base */
|
||||
#include <mach/irqs.h>
|
||||
/* TWL4030 interrupts */
|
||||
|
||||
/* #define TWL4030_MODIRQ_GPIO (TWL4030_IRQ_BASE + 0) */
|
||||
#define TWL4030_MODIRQ_KEYPAD (TWL4030_IRQ_BASE + 1)
|
||||
#define TWL4030_MODIRQ_BCI (TWL4030_IRQ_BASE + 2)
|
||||
#define TWL4030_MODIRQ_MADC (TWL4030_IRQ_BASE + 3)
|
||||
/* #define TWL4030_MODIRQ_USB (TWL4030_IRQ_BASE + 4) */
|
||||
/* #define TWL4030_MODIRQ_PWR (TWL4030_IRQ_BASE + 5) */
|
||||
|
||||
#define TWL4030_PWRIRQ_PWRBTN (TWL4030_PWR_IRQ_BASE + 0)
|
||||
/* #define TWL4030_PWRIRQ_CHG_PRES (TWL4030_PWR_IRQ_BASE + 1) */
|
||||
/* #define TWL4030_PWRIRQ_USB_PRES (TWL4030_PWR_IRQ_BASE + 2) */
|
||||
/* #define TWL4030_PWRIRQ_RTC (TWL4030_PWR_IRQ_BASE + 3) */
|
||||
/* #define TWL4030_PWRIRQ_HOT_DIE (TWL4030_PWR_IRQ_BASE + 4) */
|
||||
/* #define TWL4030_PWRIRQ_PWROK_TIMEOUT (TWL4030_PWR_IRQ_BASE + 5) */
|
||||
/* #define TWL4030_PWRIRQ_MBCHG (TWL4030_PWR_IRQ_BASE + 6) */
|
||||
/* #define TWL4030_PWRIRQ_SC_DETECT (TWL4030_PWR_IRQ_BASE + 7) */
|
||||
|
||||
/* Rest are unsued currently*/
|
||||
|
||||
/* Offsets to Power Registers */
|
||||
#define TWL4030_VDAC_DEV_GRP 0x3B
|
||||
#define TWL4030_VDAC_DEDICATED 0x3E
|
||||
@ -322,10 +307,6 @@ int twl4030_sih_setup(int module);
|
||||
#define TWL4030_VAUX3_DEV_GRP 0x1F
|
||||
#define TWL4030_VAUX3_DEDICATED 0x22
|
||||
|
||||
/* TWL4030 GPIO interrupt definitions */
|
||||
|
||||
#define TWL4030_GPIO_IRQ_NO(n) (TWL4030_GPIO_IRQ_BASE + (n))
|
||||
|
||||
/*
|
||||
* Exported TWL4030 GPIO APIs
|
||||
*
|
||||
@ -340,4 +321,38 @@ int twl4030_set_gpio_debounce(int gpio, int enable);
|
||||
static inline int twl4030charger_usb_en(int enable) { return 0; }
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/* Linux-specific regulator identifiers ... for now, we only support
|
||||
* the LDOs, and leave the three buck converters alone. VDD1 and VDD2
|
||||
* need to tie into hardware based voltage scaling (cpufreq etc), while
|
||||
* VIO is generally fixed.
|
||||
*/
|
||||
|
||||
/* EXTERNAL dc-to-dc buck converters */
|
||||
#define TWL4030_REG_VDD1 0
|
||||
#define TWL4030_REG_VDD2 1
|
||||
#define TWL4030_REG_VIO 2
|
||||
|
||||
/* EXTERNAL LDOs */
|
||||
#define TWL4030_REG_VDAC 3
|
||||
#define TWL4030_REG_VPLL1 4
|
||||
#define TWL4030_REG_VPLL2 5 /* not on all chips */
|
||||
#define TWL4030_REG_VMMC1 6
|
||||
#define TWL4030_REG_VMMC2 7 /* not on all chips */
|
||||
#define TWL4030_REG_VSIM 8 /* not on all chips */
|
||||
#define TWL4030_REG_VAUX1 9 /* not on all chips */
|
||||
#define TWL4030_REG_VAUX2_4030 10 /* (twl4030-specific) */
|
||||
#define TWL4030_REG_VAUX2 11 /* (twl5030 and newer) */
|
||||
#define TWL4030_REG_VAUX3 12 /* not on all chips */
|
||||
#define TWL4030_REG_VAUX4 13 /* not on all chips */
|
||||
|
||||
/* INTERNAL LDOs */
|
||||
#define TWL4030_REG_VINTANA1 14
|
||||
#define TWL4030_REG_VINTANA2 15
|
||||
#define TWL4030_REG_VINTDIG 16
|
||||
#define TWL4030_REG_VUSB1V5 17
|
||||
#define TWL4030_REG_VUSB1V8 18
|
||||
#define TWL4030_REG_VUSB3V1 19
|
||||
|
||||
#endif /* End of __TWL4030_H */
|
||||
|
@ -32,6 +32,7 @@ enum {
|
||||
DA9030_ID_LDO18,
|
||||
DA9030_ID_LDO19,
|
||||
DA9030_ID_LDO_INT, /* LDO Internal */
|
||||
DA9030_ID_BAT, /* battery charger */
|
||||
|
||||
DA9034_ID_LED_1,
|
||||
DA9034_ID_LED_2,
|
||||
@ -93,6 +94,43 @@ struct da9034_touch_pdata {
|
||||
int y_inverted;
|
||||
};
|
||||
|
||||
/* DA9030 battery charger data */
|
||||
struct power_supply_info;
|
||||
|
||||
struct da9030_battery_info {
|
||||
/* battery parameters */
|
||||
struct power_supply_info *battery_info;
|
||||
|
||||
/* current and voltage to use for battery charging */
|
||||
unsigned int charge_milliamp;
|
||||
unsigned int charge_millivolt;
|
||||
|
||||
/* voltage thresholds (in millivolts) */
|
||||
int vbat_low;
|
||||
int vbat_crit;
|
||||
int vbat_charge_start;
|
||||
int vbat_charge_stop;
|
||||
int vbat_charge_restart;
|
||||
|
||||
/* battery nominal minimal and maximal voltages in millivolts */
|
||||
int vcharge_min;
|
||||
int vcharge_max;
|
||||
|
||||
/* Temperature thresholds. These are DA9030 register values
|
||||
"as is" and should be measured for each battery type */
|
||||
int tbat_low;
|
||||
int tbat_high;
|
||||
int tbat_restart;
|
||||
|
||||
|
||||
/* battery monitor interval (seconds) */
|
||||
unsigned int batmon_interval;
|
||||
|
||||
/* platform callbacks for battery low and critical events */
|
||||
void (*battery_low)(void);
|
||||
void (*battery_critical)(void);
|
||||
};
|
||||
|
||||
struct da903x_subdev_info {
|
||||
int id;
|
||||
const char *name;
|
||||
@ -190,11 +228,13 @@ extern int da903x_unregister_notifier(struct device *dev,
|
||||
extern int da903x_query_status(struct device *dev, unsigned int status);
|
||||
|
||||
|
||||
/* NOTE: the two functions below are not intended for use outside
|
||||
* of the DA9034 sub-device drivers
|
||||
/* NOTE: the functions below are not intended for use outside
|
||||
* of the DA903x sub-device drivers
|
||||
*/
|
||||
extern int da903x_write(struct device *dev, int reg, uint8_t val);
|
||||
extern int da903x_writes(struct device *dev, int reg, int len, uint8_t *val);
|
||||
extern int da903x_read(struct device *dev, int reg, uint8_t *val);
|
||||
extern int da903x_reads(struct device *dev, int reg, int len, uint8_t *val);
|
||||
extern int da903x_update(struct device *dev, int reg, uint8_t val, uint8_t mask);
|
||||
extern int da903x_set_bits(struct device *dev, int reg, uint8_t bit_mask);
|
||||
extern int da903x_clr_bits(struct device *dev, int reg, uint8_t bit_mask);
|
||||
|
@ -164,4 +164,12 @@
|
||||
#define WM8350_AUXADC_BATT 6
|
||||
#define WM8350_AUXADC_TEMP 7
|
||||
|
||||
struct wm8350;
|
||||
|
||||
/*
|
||||
* AUX ADC Readback
|
||||
*/
|
||||
int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale,
|
||||
int vref);
|
||||
|
||||
#endif
|
||||
|
@ -29,6 +29,7 @@
|
||||
*/
|
||||
#define WM8350_RESET_ID 0x00
|
||||
#define WM8350_ID 0x01
|
||||
#define WM8350_REVISION 0x02
|
||||
#define WM8350_SYSTEM_CONTROL_1 0x03
|
||||
#define WM8350_SYSTEM_CONTROL_2 0x04
|
||||
#define WM8350_SYSTEM_HIBERNATE 0x05
|
||||
@ -57,6 +58,10 @@
|
||||
#define WM8350_OVER_CURRENT_INT_STATUS_MASK 0x25
|
||||
#define WM8350_GPIO_INT_STATUS_MASK 0x26
|
||||
#define WM8350_COMPARATOR_INT_STATUS_MASK 0x27
|
||||
#define WM8350_CHARGER_OVERRIDES 0xE2
|
||||
#define WM8350_MISC_OVERRIDES 0xE3
|
||||
#define WM8350_COMPARATOR_OVERRIDES 0xE7
|
||||
#define WM8350_STATE_MACHINE_STATUS 0xE9
|
||||
|
||||
#define WM8350_MAX_REGISTER 0xFF
|
||||
|
||||
@ -76,6 +81,11 @@
|
||||
#define WM8350_CONF_STS_MASK 0x0C00
|
||||
#define WM8350_CUST_ID_MASK 0x00FF
|
||||
|
||||
/*
|
||||
* R2 (0x02) - Revision
|
||||
*/
|
||||
#define WM8350_MASK_REV_MASK 0x00FF
|
||||
|
||||
/*
|
||||
* R3 (0x03) - System Control 1
|
||||
*/
|
||||
@ -523,6 +533,35 @@
|
||||
#define WM8350_DC2_STS 0x0002
|
||||
#define WM8350_DC1_STS 0x0001
|
||||
|
||||
/*
|
||||
* R226 (0xE2) - Charger status
|
||||
*/
|
||||
#define WM8350_CHG_BATT_HOT_OVRDE 0x8000
|
||||
#define WM8350_CHG_BATT_COLD_OVRDE 0x4000
|
||||
|
||||
/*
|
||||
* R227 (0xE3) - Misc Overrides
|
||||
*/
|
||||
#define WM8350_USB_LIMIT_OVRDE 0x0400
|
||||
|
||||
/*
|
||||
* R227 (0xE7) - Comparator Overrides
|
||||
*/
|
||||
#define WM8350_USB_FB_OVRDE 0x8000
|
||||
#define WM8350_WALL_FB_OVRDE 0x4000
|
||||
#define WM8350_BATT_FB_OVRDE 0x2000
|
||||
|
||||
|
||||
/*
|
||||
* R233 (0xE9) - State Machinine Status
|
||||
*/
|
||||
#define WM8350_USB_SM_MASK 0x0700
|
||||
#define WM8350_USB_SM_SHIFT 8
|
||||
|
||||
#define WM8350_USB_SM_100_SLV 1
|
||||
#define WM8350_USB_SM_500_SLV 5
|
||||
#define WM8350_USB_SM_STDBY_SLV 7
|
||||
|
||||
/* WM8350 wake up conditions */
|
||||
#define WM8350_IRQ_WKUP_OFF_STATE 43
|
||||
#define WM8350_IRQ_WKUP_HIB_STATE 44
|
||||
@ -536,6 +575,7 @@
|
||||
#define WM8350_REV_E 0x4
|
||||
#define WM8350_REV_F 0x5
|
||||
#define WM8350_REV_G 0x6
|
||||
#define WM8350_REV_H 0x7
|
||||
|
||||
#define WM8350_NUM_IRQ 63
|
||||
|
||||
@ -549,6 +589,14 @@ extern const u16 wm8350_mode0_defaults[];
|
||||
extern const u16 wm8350_mode1_defaults[];
|
||||
extern const u16 wm8350_mode2_defaults[];
|
||||
extern const u16 wm8350_mode3_defaults[];
|
||||
extern const u16 wm8351_mode0_defaults[];
|
||||
extern const u16 wm8351_mode1_defaults[];
|
||||
extern const u16 wm8351_mode2_defaults[];
|
||||
extern const u16 wm8351_mode3_defaults[];
|
||||
extern const u16 wm8352_mode0_defaults[];
|
||||
extern const u16 wm8352_mode1_defaults[];
|
||||
extern const u16 wm8352_mode2_defaults[];
|
||||
extern const u16 wm8352_mode3_defaults[];
|
||||
|
||||
struct wm8350;
|
||||
|
||||
@ -558,8 +606,6 @@ struct wm8350_irq {
|
||||
};
|
||||
|
||||
struct wm8350 {
|
||||
int rev; /* chip revision */
|
||||
|
||||
struct device *dev;
|
||||
|
||||
/* device IO */
|
||||
@ -572,6 +618,8 @@ struct wm8350 {
|
||||
void *src);
|
||||
u16 *reg_cache;
|
||||
|
||||
struct mutex auxadc_mutex;
|
||||
|
||||
/* Interrupt handling */
|
||||
struct work_struct irq_work;
|
||||
struct mutex irq_mutex; /* IRQ table mutex */
|
||||
|
@ -701,6 +701,10 @@ struct platform_device;
|
||||
struct regulator_init_data;
|
||||
|
||||
struct wm8350_pmic {
|
||||
/* Number of regulators of each type on this device */
|
||||
int max_dcdc;
|
||||
int max_isink;
|
||||
|
||||
/* ISINK to DCDC mapping */
|
||||
int isink_A_dcdc;
|
||||
int isink_B_dcdc;
|
||||
|
@ -13,7 +13,8 @@
|
||||
#ifndef __LINUX_MFD_WM8350_SUPPLY_H_
|
||||
#define __LINUX_MFD_WM8350_SUPPLY_H_
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/power_supply.h>
|
||||
|
||||
/*
|
||||
* Charger registers
|
||||
@ -104,8 +105,30 @@
|
||||
#define WM8350_IRQ_EXT_WALL_FB 37
|
||||
#define WM8350_IRQ_EXT_BAT_FB 38
|
||||
|
||||
/*
|
||||
* Policy to control charger state machine.
|
||||
*/
|
||||
struct wm8350_charger_policy {
|
||||
|
||||
/* charger state machine policy - set in machine driver */
|
||||
int eoc_mA; /* end of charge current (mA) */
|
||||
int charge_mV; /* charge voltage */
|
||||
int fast_limit_mA; /* fast charge current limit */
|
||||
int fast_limit_USB_mA; /* USB fast charge current limit */
|
||||
int charge_timeout; /* charge timeout (mins) */
|
||||
int trickle_start_mV; /* trickle charge starts at mV */
|
||||
int trickle_charge_mA; /* trickle charge current */
|
||||
int trickle_charge_USB_mA; /* USB trickle charge current */
|
||||
};
|
||||
|
||||
struct wm8350_power {
|
||||
struct platform_device *pdev;
|
||||
struct power_supply battery;
|
||||
struct power_supply usb;
|
||||
struct power_supply ac;
|
||||
struct wm8350_charger_policy *policy;
|
||||
|
||||
int rev_g_coeff;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -45,6 +45,7 @@ enum {
|
||||
POWER_SUPPLY_HEALTH_DEAD,
|
||||
POWER_SUPPLY_HEALTH_OVERVOLTAGE,
|
||||
POWER_SUPPLY_HEALTH_UNSPEC_FAILURE,
|
||||
POWER_SUPPLY_HEALTH_COLD,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
Loading…
Reference in New Issue
Block a user