Commit 3b72e441 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-next' of git://git.o-hand.com/linux-mfd

* 'for-next' of git://git.o-hand.com/linux-mfd:
  mfd: further unbork the ucb1400 ac97_bus dependencies
  mfd: ucb1400 needs GPIO
  mfd: ucb1400 sound driver uses/depends on AC97_BUS:
  mfd: Don't use NO_IRQ in WM8350
  mfd: update TMIO drivers to use the clock API
  mfd: twl4030-core irq simplification
  mfd: add base support for Dialog DA9030/DA9034 PMICs
  mfd: TWL4030 core driver
  mfd: support tmiofb cell on tc6393xb
  mfd: add OHCI cell to tc6393xb
  mfd: Fix htc-egpio compile warning
  mfd: do tcb6393xb state restore on resume only if requested
  mfd: provide and use setup hook for tc6393xb
  mfd: update sm501 debugging/low information messages
  mfd: reduce stack usage in mfd-core.c
parents 396b122f 64c12e9b
...@@ -59,8 +59,6 @@ ...@@ -59,8 +59,6 @@
* TC6393XB GPIOs * TC6393XB GPIOs
*/ */
#define TOSA_TC6393XB_GPIO_BASE (NR_BUILTIN_GPIO + 2 * 12) #define TOSA_TC6393XB_GPIO_BASE (NR_BUILTIN_GPIO + 2 * 12)
#define TOSA_TC6393XB_GPIO(i) (TOSA_TC6393XB_GPIO_BASE + (i))
#define TOSA_TC6393XB_GPIO_BIT(gpio) (1 << (gpio - TOSA_TC6393XB_GPIO_BASE))
#define TOSA_GPIO_TG_ON (TOSA_TC6393XB_GPIO_BASE + 0) #define TOSA_GPIO_TG_ON (TOSA_TC6393XB_GPIO_BASE + 0)
#define TOSA_GPIO_L_MUTE (TOSA_TC6393XB_GPIO_BASE + 1) #define TOSA_GPIO_L_MUTE (TOSA_TC6393XB_GPIO_BASE + 1)
......
...@@ -706,16 +706,39 @@ static struct tmio_nand_data tosa_tc6393xb_nand_config = { ...@@ -706,16 +706,39 @@ static struct tmio_nand_data tosa_tc6393xb_nand_config = {
.badblock_pattern = &tosa_tc6393xb_nand_bbt, .badblock_pattern = &tosa_tc6393xb_nand_bbt,
}; };
static struct tc6393xb_platform_data tosa_tc6393xb_setup = { static int tosa_tc6393xb_setup(struct platform_device *dev)
{
int rc;
rc = gpio_request(TOSA_GPIO_CARD_VCC_ON, "CARD_VCC_ON");
if (rc)
goto err_req;
rc = gpio_direction_output(TOSA_GPIO_CARD_VCC_ON, 1);
if (rc)
goto err_dir;
return rc;
err_dir:
gpio_free(TOSA_GPIO_CARD_VCC_ON);
err_req:
return rc;
}
static void tosa_tc6393xb_teardown(struct platform_device *dev)
{
gpio_free(TOSA_GPIO_CARD_VCC_ON);
}
static struct tc6393xb_platform_data tosa_tc6393xb_data = {
.scr_pll2cr = 0x0cc1, .scr_pll2cr = 0x0cc1,
.scr_gper = 0x3300, .scr_gper = 0x3300,
.scr_gpo_dsr =
TOSA_TC6393XB_GPIO_BIT(TOSA_GPIO_CARD_VCC_ON),
.scr_gpo_doecr =
TOSA_TC6393XB_GPIO_BIT(TOSA_GPIO_CARD_VCC_ON),
.irq_base = IRQ_BOARD_START, .irq_base = IRQ_BOARD_START,
.gpio_base = TOSA_TC6393XB_GPIO_BASE, .gpio_base = TOSA_TC6393XB_GPIO_BASE,
.setup = tosa_tc6393xb_setup,
.teardown = tosa_tc6393xb_teardown,
.enable = tosa_tc6393xb_enable, .enable = tosa_tc6393xb_enable,
.disable = tosa_tc6393xb_disable, .disable = tosa_tc6393xb_disable,
...@@ -723,6 +746,8 @@ static struct tc6393xb_platform_data tosa_tc6393xb_setup = { ...@@ -723,6 +746,8 @@ static struct tc6393xb_platform_data tosa_tc6393xb_setup = {
.resume = tosa_tc6393xb_resume, .resume = tosa_tc6393xb_resume,
.nand_data = &tosa_tc6393xb_nand_config, .nand_data = &tosa_tc6393xb_nand_config,
.resume_restore = 1,
}; };
...@@ -730,7 +755,7 @@ static struct platform_device tc6393xb_device = { ...@@ -730,7 +755,7 @@ static struct platform_device tc6393xb_device = {
.name = "tc6393xb", .name = "tc6393xb",
.id = -1, .id = -1,
.dev = { .dev = {
.platform_data = &tosa_tc6393xb_setup, .platform_data = &tosa_tc6393xb_data,
}, },
.num_resources = ARRAY_SIZE(tc6393xb_resources), .num_resources = ARRAY_SIZE(tc6393xb_resources),
.resource = tc6393xb_resources, .resource = tc6393xb_resources,
......
...@@ -219,7 +219,7 @@ config TOUCHSCREEN_ATMEL_TSADCC ...@@ -219,7 +219,7 @@ config TOUCHSCREEN_ATMEL_TSADCC
config TOUCHSCREEN_UCB1400 config TOUCHSCREEN_UCB1400
tristate "Philips UCB1400 touchscreen" tristate "Philips UCB1400 touchscreen"
select AC97_BUS depends on AC97_BUS
depends on UCB1400_CORE depends on UCB1400_CORE
help help
This enables support for the Philips UCB1400 touchscreen interface. This enables support for the Philips UCB1400 touchscreen interface.
......
...@@ -52,6 +52,8 @@ config HTC_PASIC3 ...@@ -52,6 +52,8 @@ config HTC_PASIC3
config UCB1400_CORE config UCB1400_CORE
tristate "Philips UCB1400 Core driver" tristate "Philips UCB1400 Core driver"
depends on AC97_BUS
depends on GPIOLIB
help help
This enables support for the Philips UCB1400 core functions. This enables support for the Philips UCB1400 core functions.
The UCB1400 is an AC97 audio codec. The UCB1400 is an AC97 audio codec.
...@@ -59,6 +61,20 @@ config UCB1400_CORE ...@@ -59,6 +61,20 @@ config UCB1400_CORE
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called ucb1400_core. module will be called ucb1400_core.
config TWL4030_CORE
bool "Texas Instruments TWL4030/TPS659x0 Support"
depends on I2C=y && GENERIC_HARDIRQS && (ARCH_OMAP2 || ARCH_OMAP3)
help
Say yes here if you have TWL4030 family chip on your board.
This core driver provides register access and IRQ handling
facilities, and registers devices for the various functions
so that function-specific drivers can bind to them.
These multi-function chips are found on many OMAP2 and OMAP3
boards, providing power management, RTC, GPIO, keypad, a
high speed USB OTG transceiver, an audio codec (on most
versions) and many other features.
config MFD_TMIO config MFD_TMIO
bool bool
default n default n
......
...@@ -17,6 +17,8 @@ wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o ...@@ -17,6 +17,8 @@ wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o
obj-$(CONFIG_MFD_WM8350) += wm8350.o obj-$(CONFIG_MFD_WM8350) += wm8350.o
obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o
obj-$(CONFIG_MFD_CORE) += mfd-core.o obj-$(CONFIG_MFD_CORE) += mfd-core.o
obj-$(CONFIG_MCP) += mcp-core.o obj-$(CONFIG_MCP) += mcp-core.o
......
/*
* Base driver for Dialog Semiconductor DA9030/DA9034
*
* Copyright (C) 2008 Compulab, Ltd.
* Mike Rapoport <mike@compulab.co.il>
*
* Copyright (C) 2006-2008 Marvell International Ltd.
* Eric Miao <eric.miao@marvell.com>
*
* 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/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/mfd/da903x.h>
#define DA9030_CHIP_ID 0x00
#define DA9030_EVENT_A 0x01
#define DA9030_EVENT_B 0x02
#define DA9030_EVENT_C 0x03
#define DA9030_STATUS 0x04
#define DA9030_IRQ_MASK_A 0x05
#define DA9030_IRQ_MASK_B 0x06
#define DA9030_IRQ_MASK_C 0x07
#define DA9030_SYS_CTRL_A 0x08
#define DA9030_SYS_CTRL_B 0x09
#define DA9030_FAULT_LOG 0x0a
#define DA9034_CHIP_ID 0x00
#define DA9034_EVENT_A 0x01
#define DA9034_EVENT_B 0x02
#define DA9034_EVENT_C 0x03
#define DA9034_EVENT_D 0x04
#define DA9034_STATUS_A 0x05
#define DA9034_STATUS_B 0x06
#define DA9034_IRQ_MASK_A 0x07
#define DA9034_IRQ_MASK_B 0x08
#define DA9034_IRQ_MASK_C 0x09
#define DA9034_IRQ_MASK_D 0x0a
#define DA9034_SYS_CTRL_A 0x0b
#define DA9034_SYS_CTRL_B 0x0c
#define DA9034_FAULT_LOG 0x0d
struct da903x_chip;
struct da903x_chip_ops {
int (*init_chip)(struct da903x_chip *);
int (*unmask_events)(struct da903x_chip *, unsigned int events);
int (*mask_events)(struct da903x_chip *, unsigned int events);
int (*read_events)(struct da903x_chip *, unsigned int *events);
int (*read_status)(struct da903x_chip *, unsigned int *status);
};
struct da903x_chip {
struct i2c_client *client;
struct device *dev;
struct da903x_chip_ops *ops;
int type;
uint32_t events_mask;
struct mutex lock;
struct work_struct irq_work;
struct blocking_notifier_head notifier_list;
};
static inline int __da903x_read(struct i2c_client *client,
int reg, uint8_t *val)
{
int ret;
ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0) {
dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
return ret;
}
*val = (uint8_t)ret;
return 0;
}
static inline int __da903x_reads(struct i2c_client *client, int reg,
int len, uint8_t *val)
{
int ret;
ret = i2c_smbus_read_i2c_block_data(client, reg, len, val);
if (ret < 0) {
dev_err(&client->dev, "failed reading from 0x%02x\n", reg);
return ret;
}
return 0;
}
static inline int __da903x_write(struct i2c_client *client,
int reg, uint8_t val)
{
int ret;
ret = i2c_smbus_write_byte_data(client, reg, val);
if (ret < 0) {
dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
val, reg);
return ret;
}
return 0;
}
static inline int __da903x_writes(struct i2c_client *client, int reg,
int len, uint8_t *val)
{
int ret;
ret = i2c_smbus_write_i2c_block_data(client, reg, len, val);
if (ret < 0) {
dev_err(&client->dev, "failed writings to 0x%02x\n", reg);
return ret;
}
return 0;
}
int da903x_register_notifier(struct device *dev, struct notifier_block *nb,
unsigned int events)
{
struct da903x_chip *chip = dev_get_drvdata(dev);
chip->ops->unmask_events(chip, events);
return blocking_notifier_chain_register(&chip->notifier_list, nb);
}
EXPORT_SYMBOL_GPL(da903x_register_notifier);
int da903x_unregister_notifier(struct device *dev, struct notifier_block *nb,
unsigned int events)
{
struct da903x_chip *chip = dev_get_drvdata(dev);
chip->ops->mask_events(chip, events);
return blocking_notifier_chain_unregister(&chip->notifier_list, nb);
}
EXPORT_SYMBOL_GPL(da903x_unregister_notifier);
int da903x_write(struct device *dev, int reg, uint8_t val)
{
return __da903x_write(to_i2c_client(dev), reg, val);
}
EXPORT_SYMBOL_GPL(da903x_write);
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_set_bits(struct device *dev, int reg, uint8_t bit_mask)
{
struct da903x_chip *chip = dev_get_drvdata(dev);
uint8_t reg_val;
int ret = 0;
mutex_lock(&chip->lock);
ret = __da903x_read(chip->client, reg, &reg_val);
if (ret)
goto out;
if ((reg_val & bit_mask) == 0) {
reg_val |= bit_mask;
ret = __da903x_write(chip->client, reg, reg_val);
}
out:
mutex_unlock(&chip->lock);
return ret;
}
EXPORT_SYMBOL_GPL(da903x_set_bits);
int da903x_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
{
struct da903x_chip *chip = dev_get_drvdata(dev);
uint8_t reg_val;
int ret = 0;
mutex_lock(&chip->lock);
ret = __da903x_read(chip->client, reg, &reg_val);
if (ret)
goto out;
if (reg_val & bit_mask) {
reg_val &= ~bit_mask;
ret = __da903x_write(chip->client, reg, reg_val);
}
out:
mutex_unlock(&chip->lock);
return ret;
}
EXPORT_SYMBOL_GPL(da903x_clr_bits);
int da903x_update(struct device *dev, int reg, uint8_t val, uint8_t mask)
{
struct da903x_chip *chip = dev_get_drvdata(dev);
uint8_t reg_val;
int ret = 0;
mutex_lock(&chip->lock);
ret = __da903x_read(chip->client, reg, &reg_val);
if (ret)
goto out;
if ((reg_val & mask) != val) {
reg_val = (reg_val & ~mask) | val;
ret = __da903x_write(chip->client, reg, reg_val);
}
out:
mutex_unlock(&chip->lock);
return ret;
}
EXPORT_SYMBOL_GPL(da903x_update);
int da903x_query_status(struct device *dev, unsigned int sbits)
{
struct da903x_chip *chip = dev_get_drvdata(dev);
unsigned int status = 0;
chip->ops->read_status(chip, &status);
return ((status & sbits) == sbits);
}
EXPORT_SYMBOL(da903x_query_status);
static int __devinit da9030_init_chip(struct da903x_chip *chip)
{
uint8_t chip_id;
int err;
err = __da903x_read(chip->client, DA9030_CHIP_ID, &chip_id);
if (err)
return err;
err = __da903x_write(chip->client, DA9030_SYS_CTRL_A, 0xE8);
if (err)
return err;
dev_info(chip->dev, "DA9030 (CHIP ID: 0x%02x) detected\n", chip_id);
return 0;
}
static int da9030_unmask_events(struct da903x_chip *chip, unsigned int events)
{
uint8_t v[3];
chip->events_mask &= ~events;
v[0] = (chip->events_mask & 0xff);
v[1] = (chip->events_mask >> 8) & 0xff;
v[2] = (chip->events_mask >> 16) & 0xff;
return __da903x_writes(chip->client, DA9030_IRQ_MASK_A, 3, v);
}
static int da9030_mask_events(struct da903x_chip *chip, unsigned int events)
{
uint8_t v[3];
chip->events_mask &= ~events;
v[0] = (chip->events_mask & 0xff);
v[1] = (chip->events_mask >> 8) & 0xff;
v[2] = (chip->events_mask >> 16) & 0xff;
return __da903x_writes(chip->client, DA9030_IRQ_MASK_A, 3, v);
}
static int da9030_read_events(struct da903x_chip *chip, unsigned int *events)
{
uint8_t v[3] = {0, 0, 0};
int ret;
ret = __da903x_reads(chip->client, DA9030_EVENT_A, 3, v);
if (ret < 0)
return ret;
*events = (v[2] << 16) | (v[1] << 8) | v[0];
return 0;
}
static int da9030_read_status(struct da903x_chip *chip, unsigned int *status)
{
return __da903x_read(chip->client, DA9030_STATUS, (uint8_t *)status);
}
static int da9034_init_chip(struct da903x_chip *chip)
{
uint8_t chip_id;
int err;
err = __da903x_read(chip->client, DA9034_CHIP_ID, &chip_id);
if (err)
return err;
err = __da903x_write(chip->client, DA9034_SYS_CTRL_A, 0xE8);
if (err)
return err;
/* avoid SRAM power off during sleep*/
__da903x_write(chip->client, 0x10, 0x07);
__da903x_write(chip->client, 0x11, 0xff);
__da903x_write(chip->client, 0x12, 0xff);
/* Enable the ONKEY power down functionality */
__da903x_write(chip->client, DA9034_SYS_CTRL_B, 0x20);
__da903x_write(chip->client, DA9034_SYS_CTRL_A, 0x60);
/* workaround to make LEDs work */
__da903x_write(chip->client, 0x90, 0x01);
__da903x_write(chip->client, 0xB0, 0x08);
/* make ADTV1 and SDTV1 effective */
__da903x_write(chip->client, 0x20, 0x00);
dev_info(chip->dev, "DA9034 (CHIP ID: 0x%02x) detected\n", chip_id);
return 0;
}
static int da9034_unmask_events(struct da903x_chip *chip, unsigned int events)
{
uint8_t v[4];
chip->events_mask &= ~events;
v[0] = (chip->events_mask & 0xff);
v[1] = (chip->events_mask >> 8) & 0xff;
v[2] = (chip->events_mask >> 16) & 0xff;
v[3] = (chip->events_mask >> 24) & 0xff;
return __da903x_writes(chip->client, DA9034_IRQ_MASK_A, 4, v);
}
static int da9034_mask_events(struct da903x_chip *chip, unsigned int events)
{
uint8_t v[4];
chip->events_mask |= events;
v[0] = (chip->events_mask & 0xff);
v[1] = (chip->events_mask >> 8) & 0xff;
v[2] = (chip->events_mask >> 16) & 0xff;
v[3] = (chip->events_mask >> 24) & 0xff;
return __da903x_writes(chip->client, DA9034_IRQ_MASK_A, 4, v);
}
static int da9034_read_events(struct da903x_chip *chip, unsigned int *events)
{
uint8_t v[4] = {0, 0, 0, 0};
int ret;
ret = __da903x_reads(chip->client, DA9034_EVENT_A, 4, v);
if (ret < 0)
return ret;
*events = (v[3] << 24) | (v[2] << 16) | (v[1] << 8) | v[0];
return 0;
}
static int da9034_read_status(struct da903x_chip *chip, unsigned int *status)
{
uint8_t v[2] = {0, 0};
int ret = 0;
ret = __da903x_reads(chip->client, DA9034_STATUS_A, 2, v);
if (ret)
return ret;
*status = (v[1] << 8) | v[0];
return 0;
}
static void da903x_irq_work(struct work_struct *work)
{
struct da903x_chip *chip =
container_of(work, struct da903x_chip, irq_work);
unsigned int events = 0;
while (1) {
if (chip->ops->read_events(chip, &events))
break;
events &= ~chip->events_mask;
if (events == 0)
break;
blocking_notifier_call_chain(
&chip->notifier_list, events, NULL);
}
enable_irq(chip->client->irq);
}
static int da903x_irq_handler(int irq, void *data)
{
struct da903x_chip *chip = data;
disable_irq_nosync(irq);
(void)schedule_work(&chip->irq_work);
return IRQ_HANDLED;
}
static struct da903x_chip_ops da903x_ops[] = {
[0] = {
.init_chip = da9030_init_chip,
.unmask_events = da9030_unmask_events,
.mask_events = da9030_mask_events,
.read_events = da9030_read_events,
.read_status = da9030_read_status,
},
[1] = {
.init_chip = da9034_init_chip,
.unmask_events = da9034_unmask_events,
.mask_events = da9034_mask_events,
.read_events = da9034_read_events,
.read_status = da9034_read_status,
}
};
static const struct i2c_device_id da903x_id_table[] = {
{ "da9030", 0 },
{ "da9034", 1 },
{ },
};
MODULE_DEVICE_TABLE(i2c, da903x_id_table);
static int __devexit __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)
{
return device_for_each_child(chip->dev, NULL, __remove_subdev);
}
static int __devinit da903x_add_subdevs(struct da903x_chip *chip,
struct da903x_platform_data *pdata)
{
struct da903x_subdev_info *subdev;
struct platform_device *pdev;
int i, ret = 0;
for (i = 0; i < pdata->num_subdevs; i++) {
subdev = &pdata->subdevs[i];
pdev = platform_device_alloc(subdev->name, subdev->id);
pdev->dev.parent = chip->dev;
pdev->dev.platform_data = subdev->platform_data;
ret = platform_device_add(pdev);
if (ret)
goto failed;
}
return 0;
failed:
da903x_remove_subdevs(chip);
return ret;
}
static int __devinit da903x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct da903x_platform_data *pdata = client->dev.platform_data;
struct da903x_chip *chip;
unsigned int tmp;
int ret;
chip = kzalloc(sizeof(struct da903x_chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
chip->client = client;
chip->dev = &client->dev;
chip->ops = &da903x_ops[id->driver_data];
mutex_init(&chip->lock);
INIT_WORK(&chip->irq_work, da903x_irq_work);
BLOCKING_INIT_NOTIFIER_HEAD(&chip->notifier_list);
i2c_set_clientdata(client, chip);
ret = chip->ops->init_chip(chip);
if (ret)
goto out_free_chip;
/* mask and clear all IRQs */
chip->events_mask = 0xffffffff;
chip->ops->mask_events(chip, chip->events_mask);
chip->ops->read_events(chip, &tmp);
ret = request_irq(client->irq, da903x_irq_handler,
IRQF_DISABLED | IRQF_TRIGGER_FALLING,
"da903x", chip);
if (ret) {
dev_err(&client->dev, "failed to request irq %d\n",
client->irq);
goto out_free_chip;
}
ret = da903x_add_subdevs(chip, pdata);
if (ret)
goto out_free_irq;
return 0;
out_free_irq:
free_irq(client->irq, chip);
out_free_chip:
i2c_set_clientdata(client, NULL);
kfree(chip);
return ret;
}
static int __devexit da903x_remove(struct i2c_client *client)
{
struct da903x_chip *chip = i2c_get_clientdata(client);
da903x_remove_subdevs(chip);
kfree(chip);
return 0;
}
static struct i2c_driver da903x_driver = {
.driver = {
.name = "da903x",
.owner = THIS_MODULE,
},
.probe = da903x_probe,
.remove = __devexit_p(da903x_remove),
.id_table = da903x_id_table,
};
static int __init da903x_init(void)
{
return i2c_add_driver(&da903x_driver);
}
module_init(da903x_init);
static void __exit da903x_exit(void)
{
i2c_del_driver(&da903x_driver);
}
module_exit(da903x_exit);
MODULE_DESCRIPTION("PMIC Driver for Dialog Semiconductor DA9034");
MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"
"Mike Rapoport <mike@compulab.co.il>");
MODULE_LICENSE("GPL");
...@@ -289,7 +289,7 @@ static int __init egpio_probe(struct platform_device *pdev) ...@@ -289,7 +289,7 @@ static int __init egpio_probe(struct platform_device *pdev)
ei->base_addr = ioremap_nocache(res->start, res->end - res->start); ei->base_addr = ioremap_nocache(res->start, res->end - res->start);
if (!ei->base_addr) if (!ei->base_addr)
goto fail; goto fail;
pr_debug("EGPIO phys=%08x virt=%p\n", res->start, ei->base_addr); pr_debug("EGPIO phys=%08x virt=%p\n", (u32)res->start, ei->base_addr);
if ((pdata->bus_width != 16) && (pdata->bus_width != 32)) if ((pdata->bus_width != 16) && (pdata->bus_width != 32))
goto fail; goto fail;
......
...@@ -20,7 +20,7 @@ static int mfd_add_device(struct device *parent, int id, ...@@ -20,7 +20,7 @@ static int mfd_add_device(struct device *parent, int id,
struct resource *mem_base, struct resource *mem_base,
int irq_base) int irq_base)
{ {
struct resource res[cell->num_resources]; struct resource *res;
struct platform_device *pdev; struct platform_device *pdev;
int ret = -ENOMEM; int ret = -ENOMEM;
int r; int r;
...@@ -29,14 +29,17 @@ static int mfd_add_device(struct device *parent, int id, ...@@ -29,14 +29,17 @@ static int mfd_add_device(struct device *parent, int id,
if (!pdev) if (!pdev)
goto fail_alloc; goto fail_alloc;
res = kzalloc(sizeof(*res) * cell->num_resources, GFP_KERNEL);
if (!res)
goto fail_device;
pdev->dev.parent = parent; pdev->dev.parent = parent;
ret = platform_device_add_data(pdev, ret = platform_device_add_data(pdev,
cell->platform_data, cell->data_size); cell->platform_data, cell->data_size);
if (ret) if (ret)
goto fail_device; goto fail_res;
memset(res, 0, sizeof(res));
for (r = 0; r < cell->num_resources; r++) { for (r = 0; r < cell->num_resources; r++) {
res[r].name = cell->resources[r].name; res[r].name = cell->resources[r].name;
res[r].flags = cell->resources[r].flags; res[r].flags = cell->resources[r].flags;
...@@ -64,11 +67,15 @@ static int mfd_add_device(struct device *parent, int id, ...@@ -64,11 +67,15 @@ static int mfd_add_device(struct device *parent, int id,
ret = platform_device_add(pdev); ret = platform_device_add(pdev);
if (ret) if (ret)
goto fail_device; goto fail_res;
kfree(res);
return 0; return 0;
/* platform_device_del(pdev); */ /* platform_device_del(pdev); */
fail_res:
kfree(res);
fail_device: fail_device:
platform_device_put(pdev); platform_device_put(pdev);
fail_alloc: fail_alloc:
......
...@@ -623,7 +623,7 @@ unsigned long sm501_set_clock(struct device *dev, ...@@ -623,7 +623,7 @@ unsigned long sm501_set_clock(struct device *dev,
sm501_sync_regs(sm); sm501_sync_regs(sm);
dev_info(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n",
gate, clock, mode); gate, clock, mode);
sm501_mdelay(sm, 16); sm501_mdelay(sm, 16);
...@@ -742,7 +742,7 @@ static int sm501_register_device(struct sm501_devdata *sm, ...@@ -742,7 +742,7 @@ static int sm501_register_device(struct sm501_devdata *sm,
int ret; int ret;
for (ptr = 0; ptr < pdev->num_resources; ptr++) { for (ptr = 0; ptr < pdev->num_resources; ptr++) {
printk("%s[%d] flags %08lx: %08llx..%08llx\n", printk(KERN_DEBUG "%s[%d] flags %08lx: %08llx..%08llx\n",
pdev->name, ptr, pdev->name, ptr,
pdev->resource[ptr].flags, pdev->resource[ptr].flags,
(unsigned long long)pdev->resource[ptr].start, (unsigned long long)pdev->resource[ptr].start,
......
...@@ -24,8 +24,10 @@ ...@@ -24,8 +24,10 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/clk.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mfd/core.h> #include <linux/mfd/core.h>
#include <linux/mfd/tmio.h> #include <linux/mfd/tmio.h>
...@@ -56,6 +58,8 @@ struct t7l66xb { ...@@ -56,6 +58,8 @@ struct t7l66xb {
spinlock_t lock; spinlock_t lock;
struct resource rscr; struct resource rscr;
struct clk *clk48m;
struct clk *clk32k;
int irq; int irq;
int irq_base; int irq_base;
}; };
...@@ -65,13 +69,11 @@ struct t7l66xb { ...@@ -65,13 +69,11 @@ struct t7l66xb {
static int t7l66xb_mmc_enable(struct platform_device *mmc) static int t7l66xb_mmc_enable(struct platform_device *mmc)
{ {
struct platform_device *dev = to_platform_device(mmc->dev.parent); struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
struct t7l66xb *t7l66xb = platform_get_drvdata(dev); struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
unsigned long flags; unsigned long flags;
u8 dev_ctl; u8 dev_ctl;
if (pdata->enable_clk32k) clk_enable(t7l66xb->clk32k);
pdata->enable_clk32k(dev);
spin_lock_irqsave(&t7l66xb->lock, flags); spin_lock_irqsave(&t7l66xb->lock, flags);
...@@ -87,7 +89,6 @@ static int t7l66xb_mmc_enable(struct platform_device *mmc) ...@@ -87,7 +89,6 @@ static int t7l66xb_mmc_enable(struct platform_device *mmc)
static int t7l66xb_mmc_disable(struct platform_device *mmc) static int t7l66xb_mmc_disable(struct platform_device *mmc)
{ {
struct platform_device *dev = to_platform_device(mmc->dev.parent); struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
struct t7l66xb *t7l66xb = platform_get_drvdata(dev); struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
unsigned long flags; unsigned long flags;
u8 dev_ctl; u8 dev_ctl;
...@@ -100,8 +101,7 @@ static int t7l66xb_mmc_disable(struct platform_device *mmc) ...@@ -100,8 +101,7 @@ static int t7l66xb_mmc_disable(struct platform_device *mmc)
spin_unlock_irqrestore(&t7l66xb->lock, flags); spin_unlock_irqrestore(&t7l66xb->lock, flags);
if (pdata->disable_clk32k) clk_disable(t7l66xb->clk32k);
pdata->disable_clk32k(dev);
return 0; return 0;
} }
...@@ -258,18 +258,22 @@ static void t7l66xb_detach_irq(struct platform_device *dev) ...@@ -258,18 +258,22 @@ static void t7l66xb_detach_irq(struct platform_device *dev)
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int t7l66xb_suspend(struct platform_device *dev, pm_message_t state) static int t7l66xb_suspend(struct platform_device *dev, pm_message_t state)
{ {
struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
struct t7l66xb_platform_data *pdata = dev->dev.platform_data; struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
if (pdata && pdata->suspend) if (pdata && pdata->suspend)
pdata->suspend(dev); pdata->suspend(dev);
clk_disable(t7l66xb->clk48m);
return 0; return 0;
} }
static int t7l66xb_resume(struct platform_device *dev) static int t7l66xb_resume(struct platform_device *dev)
{ {
struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
struct t7l66xb_platform_data *pdata = dev->dev.platform_data; struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
clk_enable(t7l66xb->clk48m);
if (pdata && pdata->resume) if (pdata && pdata->resume)
pdata->resume(dev); pdata->resume(dev);
...@@ -309,6 +313,19 @@ static int t7l66xb_probe(struct platform_device *dev) ...@@ -309,6 +313,19 @@ static int t7l66xb_probe(struct platform_device *dev)
t7l66xb->irq_base = pdata->irq_base; t7l66xb->irq_base = pdata->irq_base;
t7l66xb->clk32k = clk_get(&dev->dev, "CLK_CK32K");
if (IS_ERR(t7l66xb->clk32k)) {
ret = PTR_ERR(t7l66xb->clk32k);
goto err_clk32k_get;
}
t7l66xb->clk48m = clk_get(&dev->dev, "CLK_CK48M");
if (IS_ERR(t7l66xb->clk48m)) {
ret = PTR_ERR(t7l66xb->clk48m);
clk_put(t7l66xb->clk32k);
goto err_clk48m_get;
}
rscr = &t7l66xb->rscr; rscr = &t7l66xb->rscr;
rscr->name = "t7l66xb-core"; rscr->name = "t7l66xb-core";
rscr->start = iomem->start; rscr->start = iomem->start;
...@@ -325,6 +342,8 @@ static int t7l66xb_probe(struct platform_device *dev) ...@@ -325,6 +342,8 @@ static int t7l66xb_probe(struct platform_device *dev)
goto err_ioremap; goto err_ioremap;
} }
clk_enable(t7l66xb->clk48m);
if (pdata && pdata->enable) if (pdata && pdata->enable)
pdata->enable(dev); pdata->enable(dev);
...@@ -359,9 +378,13 @@ static int t7l66xb_probe(struct platform_device *dev) ...@@ -359,9 +378,13 @@ static int t7l66xb_probe(struct platform_device *dev)
iounmap(t7l66xb->scr); iounmap(t7l66xb->scr);
err_ioremap: err_ioremap:
release_resource(&t7l66xb->rscr); release_resource(&t7l66xb->rscr);
err_noirq:
err_request_scr: err_request_scr:
kfree(t7l66xb); kfree(t7l66xb);
clk_put(t7l66xb->clk48m);
err_clk48m_get:
clk_put(t7l66xb->clk32k);
err_clk32k_get:
err_noirq:
return ret; return ret;
} }
...@@ -372,7 +395,8 @@ static int t7l66xb_remove(struct platform_device *dev) ...@@ -372,7 +395,8 @@ static int t7l66xb_remove(struct platform_device *dev)
int ret; int ret;
ret = pdata->disable(dev); ret = pdata->disable(dev);
clk_disable(t7l66xb->clk48m);
clk_put(t7l66xb->clk48m);
t7l66xb_detach_irq(dev); t7l66xb_detach_irq(dev);
iounmap(t7l66xb->scr); iounmap(t7l66xb->scr);
release_resource(&t7l66xb->rscr); release_resource(&t7l66xb->rscr);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/mfd/core.h> #include <linux/mfd/core.h>
#include <linux/mfd/tmio.h> #include <linux/mfd/tmio.h>
...@@ -24,18 +25,22 @@ enum { ...@@ -24,18 +25,22 @@ enum {
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int tc6387xb_suspend(struct platform_device *dev, pm_message_t state) static int tc6387xb_suspend(struct platform_device *dev, pm_message_t state)
{ {
struct tc6387xb_platform_data *pdata = platform_get_drvdata(dev); struct clk *clk32k = platform_get_drvdata(dev);
struct tc6387xb_platform_data *pdata = dev->dev.platform_data;
if (pdata && pdata->suspend) if (pdata && pdata->suspend)
pdata->suspend(dev); pdata->suspend(dev);
clk_disable(clk32k);
return 0; return 0;
} }
static int tc6387xb_resume(struct platform_device *dev) static int tc6387xb_resume(struct platform_device *dev)
{ {
struct tc6387xb_platform_data *pdata = platform_get_drvdata(dev); struct clk *clk32k = platform_get_drvdata(dev);
struct tc6387xb_platform_data *pdata = dev->dev.platform_data;
clk_enable(clk32k);
if (pdata && pdata->resume) if (pdata && pdata->resume)
pdata->resume(dev); pdata->resume(dev);
...@@ -51,10 +56,9 @@ static int tc6387xb_resume(struct platform_device *dev) ...@@ -51,10 +56,9 @@ static int tc6387xb_resume(struct platform_device *dev)
static int tc6387xb_mmc_enable(struct platform_device *mmc) static int tc6387xb_mmc_enable(struct platform_device *mmc)
{ {
struct platform_device *dev = to_platform_device(mmc->dev.parent); struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct tc6387xb_platform_data *tc6387xb = dev->dev.platform_data; struct clk *clk32k = platform_get_drvdata(dev);
if (tc6387xb->enable_clk32k) clk_enable(clk32k);
tc6387xb->enable_clk32k(dev);
return 0; return 0;
} }
...@@ -62,10 +66,9 @@ static int tc6387xb_mmc_enable(struct platform_device *mmc) ...@@ -62,10 +66,9 @@ static int tc6387xb_mmc_enable(struct platform_device *mmc)
static int tc6387xb_mmc_disable(struct platform_device *mmc) static int tc6387xb_mmc_disable(struct platform_device *mmc)
{ {
struct platform_device *dev = to_platform_device(mmc->dev.parent); struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct tc6387xb_platform_data *tc6387xb = dev->dev.platform_data; struct clk *clk32k = platform_get_drvdata(dev);
if (tc6387xb->disable_clk32k) clk_disable(clk32k);
tc6387xb->disable_clk32k(dev);
return 0; return 0;
} }
...@@ -102,14 +105,14 @@ static struct mfd_cell tc6387xb_cells[] = { ...@@ -102,14 +105,14 @@ static struct mfd_cell tc6387xb_cells[] = {
static int tc6387xb_probe(struct platform_device *dev) static int tc6387xb_probe(struct platform_device *dev)
{ {
struct tc6387xb_platform_data *data = platform_get_drvdata(dev); struct tc6387xb_platform_data *pdata = dev->dev.platform_data;
struct resource *iomem; struct resource *iomem;
struct clk *clk32k;
int irq, ret; int irq, ret;
iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!iomem) { if (!iomem) {
ret = -EINVAL; return -EINVAL;
goto err_resource;
} }
ret = platform_get_irq(dev, 0); ret = platform_get_irq(dev, 0);
...@@ -118,8 +121,15 @@ static int tc6387xb_probe(struct platform_device *dev) ...@@ -118,8 +121,15 @@ static int tc6387xb_probe(struct platform_device *dev)
else else
goto err_resource; goto err_resource;
if (data && data->enable) clk32k = clk_get(&dev->dev, "CLK_CK32K");
data->enable(dev); if (IS_ERR(clk32k)) {
ret = PTR_ERR(clk32k);
goto err_resource;
}
platform_set_drvdata(dev, clk32k);
if (pdata && pdata->enable)
pdata->enable(dev);
printk(KERN_INFO "Toshiba tc6387xb initialised\n"); printk(KERN_INFO "Toshiba tc6387xb initialised\n");
...@@ -134,18 +144,19 @@ static int tc6387xb_probe(struct platform_device *dev) ...@@ -134,18 +144,19 @@ static int tc6387xb_probe(struct platform_device *dev)
if (!ret) if (!ret)
return 0; return 0;
clk_put(clk32k);
err_resource: err_resource:
return ret; return ret;
} }
static int tc6387xb_remove(struct platform_device *dev) static int tc6387xb_remove(struct platform_device *dev)
{ {
struct tc6387xb_platform_data *data = platform_get_drvdata(dev); struct clk *clk32k = platform_get_drvdata(dev);
if (data && data->disable)
data->disable(dev);
/* FIXME - free the resources! */ mfd_remove_devices(&dev->dev);
clk_disable(clk32k);
clk_put(clk32k);
platform_set_drvdata(dev, NULL);
return 0; return 0;
} }
......
...@@ -113,6 +113,8 @@ struct tc6393xb { ...@@ -113,6 +113,8 @@ struct tc6393xb {
enum { enum {
TC6393XB_CELL_NAND, TC6393XB_CELL_NAND,
TC6393XB_CELL_MMC, TC6393XB_CELL_MMC,
TC6393XB_CELL_OHCI,
TC6393XB_CELL_FB,
}; };
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
...@@ -170,6 +172,176 @@ static struct resource __devinitdata tc6393xb_mmc_resources[] = { ...@@ -170,6 +172,176 @@ static struct resource __devinitdata tc6393xb_mmc_resources[] = {
}, },
}; };
const static struct resource tc6393xb_ohci_resources[] = {
{
.start = 0x3000,
.end = 0x31ff,
.flags = IORESOURCE_MEM,
},
{
.start = 0x0300,
.end = 0x03ff,
.flags = IORESOURCE_MEM,
},
{
.start = 0x010000,
.end = 0x017fff,
.flags = IORESOURCE_MEM,
},
{
.start = 0x018000,
.end = 0x01ffff,
.flags = IORESOURCE_MEM,
},
{
.start = IRQ_TC6393_OHCI,
.end = IRQ_TC6393_OHCI,
.flags = IORESOURCE_IRQ,
},
};
static struct resource __devinitdata tc6393xb_fb_resources[] = {
{
.start = 0x5000,
.end = 0x51ff,
.flags = IORESOURCE_MEM,
},
{
.start = 0x0500,
.end = 0x05ff,
.flags = IORESOURCE_MEM,
},
{
.start = 0x100000,
.end = 0x1fffff,
.flags = IORESOURCE_MEM,
},
{
.start = IRQ_TC6393_FB,
.end = IRQ_TC6393_FB,
.flags = IORESOURCE_IRQ,
},
};
static int tc6393xb_ohci_enable(struct platform_device *dev)
{
struct tc6393xb *tc6393xb = dev_get_drvdata(dev->dev.parent);
unsigned long flags;
u16 ccr;
u8 fer;
spin_lock_irqsave(&tc6393xb->lock, flags);
ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR);
ccr |= SCR_CCR_USBCK;
tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR);
fer = tmio_ioread8(tc6393xb->scr + SCR_FER);
fer |= SCR_FER_USBEN;
tmio_iowrite8(fer, tc6393xb->scr + SCR_FER);
spin_unlock_irqrestore(&tc6393xb->lock, flags);
return 0;
}
static int tc6393xb_ohci_disable(struct platform_device *dev)
{
struct tc6393xb *tc6393xb = dev_get_drvdata(dev->dev.parent);
unsigned long flags;
u16 ccr;
u8 fer;
spin_lock_irqsave(&tc6393xb->lock, flags);
fer = tmio_ioread8(tc6393xb->scr + SCR_FER);
fer &= ~SCR_FER_USBEN;
tmio_iowrite8(fer, tc6393xb->scr + SCR_FER);
ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR);
ccr &= ~SCR_CCR_USBCK;
tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR);
spin_unlock_irqrestore(&tc6393xb->lock, flags);
return 0;
}
static int tc6393xb_fb_enable(struct platform_device *dev)
{
struct tc6393xb *tc6393xb = dev_get_drvdata(dev->dev.parent);
unsigned long flags;
u16 ccr;
spin_lock_irqsave(&tc6393xb->lock, flags);
ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR);
ccr &= ~SCR_CCR_MCLK_MASK;
ccr |= SCR_CCR_MCLK_48;
tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR);
spin_unlock_irqrestore(&tc6393xb->lock, flags);
return 0;
}
static int tc6393xb_fb_disable(struct platform_device *dev)
{
struct tc6393xb *tc6393xb = dev_get_drvdata(dev->dev.parent);
unsigned long flags;
u16 ccr;
spin_lock_irqsave(&tc6393xb->lock, flags);
ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR);
ccr &= ~SCR_CCR_MCLK_MASK;
ccr |= SCR_CCR_MCLK_OFF;
tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR);
spin_unlock_irqrestore(&tc6393xb->lock, flags);
return 0;
}
int tc6393xb_lcd_set_power(struct platform_device *fb, bool on)
{
struct platform_device *dev = to_platform_device(fb->dev.parent);
struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
u8 fer;
unsigned long flags;
spin_lock_irqsave(&tc6393xb->lock, flags);
fer = ioread8(tc6393xb->scr + SCR_FER);
if (on)
fer |= SCR_FER_SLCDEN;
else
fer &= ~SCR_FER_SLCDEN;
iowrite8(fer, tc6393xb->scr + SCR_FER);
spin_unlock_irqrestore(&tc6393xb->lock, flags);
return 0;
}
EXPORT_SYMBOL(tc6393xb_lcd_set_power);
int tc6393xb_lcd_mode(struct platform_device *fb,
const struct fb_videomode *mode) {
struct platform_device *dev = to_platform_device(fb->dev.parent);
struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
unsigned long flags;
spin_lock_irqsave(&tc6393xb->lock, flags);
iowrite16(mode->pixclock, tc6393xb->scr + SCR_PLL1CR + 0);
iowrite16(mode->pixclock >> 16, tc6393xb->scr + SCR_PLL1CR + 2);
spin_unlock_irqrestore(&tc6393xb->lock, flags);
return 0;
}
EXPORT_SYMBOL(tc6393xb_lcd_mode);
static struct mfd_cell __devinitdata tc6393xb_cells[] = { static struct mfd_cell __devinitdata tc6393xb_cells[] = {
[TC6393XB_CELL_NAND] = { [TC6393XB_CELL_NAND] = {
.name = "tmio-nand", .name = "tmio-nand",
...@@ -182,6 +354,24 @@ static struct mfd_cell __devinitdata tc6393xb_cells[] = { ...@@ -182,6 +354,24 @@ static struct mfd_cell __devinitdata tc6393xb_cells[] = {
.num_resources = ARRAY_SIZE(tc6393xb_mmc_resources), .num_resources = ARRAY_SIZE(tc6393xb_mmc_resources),
.resources = tc6393xb_mmc_resources, .resources = tc6393xb_mmc_resources,
}, },
[TC6393XB_CELL_OHCI] = {
.name = "tmio-ohci",
.num_resources = ARRAY_SIZE(tc6393xb_ohci_resources),
.resources = tc6393xb_ohci_resources,
.enable = tc6393xb_ohci_enable,
.suspend = tc6393xb_ohci_disable,
.resume = tc6393xb_ohci_enable,
.disable = tc6393xb_ohci_disable,
},
[TC6393XB_CELL_FB] = {
.name = "tmio-fb",
.num_resources = ARRAY_SIZE(tc6393xb_fb_resources),
.resources = tc6393xb_fb_resources,
.enable = tc6393xb_fb_enable,
.suspend = tc6393xb_fb_disable,
.resume = tc6393xb_fb_enable,
.disable = tc6393xb_fb_disable,
},
}; };
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
...@@ -369,41 +559,12 @@ static void tc6393xb_detach_irq(struct platform_device *dev) ...@@ -369,41 +559,12 @@ static void tc6393xb_detach_irq(struct platform_device *dev)
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
static int tc6393xb_hw_init(struct platform_device *dev)
{
struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
int i;
iowrite8(tc6393xb->suspend_state.fer, tc6393xb->scr + SCR_FER);
iowrite16(tcpd->scr_pll2cr, tc6393xb->scr + SCR_PLL2CR);
iowrite16(tc6393xb->suspend_state.ccr, tc6393xb->scr + SCR_CCR);
iowrite16(SCR_MCR_RDY_OPENDRAIN | SCR_MCR_RDY_UNK | SCR_MCR_RDY_EN |
SCR_MCR_INT_OPENDRAIN | SCR_MCR_INT_UNK | SCR_MCR_INT_EN |
BIT(15), tc6393xb->scr + SCR_MCR);
iowrite16(tcpd->scr_gper, tc6393xb->scr + SCR_GPER);
iowrite8(0, tc6393xb->scr + SCR_IRR);
iowrite8(0xbf, tc6393xb->scr + SCR_IMR);
for (i = 0; i < 3; i++) {
iowrite8(tc6393xb->suspend_state.gpo_dsr[i],
tc6393xb->scr + SCR_GPO_DSR(i));
iowrite8(tc6393xb->suspend_state.gpo_doecr[i],
tc6393xb->scr + SCR_GPO_DOECR(i));
iowrite8(tc6393xb->suspend_state.gpi_bcr[i],
tc6393xb->scr + SCR_GPI_BCR(i));
}
return 0;
}
static int __devinit tc6393xb_probe(struct platform_device *dev) static int __devinit tc6393xb_probe(struct platform_device *dev)
{ {
struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
struct tc6393xb *tc6393xb; struct tc6393xb *tc6393xb;
struct resource *iomem, *rscr; struct resource *iomem, *rscr;
int ret, temp; int ret, temp;
int i;
iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!iomem) if (!iomem)
...@@ -458,21 +619,16 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) ...@@ -458,21 +619,16 @@ static int __devinit tc6393xb_probe(struct platform_device *dev)
if (ret) if (ret)
goto err_enable; goto err_enable;
tc6393xb->suspend_state.fer = 0; iowrite8(0, tc6393xb->scr + SCR_FER);
iowrite16(tcpd->scr_pll2cr, tc6393xb->scr + SCR_PLL2CR);
for (i = 0; i < 3; i++) { iowrite16(SCR_CCR_UNK1 | SCR_CCR_HCLK_48,
tc6393xb->suspend_state.gpo_dsr[i] = tc6393xb->scr + SCR_CCR);
(tcpd->scr_gpo_dsr >> (8 * i)) & 0xff; iowrite16(SCR_MCR_RDY_OPENDRAIN | SCR_MCR_RDY_UNK | SCR_MCR_RDY_EN |
tc6393xb->suspend_state.gpo_doecr[i] = SCR_MCR_INT_OPENDRAIN | SCR_MCR_INT_UNK | SCR_MCR_INT_EN |
(tcpd->scr_gpo_doecr >> (8 * i)) & 0xff; BIT(15), tc6393xb->scr + SCR_MCR);
} iowrite16(tcpd->scr_gper, tc6393xb->scr + SCR_GPER);
iowrite8(0, tc6393xb->scr + SCR_IRR);
tc6393xb->suspend_state.ccr = SCR_CCR_UNK1 | iowrite8(0xbf, tc6393xb->scr + SCR_IMR);
SCR_CCR_HCLK_48;
ret = tc6393xb_hw_init(dev);
if (ret)
goto err_hw_init;
printk(KERN_INFO "Toshiba tc6393xb revision %d at 0x%08lx, irq %d\n", printk(KERN_INFO "Toshiba tc6393xb revision %d at 0x%08lx, irq %d\n",
tmio_ioread8(tc6393xb->scr + SCR_REVID), tmio_ioread8(tc6393xb->scr + SCR_REVID),
...@@ -488,16 +644,33 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) ...@@ -488,16 +644,33 @@ static int __devinit tc6393xb_probe(struct platform_device *dev)
tc6393xb_attach_irq(dev); tc6393xb_attach_irq(dev);
if (tcpd->setup) {
ret = tcpd->setup(dev);
if (ret)
goto err_setup;
}
tc6393xb_cells[TC6393XB_CELL_NAND].driver_data = tcpd->nand_data; tc6393xb_cells[TC6393XB_CELL_NAND].driver_data = tcpd->nand_data;
tc6393xb_cells[TC6393XB_CELL_NAND].platform_data = tc6393xb_cells[TC6393XB_CELL_NAND].platform_data =
&tc6393xb_cells[TC6393XB_CELL_NAND]; &tc6393xb_cells[TC6393XB_CELL_NAND];
tc6393xb_cells[TC6393XB_CELL_NAND].data_size = tc6393xb_cells[TC6393XB_CELL_NAND].data_size =
sizeof(tc6393xb_cells[TC6393XB_CELL_NAND]); sizeof(tc6393xb_cells[TC6393XB_CELL_NAND]);
tc6393xb_cells[TC6393XB_CELL_MMC].platform_data = tc6393xb_cells[TC6393XB_CELL_MMC].platform_data =
&tc6393xb_cells[TC6393XB_CELL_MMC]; &tc6393xb_cells[TC6393XB_CELL_MMC];
tc6393xb_cells[TC6393XB_CELL_MMC].data_size = tc6393xb_cells[TC6393XB_CELL_MMC].data_size =
sizeof(tc6393xb_cells[TC6393XB_CELL_MMC]); sizeof(tc6393xb_cells[TC6393XB_CELL_MMC]);
tc6393xb_cells[TC6393XB_CELL_OHCI].platform_data =
&tc6393xb_cells[TC6393XB_CELL_OHCI];
tc6393xb_cells[TC6393XB_CELL_OHCI].data_size =
sizeof(tc6393xb_cells[TC6393XB_CELL_OHCI]);
tc6393xb_cells[TC6393XB_CELL_FB].driver_data = tcpd->fb_data;
tc6393xb_cells[TC6393XB_CELL_FB].platform_data =
&tc6393xb_cells[TC6393XB_CELL_FB];
tc6393xb_cells[TC6393XB_CELL_FB].data_size =
sizeof(tc6393xb_cells[TC6393XB_CELL_FB]);
ret = mfd_add_devices(&dev->dev, dev->id, ret = mfd_add_devices(&dev->dev, dev->id,
tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells), tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells),
...@@ -506,12 +679,15 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) ...@@ -506,12 +679,15 @@ static int __devinit tc6393xb_probe(struct platform_device *dev)
if (!ret) if (!ret)
return 0; return 0;
if (tcpd->teardown)
tcpd->teardown(dev);
err_setup:
tc6393xb_detach_irq(dev); tc6393xb_detach_irq(dev);
err_gpio_add: err_gpio_add:
if (tc6393xb->gpio.base != -1) if (tc6393xb->gpio.base != -1)
temp = gpiochip_remove(&tc6393xb->gpio); temp = gpiochip_remove(&tc6393xb->gpio);
err_hw_init:
tcpd->disable(dev); tcpd->disable(dev);
err_clk_enable: err_clk_enable:
clk_disable(tc6393xb->clk); clk_disable(tc6393xb->clk);
...@@ -535,6 +711,10 @@ static int __devexit tc6393xb_remove(struct platform_device *dev) ...@@ -535,6 +711,10 @@ static int __devexit tc6393xb_remove(struct platform_device *dev)
int ret; int ret;
mfd_remove_devices(&dev->dev); mfd_remove_devices(&dev->dev);
if (tcpd->teardown)
tcpd->teardown(dev);
tc6393xb_detach_irq(dev); tc6393xb_detach_irq(dev);
if (tc6393xb->gpio.base != -1) { if (tc6393xb->gpio.base != -1) {
...@@ -585,15 +765,37 @@ static int tc6393xb_resume(struct platform_device *dev) ...@@ -585,15 +765,37 @@ static int tc6393xb_resume(struct platform_device *dev)
struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
struct tc6393xb *tc6393xb = platform_get_drvdata(dev); struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
int ret; int ret;
int i;
clk_enable(tc6393xb->clk); clk_enable(tc6393xb->clk);
ret = tcpd->resume(dev); ret = tcpd->resume(dev);
if (ret) if (ret)
return ret; return ret;
return tc6393xb_hw_init(dev); if (!tcpd->resume_restore)
return 0;
iowrite8(tc6393xb->suspend_state.fer, tc6393xb->scr + SCR_FER);
iowrite16(tcpd->scr_pll2cr, tc6393xb->scr + SCR_PLL2CR);
iowrite16(tc6393xb->suspend_state.ccr, tc6393xb->scr + SCR_CCR);
iowrite16(SCR_MCR_RDY_OPENDRAIN | SCR_MCR_RDY_UNK | SCR_MCR_RDY_EN |
SCR_MCR_INT_OPENDRAIN | SCR_MCR_INT_UNK | SCR_MCR_INT_EN |
BIT(15), tc6393xb->scr + SCR_MCR);
iowrite16(tcpd->scr_gper, tc6393xb->scr + SCR_GPER);
iowrite8(0, tc6393xb->scr + SCR_IRR);
iowrite8(0xbf, tc6393xb->scr + SCR_IMR);
for (i = 0; i < 3; i++) {
iowrite8(tc6393xb->suspend_state.gpo_dsr[i],
tc6393xb->scr + SCR_GPO_DSR(i));
iowrite8(tc6393xb->suspend_state.gpo_doecr[i],
tc6393xb->scr + SCR_GPO_DOECR(i));
iowrite8(tc6393xb->suspend_state.gpi_bcr[i],
tc6393xb->scr + SCR_GPI_BCR(i));
}
return 0;
} }
#else #else
#define tc6393xb_suspend NULL #define tc6393xb_suspend NULL
......
/*
* twl4030_core.c - driver for TWL4030/TPS659x0 PM and audio CODEC devices
*
* Copyright (C) 2005-2006 Texas Instruments, Inc.
*
* Modifications to defer interrupt handling to a kernel thread:
* Copyright (C) 2006 MontaVista Software, Inc.
*
* Based on tlv320aic23.c:
* Copyright (c) by Kai Svahn <kai.svahn@nokia.com>
*
* Code cleanup and modifications to IRQ handler.
* by syed khasim <x0khasim@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel_stat.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/random.h>
#include <linux/kthread.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/i2c/twl4030.h>
/*
* The TWL4030 "Triton 2" is one of a family of a multi-function "Power
* Management and System Companion Device" chips originally designed for
* use in OMAP2 and OMAP 3 based systems. Its control interfaces use I2C,
* often at around 3 Mbit/sec, including for interrupt handling.
*
* This driver core provides genirq support for the interrupts emitted,
* by the various modules, and exports register access primitives.
*
* FIXME this driver currently requires use of the first interrupt line
* (and associated registers).
*/
#define DRIVER_NAME "twl4030"
#if defined(CONFIG_TWL4030_BCI_BATTERY) || \
defined(CONFIG_TWL4030_BCI_BATTERY_MODULE)
#define twl_has_bci() true
#else
#define twl_has_bci() false
#endif
#if defined(CONFIG_KEYBOARD_TWL4030) || defined(CONFIG_KEYBOARD_TWL4030_MODULE)
#define twl_has_keypad() true
#else
#define twl_has_keypad() false
#endif
#if defined(CONFIG_GPIO_TWL4030) || defined(CONFIG_GPIO_TWL4030_MODULE)
#define twl_has_gpio() true
#else
#define twl_has_gpio() false
#endif
#if defined(CONFIG_TWL4030_MADC) || defined(CONFIG_TWL4030_MADC_MODULE)
#define twl_has_madc() true
#else
#define twl_has_madc() false
#endif
#if defined(CONFIG_RTC_DRV_TWL4030) || defined(CONFIG_RTC_DRV_TWL4030_MODULE)
#define twl_has_rtc() true
#else
#define twl_has_rtc() false
#endif
#if defined(CONFIG_TWL4030_USB) || defined(CONFIG_TWL4030_USB_MODULE)
#define twl_has_usb() true
#else
#define twl_has_usb() false
#endif
static inline void activate_irq(int irq)
{
#ifdef CONFIG_ARM
/* ARM requires an extra step to clear IRQ_NOREQUEST, which it
* sets on behalf of every irq_chip. Also sets IRQ_NOPROBE.
*/
set_irq_flags(irq, IRQF_VALID);
#else
/* same effect on other architectures */
set_irq_noprobe(irq);
#endif
}
/* Primary Interrupt Handler on TWL4030 Registers */
/* Register Definitions */
#define REG_PIH_ISR_P1 (0x1)
#define REG_PIH_ISR_P2 (0x2)
#define REG_PIH_SIR (0x3)
/* Triton Core internal information (BEGIN) */
/* Last - for index max*/
#define TWL4030_MODULE_LAST TWL4030_MODULE_SECURED_REG
#define TWL4030_NUM_SLAVES 4
/* Base Address defns for twl4030_map[] */
/* subchip/slave 0 - USB ID */
#define TWL4030_BASEADD_USB 0x0000
/* subchip/slave 1 - AUD ID */
#define TWL4030_BASEADD_AUDIO_VOICE 0x0000
#define TWL4030_BASEADD_GPIO 0x0098
#define TWL4030_BASEADD_INTBR 0x0085
#define TWL4030_BASEADD_PIH 0x0080
#define TWL4030_BASEADD_TEST 0x004C
/* subchip/slave 2 - AUX ID */
#define TWL4030_BASEADD_INTERRUPTS 0x00B9
#define TWL4030_BASEADD_LED 0x00EE
#define TWL4030_BASEADD_MADC 0x0000
#define TWL4030_BASEADD_MAIN_CHARGE 0x0074
#define TWL4030_BASEADD_PRECHARGE 0x00AA
#define TWL4030_BASEADD_PWM0 0x00F8
#define TWL4030_BASEADD_PWM1 0x00FB
#define TWL4030_BASEADD_PWMA 0x00EF
#define TWL4030_BASEADD_PWMB 0x00F1
#define TWL4030_BASEADD_KEYPAD 0x00D2
/* subchip/slave 3 - POWER ID */
#define TWL4030_BASEADD_BACKUP 0x0014
#define TWL4030_BASEADD_INT 0x002E
#define TWL4030_BASEADD_PM_MASTER 0x0036
#define TWL4030_BASEADD_PM_RECEIVER 0x005B
#define TWL4030_BASEADD_RTC 0x001C
#define TWL4030_BASEADD_SECURED_REG 0x0000
/* Triton Core internal information (END) */
/* Few power values */
#define R_CFG_BOOT 0x05
#define R_PROTECT_KEY 0x0E
/* access control values for R_PROTECT_KEY */
#define KEY_UNLOCK1 0xce
#define KEY_UNLOCK2 0xec
#define KEY_LOCK 0x00
/* some fields in R_CFG_BOOT */
#define HFCLK_FREQ_19p2_MHZ (1 << 0)
#define HFCLK_FREQ_26_MHZ (2 << 0)
#define HFCLK_FREQ_38p4_MHZ (3 << 0)
#define HIGH_PERF_SQ (1 << 3)
/*----------------------------------------------------------------------*/
/**
* struct twl4030_mod_iregs - TWL module IMR/ISR regs to mask/clear at init
* @mod_no: TWL4030 module number (e.g., TWL4030_MODULE_GPIO)
* @sih_ctrl: address of module SIH_CTRL register
* @reg_cnt: number of IMR/ISR regs
* @imrs: pointer to array of TWL module interrupt mask register indices
* @isrs: pointer to array of TWL module interrupt status register indices
*
* Ties together TWL4030 modules and lists of IMR/ISR registers to mask/clear
* during twl_init_irq().
*/
struct twl4030_mod_iregs {
const u8 mod_no;
const u8 sih_ctrl;
const u8 reg_cnt;
const u8 *imrs;
const u8 *isrs;
};
/* TWL4030 INT module interrupt mask registers */
static const u8 __initconst twl4030_int_imr_regs[] = {
TWL4030_INT_PWR_IMR1,
TWL4030_INT_PWR_IMR2,
};
/* TWL4030 INT module interrupt status registers */
static const u8 __initconst twl4030_int_isr_regs[] = {
TWL4030_INT_PWR_ISR1,
TWL4030_INT_PWR_ISR2,
};
/* TWL4030 INTERRUPTS module interrupt mask registers */
static const u8 __initconst twl4030_interrupts_imr_regs[] = {
TWL4030_INTERRUPTS_BCIIMR1A,
TWL4030_INTERRUPTS_BCIIMR1B,
TWL4030_INTERRUPTS_BCIIMR2A,
TWL4030_INTERRUPTS_BCIIMR2B,
};
/* TWL4030 INTERRUPTS module interrupt status registers */
static const u8 __initconst twl4030_interrupts_isr_regs[] = {
TWL4030_INTERRUPTS_BCIISR1A,
TWL4030_INTERRUPTS_BCIISR1B,
TWL4030_INTERRUPTS_BCIISR2A,
TWL4030_INTERRUPTS_BCIISR2B,
};
/* TWL4030 MADC module interrupt mask registers */
static const u8 __initconst twl4030_madc_imr_regs[] = {
TWL4030_MADC_IMR1,
TWL4030_MADC_IMR2,
};
/* TWL4030 MADC module interrupt status registers */
static const u8 __initconst twl4030_madc_isr_regs[] = {
TWL4030_MADC_ISR1,
TWL4030_MADC_ISR2,
};
/* TWL4030 keypad module interrupt mask registers */
static const u8 __initconst twl4030_keypad_imr_regs[] = {
TWL4030_KEYPAD_KEYP_IMR1,
TWL4030_KEYPAD_KEYP_IMR2,
};
/* TWL4030 keypad module interrupt status registers */
static const u8 __initconst twl4030_keypad_isr_regs[] = {
TWL4030_KEYPAD_KEYP_ISR1,
TWL4030_KEYPAD_KEYP_ISR2,
};
/* TWL4030 GPIO module interrupt mask registers */
static const u8 __initconst twl4030_gpio_imr_regs[] = {
REG_GPIO_IMR1A,
REG_GPIO_IMR1B,
REG_GPIO_IMR2A,
REG_GPIO_IMR2B,
REG_GPIO_IMR3A,
REG_GPIO_IMR3B,
};
/* TWL4030 GPIO module interrupt status registers */
static const u8 __initconst twl4030_gpio_isr_regs[] = {
REG_GPIO_ISR1A,
REG_GPIO_ISR1B,
REG_GPIO_ISR2A,
REG_GPIO_ISR2B,
REG_GPIO_ISR3A,
REG_GPIO_ISR3B,
};
/* TWL4030 modules that have IMR/ISR registers that must be masked/cleared */
static const struct twl4030_mod_iregs __initconst twl4030_mod_regs[] = {
{
.mod_no = TWL4030_MODULE_INT,
.sih_ctrl = TWL4030_INT_PWR_SIH_CTRL,
.reg_cnt = ARRAY_SIZE(twl4030_int_imr_regs),
.imrs = twl4030_int_imr_regs,
.isrs = twl4030_int_isr_regs,
},
{
.mod_no = TWL4030_MODULE_INTERRUPTS,
.sih_ctrl = TWL4030_INTERRUPTS_BCISIHCTRL,
.reg_cnt = ARRAY_SIZE(twl4030_interrupts_imr_regs),
.imrs = twl4030_interrupts_imr_regs,
.isrs = twl4030_interrupts_isr_regs,
},
{
.mod_no = TWL4030_MODULE_MADC,
.sih_ctrl = TWL4030_MADC_SIH_CTRL,
.reg_cnt = ARRAY_SIZE(twl4030_madc_imr_regs),
.imrs = twl4030_madc_imr_regs,
.isrs = twl4030_madc_isr_regs,
},
{
.mod_no = TWL4030_MODULE_KEYPAD,
.sih_ctrl = TWL4030_KEYPAD_KEYP_SIH_CTRL,
.reg_cnt = ARRAY_SIZE(twl4030_keypad_imr_regs),
.imrs = twl4030_keypad_imr_regs,
.isrs = twl4030_keypad_isr_regs,
},
{
.mod_no = TWL4030_MODULE_GPIO,
.sih_ctrl = REG_GPIO_SIH_CTRL,
.reg_cnt = ARRAY_SIZE(twl4030_gpio_imr_regs),
.imrs = twl4030_gpio_imr_regs,
.isrs = twl4030_gpio_isr_regs,
},
};
/*----------------------------------------------------------------*/
/* is driver active, bound to a chip? */
static bool inuse;
/* Structure for each TWL4030 Slave */
struct twl4030_client {
struct i2c_client *client;
u8 address;
/* max numb of i2c_msg required is for read =2 */
struct i2c_msg xfer_msg[2];
/* To lock access to xfer_msg */
struct mutex xfer_lock;
};
static struct twl4030_client twl4030_modules[TWL4030_NUM_SLAVES];
/* mapping the module id to slave id and base address */
struct twl4030mapping {
unsigned char sid; /* Slave ID */
unsigned char base; /* base address */
};
static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
/*
* NOTE: don't change this table without updating the
* <linux/i2c/twl4030.h> defines for TWL4030_MODULE_*
* so they continue to match the order in this table.
*/
{ 0, TWL4030_BASEADD_USB },
{ 1, TWL4030_BASEADD_AUDIO_VOICE },
{ 1, TWL4030_BASEADD_GPIO },
{ 1, TWL4030_BASEADD_INTBR },
{ 1, TWL4030_BASEADD_PIH },
{ 1, TWL4030_BASEADD_TEST },
{ 2, TWL4030_BASEADD_KEYPAD },
{ 2, TWL4030_BASEADD_MADC },
{ 2, TWL4030_BASEADD_INTERRUPTS },
{ 2, TWL4030_BASEADD_LED },
{ 2, TWL4030_BASEADD_MAIN_CHARGE },
{ 2, TWL4030_BASEADD_PRECHARGE },
{ 2, TWL4030_BASEADD_PWM0 },
{ 2, TWL4030_BASEADD_PWM1 },
{ 2, TWL4030_BASEADD_PWMA },
{ 2, TWL4030_BASEADD_PWMB },
{ 3, TWL4030_BASEADD_BACKUP },
{ 3, TWL4030_BASEADD_INT },
{ 3, TWL4030_BASEADD_PM_MASTER },
{ 3, TWL4030_BASEADD_PM_RECEIVER },
{ 3, TWL4030_BASEADD_RTC },
{ 3, TWL4030_BASEADD_SECURED_REG },
};
/*----------------------------------------------------------------------*/
/*
* TWL4030 doesn't have PIH mask, hence dummy function for mask
* and unmask of the (eight) interrupts reported at that level ...
* masking is only available from SIH (secondary) modules.
*/
static void twl4030_i2c_ackirq(unsigned int irq)
{
}
static void twl4030_i2c_disableint(unsigned int irq)
{
}
static void twl4030_i2c_enableint(unsigned int irq)
{
}
static struct irq_chip twl4030_irq_chip = {
.name = "twl4030",
.ack = twl4030_i2c_ackirq,
.mask = twl4030_i2c_disableint,
.unmask = twl4030_i2c_enableint,
};
/*----------------------------------------------------------------------*/
/* Exported Functions */
/**
* twl4030_i2c_write - Writes a n bit register in TWL4030
* @mod_no: module number
* @value: an array of num_bytes+1 containing data to write
* @reg: register address (just offset will do)
* @num_bytes: number of bytes to transfer
*
* IMPORTANT: for 'value' parameter: Allocate value num_bytes+1 and
* valid data starts at Offset 1.
*
* Returns the result of operation - 0 is success
*/
int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, u8 num_bytes)
{
int ret;
int sid;
struct twl4030_client *twl;
struct i2c_msg *msg;
if (unlikely(mod_no > TWL4030_MODULE_LAST)) {
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
return -EPERM;
}
sid = twl4030_map[mod_no].sid;
twl = &twl4030_modules[sid];
if (unlikely(!inuse)) {
pr_err("%s: client %d is not initialized\n", DRIVER_NAME, sid);
return -EPERM;
}
mutex_lock(&twl->xfer_lock);
/*
* [MSG1]: fill the register address data
* fill the data Tx buffer
*/
msg = &twl->xfer_msg[0];
msg->addr = twl->address;
msg->len = num_bytes + 1;
msg->flags = 0;
msg->buf = value;
/* over write the first byte of buffer with the register address */
*value = twl4030_map[mod_no].base + reg;
ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 1);
mutex_unlock(&twl->xfer_lock);
/* i2cTransfer returns num messages.translate it pls.. */
if (ret >= 0)
ret = 0;
return ret;
}
EXPORT_SYMBOL(twl4030_i2c_write);
/**
* twl4030_i2c_read - Reads a n bit register in TWL4030
* @mod_no: module number
* @value: an array of num_bytes containing data to be read
* @reg: register address (just offset will do)
* @num_bytes: number of bytes to transfer
*
* 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 ret;
u8 val;
int sid;
struct twl4030_client *twl;
struct i2c_msg *msg;
if (unlikely(mod_no > TWL4030_MODULE_LAST)) {
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
return -EPERM;
}
sid = twl4030_map[mod_no].sid;
twl = &twl4030_modules[sid];
if (unlikely(!inuse)) {
pr_err("%s: client %d is not initialized\n", DRIVER_NAME, sid);
return -EPERM;
}
mutex_lock(&twl->xfer_lock);
/* [MSG1] fill the register address data */
msg = &twl->xfer_msg[0];
msg->addr = twl->address;
msg->len = 1;
msg->flags = 0; /* Read the register value */
val = twl4030_map[mod_no].base + reg;
msg->buf = &val;
/* [MSG2] fill the data rx buffer */
msg = &twl->xfer_msg[1];
msg->addr = twl->address;
msg->flags = I2C_M_RD; /* Read the register value */
msg->len = num_bytes; /* only n bytes */
msg->buf = value;
ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 2);
mutex_unlock(&twl->xfer_lock);
/* i2cTransfer returns num messages.translate it pls.. */
if (ret >= 0)
ret = 0;
return ret;
}
EXPORT_SYMBOL(twl4030_i2c_read);
/**
* twl4030_i2c_write_u8 - Writes a 8 bit register in TWL4030
* @mod_no: module number
* @value: the value to be written 8 bit
* @reg: register address (just offset will do)
*
* Returns result of operation - 0 is success
*/
int twl4030_i2c_write_u8(u8 mod_no, u8 value, u8 reg)
{
/* 2 bytes offset 1 contains the data offset 0 is used by i2c_write */
u8 temp_buffer[2] = { 0 };
/* offset 1 contains the data */
temp_buffer[1] = value;
return twl4030_i2c_write(mod_no, temp_buffer, reg, 1);
}
EXPORT_SYMBOL(twl4030_i2c_write_u8);
/**
* twl4030_i2c_read_u8 - Reads a 8 bit register from TWL4030
* @mod_no: module number
* @value: the value read 8 bit
* @reg: register address (just offset will do)
*
* Returns result of operation - 0 is success
*/
int twl4030_i2c_read_u8(u8 mod_no, u8 *value, u8 reg)
{
return twl4030_i2c_read(mod_no, value, reg, 1);
}
EXPORT_SYMBOL(twl4030_i2c_read_u8);
/*----------------------------------------------------------------------*/
static unsigned twl4030_irq_base;
static struct completion irq_event;
/*
* This thread processes interrupts reported by the Primary Interrupt Handler.
*/
static int twl4030_irq_thread(void *data)
{
long irq = (long)data;
irq_desc_t *desc = irq_desc + irq;
static unsigned i2c_errors;
const static unsigned max_i2c_errors = 100;
current->flags |= PF_NOFREEZE;
while (!kthread_should_stop()) {
int ret;
int module_irq;
u8 pih_isr;
/* Wait for IRQ, then read PIH irq status (also blocking) */
wait_for_completion_interruptible(&irq_event);
ret = twl4030_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
REG_PIH_ISR_P1);
if (ret) {
pr_warning("%s: I2C error %d reading PIH ISR\n",
DRIVER_NAME, ret);
if (++i2c_errors >= max_i2c_errors) {
printk(KERN_ERR "Maximum I2C error count"
" exceeded. Terminating %s.\n",
__func__);
break;
}
complete(&irq_event);
continue;
}
/* these handlers deal with the relevant SIH irq status */
local_irq_disable();
for (module_irq = twl4030_irq_base;
pih_isr;
pih_isr >>= 1, module_irq++) {
if (pih_isr & 0x1) {
irq_desc_t *d = irq_desc + module_irq;
d->handle_irq(module_irq, d);
}
}
local_irq_enable();
desc->chip->unmask(irq);
}
return 0;
}
/*
* do_twl4030_irq() is the desc->handle method for the twl4030 interrupt.
* This is a chained interrupt, so there is no desc->action method for it.
* Now we need to query the interrupt controller in the twl4030 to determine
* which module is generating the interrupt request. However, we can't do i2c
* transactions in interrupt context, so we must defer that work to a kernel
* thread. All we do here is acknowledge and mask the interrupt and wakeup
* the kernel thread.
*/
static void do_twl4030_irq(unsigned int irq, irq_desc_t *desc)
{
const unsigned int cpu = smp_processor_id();
/*
* Earlier this was desc->triggered = 1;
*/
desc->status |= IRQ_LEVEL;
/*
* Acknowledge, clear _AND_ disable the interrupt.
*/
desc->chip->ack(irq);
if (!desc->depth) {
kstat_cpu(cpu).irqs[irq]++;
complete(&irq_event);
}
}
static struct task_struct * __init start_twl4030_irq_thread(long irq)
{
struct task_struct *thread;
init_completion(&irq_event);
thread = kthread_run(twl4030_irq_thread, (void *)irq, "twl4030-irq");
if (!thread)
pr_err("%s: could not create twl4030 irq %ld thread!\n",
DRIVER_NAME, irq);
return thread;
}
/*----------------------------------------------------------------------*/
static int add_children(struct twl4030_platform_data *pdata)
{
struct platform_device *pdev = NULL;
struct twl4030_client *twl = NULL;
int status = 0;
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 = TWL4030_PWRIRQ_CHG_PRES,
.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_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;
}
}
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;
}
}
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;
}
}
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
* "msecure" line ... but for now we just expect board
* setup to tell the chip "we are secure" at all times.
* Eventually, Linux might become more aware of such
* HW security concerns, and "least privilege".
*/
/* RTC module IRQ */
if (status == 0) {
struct resource r = {
/* REVISIT don't hard-wire this stuff */
.start = TWL4030_PWRIRQ_RTC,
.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;
}
}
if (twl_has_usb() && pdata->usb) {
twl = &twl4030_modules[0];
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 = TWL4030_PWRIRQ_USB_PRES,
.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);
}
}
err:
if (status)
pr_err("failed to add twl4030's children (status %d)\n", status);
return status;
}
/*----------------------------------------------------------------------*/
/*
* These three functions initialize the on-chip clock framework,
* letting it generate the right frequencies for USB, MADC, and
* other purposes.
*/
static inline int __init protect_pm_master(void)
{
int e = 0;
e = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_LOCK,
R_PROTECT_KEY);
return e;
}
static inline int __init unprotect_pm_master(void)
{
int e = 0;
e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_UNLOCK1,
R_PROTECT_KEY);
e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_UNLOCK2,
R_PROTECT_KEY);
return e;
}
static void __init clocks_init(void)
{
int e = 0;
struct clk *osc;
u32 rate;
u8 ctrl = HFCLK_FREQ_26_MHZ;
#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
if (cpu_is_omap2430())
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");
return;
}
rate = clk_get_rate(osc);
clk_put(osc);
switch (rate) {
case 19200000:
ctrl = HFCLK_FREQ_19p2_MHZ;
break;
case 26000000:
ctrl = HFCLK_FREQ_26_MHZ;
break;
case 38400000:
ctrl = HFCLK_FREQ_38p4_MHZ;
break;
}
ctrl |= HIGH_PERF_SQ;
e |= unprotect_pm_master();
/* effect->MADC+USB ck en */
e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, ctrl, R_CFG_BOOT);
e |= protect_pm_master();
if (e < 0)
pr_err("%s: clock init err [%d]\n", DRIVER_NAME, e);
}
/*----------------------------------------------------------------------*/
/**
* twl4030_i2c_clear_isr - clear TWL4030 SIH ISR regs via read + write
* @mod_no: TWL4030 module number
* @reg: register index to clear
* @cor: value of the <module>_SIH_CTRL.COR bit (1 or 0)
*
* Either reads (cor == 1) or writes (cor == 0) to a TWL4030 interrupt
* status register to ensure that any prior interrupts are cleared.
* Returns the status from the I2C read operation.
*/
static int __init twl4030_i2c_clear_isr(u8 mod_no, u8 reg, u8 cor)
{
u8 tmp;
return (cor) ? twl4030_i2c_read_u8(mod_no, &tmp, reg) :
twl4030_i2c_write_u8(mod_no, 0xff, reg);
}
/**
* twl4030_read_cor_bit - are TWL module ISRs cleared by reads or writes?
* @mod_no: TWL4030 module number
* @reg: register index to clear
*
* Returns 1 if the TWL4030 SIH interrupt status registers (ISRs) for
* the specified TWL module are cleared by reads, or 0 if cleared by
* writes.
*/
static int twl4030_read_cor_bit(u8 mod_no, u8 reg)
{
u8 tmp = 0;
WARN_ON(twl4030_i2c_read_u8(mod_no, &tmp, reg) < 0);
tmp &= TWL4030_SIH_CTRL_COR_MASK;
tmp >>= __ffs(TWL4030_SIH_CTRL_COR_MASK);
return tmp;
}
/**
* twl4030_mask_clear_intrs - mask and clear all TWL4030 interrupts
* @t: pointer to twl4030_mod_iregs array
* @t_sz: ARRAY_SIZE(t) (starting at 1)
*
* Mask all TWL4030 interrupt mask registers (IMRs) and clear all
* interrupt status registers (ISRs). No return value, but will WARN if
* any I2C operations fail.
*/
static void __init twl4030_mask_clear_intrs(const struct twl4030_mod_iregs *t,
const u8 t_sz)
{
int i, j;
/*
* N.B. - further efficiency is possible here. Eight I2C
* operations on BCI and GPIO modules are avoidable if I2C
* burst read/write transactions were implemented. Would
* probably save about 1ms of boot time and a small amount of
* power.
*/
for (i = 0; i < t_sz; i++) {
const struct twl4030_mod_iregs tmr = t[i];
int cor;
/* Are ISRs cleared by reads or writes? */
cor = twl4030_read_cor_bit(tmr.mod_no, tmr.sih_ctrl);
for (j = 0; j < tmr.reg_cnt; j++) {
/* Mask interrupts at the TWL4030 */
WARN_ON(twl4030_i2c_write_u8(tmr.mod_no, 0xff,
tmr.imrs[j]) < 0);
/* Clear TWL4030 ISRs */
WARN_ON(twl4030_i2c_clear_isr(tmr.mod_no,
tmr.isrs[j], cor) < 0);
}
}
}
static void twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
{
int i;
/*
* Mask and clear all TWL4030 interrupts since initially we do
* not have any TWL4030 module interrupt handlers present
*/
twl4030_mask_clear_intrs(twl4030_mod_regs,
ARRAY_SIZE(twl4030_mod_regs));
twl4030_irq_base = irq_base;
/* install an irq handler for each of the PIH modules */
for (i = irq_base; i < irq_end; i++) {
set_irq_chip_and_handler(i, &twl4030_irq_chip,
handle_simple_irq);
activate_irq(i);
}
/* install an irq handler to demultiplex the TWL4030 interrupt */
set_irq_data(irq_num, start_twl4030_irq_thread(irq_num));
set_irq_chained_handler(irq_num, do_twl4030_irq);
}
/*----------------------------------------------------------------------*/
static int twl4030_remove(struct i2c_client *client)
{
unsigned i;
/* FIXME undo twl_init_irq() */
if (twl4030_irq_base) {
dev_err(&client->dev, "can't yet clean up IRQs?\n");
return -ENOSYS;
}
for (i = 0; i < TWL4030_NUM_SLAVES; i++) {
struct twl4030_client *twl = &twl4030_modules[i];
if (twl->client && twl->client != client)
i2c_unregister_device(twl->client);
twl4030_modules[i].client = NULL;
}
inuse = false;
return 0;
}
/* NOTE: this driver only handles a single twl4030/tps659x0 chip */
static int
twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int status;
unsigned i;
struct twl4030_platform_data *pdata = client->dev.platform_data;
if (!pdata) {
dev_dbg(&client->dev, "no platform data?\n");
return -EINVAL;
}
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
dev_dbg(&client->dev, "can't talk I2C?\n");
return -EIO;
}
if (inuse || twl4030_irq_base) {
dev_dbg(&client->dev, "driver is already in use\n");
return -EBUSY;
}
for (i = 0; i < TWL4030_NUM_SLAVES; i++) {
struct twl4030_client *twl = &twl4030_modules[i];
twl->address = client->addr + i;
if (i == 0)
twl->client = client;
else {
twl->client = i2c_new_dummy(client->adapter,
twl->address);
if (!twl->client) {
dev_err(&twl->client->dev,
"can't attach client %d\n", i);
status = -ENOMEM;
goto fail;
}
strlcpy(twl->client->name, id->name,
sizeof(twl->client->name));
}
mutex_init(&twl->xfer_lock);
}
inuse = true;
/* setup clock framework */
clocks_init();
/* Maybe init the T2 Interrupt subsystem */
if (client->irq
&& pdata->irq_base
&& pdata->irq_end > pdata->irq_base) {
twl_init_irq(client->irq, pdata->irq_base, pdata->irq_end);
dev_info(&client->dev, "IRQ %d chains IRQs %d..%d\n",
client->irq, pdata->irq_base, pdata->irq_end - 1);
}
status = add_children(pdata);
fail:
if (status < 0)
twl4030_remove(client);
return status;
}
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 */
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(i2c, twl4030_ids);
/* One Client Driver , 4 Clients */
static struct i2c_driver twl4030_driver = {
.driver.name = DRIVER_NAME,
.id_table = twl4030_ids,
.probe = twl4030_probe,
.remove = twl4030_remove,
};
static int __init twl4030_init(void)
{
return i2c_add_driver(&twl4030_driver);
}
subsys_initcall(twl4030_init);
static void __exit twl4030_exit(void)
{
i2c_del_driver(&twl4030_driver);
}
module_exit(twl4030_exit);
MODULE_AUTHOR("Texas Instruments, Inc.");
MODULE_DESCRIPTION("I2C Core interface for TWL4030");
MODULE_LICENSE("GPL");
...@@ -1217,7 +1217,7 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, ...@@ -1217,7 +1217,7 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
mutex_init(&wm8350->irq_mutex); mutex_init(&wm8350->irq_mutex);
INIT_WORK(&wm8350->irq_work, wm8350_irq_worker); INIT_WORK(&wm8350->irq_work, wm8350_irq_worker);
if (irq != NO_IRQ) { if (irq) {
ret = request_irq(irq, wm8350_irq, 0, ret = request_irq(irq, wm8350_irq, 0,
"wm8350", wm8350); "wm8350", wm8350);
if (ret != 0) { if (ret != 0) {
......
/*
* twl4030.h - header for TWL4030 PM and audio CODEC device
*
* Copyright (C) 2005-2006 Texas Instruments, Inc.
*
* Based on tlv320aic23.c:
* Copyright (c) by Kai Svahn <kai.svahn@nokia.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef __TWL4030_H_
#define __TWL4030_H_
/*
* Using the twl4030 core we address registers using a pair
* { module id, relative register offset }
* which that core then maps to the relevant
* { i2c slave, absolute register address }
*
* The module IDs are meaningful only to the twl4030 core code,
* which uses them as array indices to look up the first register
* address each module uses within a given i2c slave.
*/
/* Slave 0 (i2c address 0x48) */
#define TWL4030_MODULE_USB 0x00
/* Slave 1 (i2c address 0x49) */
#define TWL4030_MODULE_AUDIO_VOICE 0x01
#define TWL4030_MODULE_GPIO 0x02
#define TWL4030_MODULE_INTBR 0x03
#define TWL4030_MODULE_PIH 0x04
#define TWL4030_MODULE_TEST 0x05
/* Slave 2 (i2c address 0x4a) */
#define TWL4030_MODULE_KEYPAD 0x06
#define TWL4030_MODULE_MADC 0x07
#define TWL4030_MODULE_INTERRUPTS 0x08
#define TWL4030_MODULE_LED 0x09
#define TWL4030_MODULE_MAIN_CHARGE 0x0A
#define TWL4030_MODULE_PRECHARGE 0x0B
#define TWL4030_MODULE_PWM0 0x0C
#define TWL4030_MODULE_PWM1 0x0D
#define TWL4030_MODULE_PWMA 0x0E
#define TWL4030_MODULE_PWMB 0x0F
/* Slave 3 (i2c address 0x4b) */
#define TWL4030_MODULE_BACKUP 0x10
#define TWL4030_MODULE_INT 0x11
#define TWL4030_MODULE_PM_MASTER 0x12
#define TWL4030_MODULE_PM_RECEIVER 0x13
#define TWL4030_MODULE_RTC 0x14
#define TWL4030_MODULE_SECURED_REG 0x15
/*
* Read and write single 8-bit registers
*/
int twl4030_i2c_write_u8(u8 mod_no, u8 val, u8 reg);
int twl4030_i2c_read_u8(u8 mod_no, u8 *val, u8 reg);
/*
* Read and write several 8-bit registers at once.
*
* 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);
/*----------------------------------------------------------------------*/
/*
* NOTE: at up to 1024 registers, this is a big chip.
*
* Avoid putting register declarations in this file, instead of into
* a driver-private file, unless some of the registers in a block
* need to be shared with other drivers. One example is blocks that
* have Secondary IRQ Handler (SIH) registers.
*/
#define TWL4030_SIH_CTRL_EXCLEN_MASK BIT(0)
#define TWL4030_SIH_CTRL_PENDDIS_MASK BIT(1)
#define TWL4030_SIH_CTRL_COR_MASK BIT(2)
/*----------------------------------------------------------------------*/
/*
* GPIO Block Register offsets (use TWL4030_MODULE_GPIO)
*/
#define REG_GPIODATAIN1 0x0
#define REG_GPIODATAIN2 0x1
#define REG_GPIODATAIN3 0x2
#define REG_GPIODATADIR1 0x3
#define REG_GPIODATADIR2 0x4
#define REG_GPIODATADIR3 0x5
#define REG_GPIODATAOUT1 0x6
#define REG_GPIODATAOUT2 0x7
#define REG_GPIODATAOUT3 0x8
#define REG_CLEARGPIODATAOUT1 0x9
#define REG_CLEARGPIODATAOUT2 0xA
#define REG_CLEARGPIODATAOUT3 0xB
#define REG_SETGPIODATAOUT1 0xC
#define REG_SETGPIODATAOUT2 0xD
#define REG_SETGPIODATAOUT3 0xE
#define REG_GPIO_DEBEN1 0xF
#define REG_GPIO_DEBEN2 0x10
#define REG_GPIO_DEBEN3 0x11
#define REG_GPIO_CTRL 0x12
#define REG_GPIOPUPDCTR1 0x13
#define REG_GPIOPUPDCTR2 0x14
#define REG_GPIOPUPDCTR3 0x15
#define REG_GPIOPUPDCTR4 0x16
#define REG_GPIOPUPDCTR5 0x17
#define REG_GPIO_ISR1A 0x19
#define REG_GPIO_ISR2A 0x1A
#define REG_GPIO_ISR3A 0x1B
#define REG_GPIO_IMR1A 0x1C
#define REG_GPIO_IMR2A 0x1D
#define REG_GPIO_IMR3A 0x1E
#define REG_GPIO_ISR1B 0x1F
#define REG_GPIO_ISR2B 0x20
#define REG_GPIO_ISR3B 0x21
#define REG_GPIO_IMR1B 0x22
#define REG_GPIO_IMR2B 0x23
#define REG_GPIO_IMR3B 0x24
#define REG_GPIO_EDR1 0x28
#define REG_GPIO_EDR2 0x29
#define REG_GPIO_EDR3 0x2A
#define REG_GPIO_EDR4 0x2B
#define REG_GPIO_EDR5 0x2C
#define REG_GPIO_SIH_CTRL 0x2D
/* Up to 18 signals are available as GPIOs, when their
* pins are not assigned to another use (such as ULPI/USB).
*/
#define TWL4030_GPIO_MAX 18
/*----------------------------------------------------------------------*/
/*
* Keypad register offsets (use TWL4030_MODULE_KEYPAD)
* ... SIH/interrupt only
*/
#define TWL4030_KEYPAD_KEYP_ISR1 0x11
#define TWL4030_KEYPAD_KEYP_IMR1 0x12
#define TWL4030_KEYPAD_KEYP_ISR2 0x13
#define TWL4030_KEYPAD_KEYP_IMR2 0x14
#define TWL4030_KEYPAD_KEYP_SIR 0x15 /* test register */
#define TWL4030_KEYPAD_KEYP_EDR 0x16
#define TWL4030_KEYPAD_KEYP_SIH_CTRL 0x17
/*----------------------------------------------------------------------*/
/*
* Multichannel ADC register offsets (use TWL4030_MODULE_MADC)
* ... SIH/interrupt only
*/
#define TWL4030_MADC_ISR1 0x61
#define TWL4030_MADC_IMR1 0x62
#define TWL4030_MADC_ISR2 0x63
#define TWL4030_MADC_IMR2 0x64
#define TWL4030_MADC_SIR 0x65 /* test register */
#define TWL4030_MADC_EDR 0x66
#define TWL4030_MADC_SIH_CTRL 0x67
/*----------------------------------------------------------------------*/
/*
* Battery charger register offsets (use TWL4030_MODULE_INTERRUPTS)
*/
#define TWL4030_INTERRUPTS_BCIISR1A 0x0
#define TWL4030_INTERRUPTS_BCIISR2A 0x1
#define TWL4030_INTERRUPTS_BCIIMR1A 0x2
#define TWL4030_INTERRUPTS_BCIIMR2A 0x3
#define TWL4030_INTERRUPTS_BCIISR1B 0x4
#define TWL4030_INTERRUPTS_BCIISR2B 0x5
#define TWL4030_INTERRUPTS_BCIIMR1B 0x6
#define TWL4030_INTERRUPTS_BCIIMR2B 0x7
#define TWL4030_INTERRUPTS_BCISIR1 0x8 /* test register */
#define TWL4030_INTERRUPTS_BCISIR2 0x9 /* test register */
#define TWL4030_INTERRUPTS_BCIEDR1 0xa
#define TWL4030_INTERRUPTS_BCIEDR2 0xb
#define TWL4030_INTERRUPTS_BCIEDR3 0xc
#define TWL4030_INTERRUPTS_BCISIHCTRL 0xd
/*----------------------------------------------------------------------*/
/*
* Power Interrupt block register offsets (use TWL4030_MODULE_INT)
*/
#define TWL4030_INT_PWR_ISR1 0x0
#define TWL4030_INT_PWR_IMR1 0x1
#define TWL4030_INT_PWR_ISR2 0x2
#define TWL4030_INT_PWR_IMR2 0x3
#define TWL4030_INT_PWR_SIR 0x4 /* test register */
#define TWL4030_INT_PWR_EDR1 0x5
#define TWL4030_INT_PWR_EDR2 0x6
#define TWL4030_INT_PWR_SIH_CTRL 0x7
/*----------------------------------------------------------------------*/
struct twl4030_bci_platform_data {
int *battery_tmp_tbl;
unsigned int tblsize;
};
/* TWL4030_GPIO_MAX (18) GPIOs, with interrupts */
struct twl4030_gpio_platform_data {
int gpio_base;
unsigned irq_base, irq_end;
/* For gpio-N, bit (1 << N) in "pullups" is set if that pullup
* should be enabled. Else, if that bit is set in "pulldowns",
* that pulldown is enabled. Don't waste power by letting any
* digital inputs float...
*/
u32 pullups;
u32 pulldowns;
int (*setup)(struct device *dev,
unsigned gpio, unsigned ngpio);
int (*teardown)(struct device *dev,
unsigned gpio, unsigned ngpio);
};
struct twl4030_madc_platform_data {
int irq_line;
};
struct twl4030_keypad_data {
int rows;
int cols;
int *keymap;
int irq;
unsigned int keymapsize;
unsigned int rep:1;
};
enum twl4030_usb_mode {
T2_USB_MODE_ULPI = 1,
T2_USB_MODE_CEA2011_3PIN = 2,
};
struct twl4030_usb_data {
enum twl4030_usb_mode usb_mode;
};
struct twl4030_platform_data {
unsigned irq_base, irq_end;
struct twl4030_bci_platform_data *bci;
struct twl4030_gpio_platform_data *gpio;
struct twl4030_madc_platform_data *madc;
struct twl4030_keypad_data *keypad;
struct twl4030_usb_data *usb;
/* REVISIT more to come ... _nothing_ should be hard-wired */
};
/*----------------------------------------------------------------------*/
/*
* 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
#define TWL4030_VAUX1_DEV_GRP 0x17
#define TWL4030_VAUX1_DEDICATED 0x1A
#define TWL4030_VAUX2_DEV_GRP 0x1B
#define TWL4030_VAUX2_DEDICATED 0x1E
#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))
#define TWL4030_GPIO_IS_ENABLE 1
/*
* Exported TWL4030 GPIO APIs
*
* WARNING -- use standard GPIO and IRQ calls instead; these will vanish.
*/
int twl4030_get_gpio_datain(int gpio);
int twl4030_request_gpio(int gpio);
int twl4030_set_gpio_debounce(int gpio, int enable);
int twl4030_free_gpio(int gpio);
#if defined(CONFIG_TWL4030_BCI_BATTERY) || \
defined(CONFIG_TWL4030_BCI_BATTERY_MODULE)
extern int twl4030charger_usb_en(int enable);
#else
static inline int twl4030charger_usb_en(int enable) { return 0; }
#endif
#endif /* End of __TWL4030_H */
#ifndef __LINUX_PMIC_DA903X_H
#define __LINUX_PMIC_DA903X_H
/* Unified sub device IDs for DA9030/DA9034 */
enum {
DA9030_ID_LED_1,
DA9030_ID_LED_2,
DA9030_ID_LED_3,
DA9030_ID_LED_4,
DA9030_ID_LED_PC,
DA9030_ID_VIBRA,
DA9030_ID_WLED,
DA9030_ID_BUCK1,
DA9030_ID_BUCK2,
DA9030_ID_LDO1,
DA9030_ID_LDO2,
DA9030_ID_LDO3,
DA9030_ID_LDO4,
DA9030_ID_LDO5,
DA9030_ID_LDO6,
DA9030_ID_LDO7,
DA9030_ID_LDO8,
DA9030_ID_LDO9,
DA9030_ID_LDO10,
DA9030_ID_LDO11,
DA9030_ID_LDO12,
DA9030_ID_LDO13,
DA9030_ID_LDO14,
DA9030_ID_LDO15,
DA9030_ID_LDO16,
DA9030_ID_LDO17,
DA9030_ID_LDO18,
DA9030_ID_LDO19,
DA9030_ID_LDO_INT, /* LDO Internal */
DA9034_ID_LED_1,
DA9034_ID_LED_2,
DA9034_ID_VIBRA,
DA9034_ID_WLED,
DA9034_ID_TOUCH,
DA9034_ID_BUCK1,
DA9034_ID_BUCK2,
DA9034_ID_LDO1,
DA9034_ID_LDO2,
DA9034_ID_LDO3,
DA9034_ID_LDO4,
DA9034_ID_LDO5,
DA9034_ID_LDO6,
DA9034_ID_LDO7,
DA9034_ID_LDO8,
DA9034_ID_LDO9,
DA9034_ID_LDO10,
DA9034_ID_LDO11,
DA9034_ID_LDO12,
DA9034_ID_LDO13,
DA9034_ID_LDO14,
DA9034_ID_LDO15,
};
/*
* DA9030/DA9034 LEDs sub-devices uses generic "struct led_info"
* as the platform_data
*/
/* DA9030 flags for "struct led_info"
*/
#define DA9030_LED_RATE_ON (0 << 5)
#define DA9030_LED_RATE_052S (1 << 5)
#define DA9030_LED_DUTY_1_16 (0 << 3)
#define DA9030_LED_DUTY_1_8 (1 << 3)
#define DA9030_LED_DUTY_1_4 (2 << 3)
#define DA9030_LED_DUTY_1_2 (3 << 3)
#define DA9030_VIBRA_MODE_1P3V (0 << 1)
#define DA9030_VIBRA_MODE_2P7V (1 << 1)
#define DA9030_VIBRA_FREQ_1HZ (0 << 2)
#define DA9030_VIBRA_FREQ_2HZ (1 << 2)
#define DA9030_VIBRA_FREQ_4HZ (2 << 2)
#define DA9030_VIBRA_FREQ_8HZ (3 << 2)
#define DA9030_VIBRA_DUTY_ON (0 << 4)
#define DA9030_VIBRA_DUTY_75P (1 << 4)
#define DA9030_VIBRA_DUTY_50P (2 << 4)
#define DA9030_VIBRA_DUTY_25P (3 << 4)
/* DA9034 flags for "struct led_info" */
#define DA9034_LED_RAMP (1 << 7)
/* DA9034 touch screen platform data */
struct da9034_touch_pdata {
int interval_ms; /* sampling interval while pen down */
int x_inverted;
int y_inverted;
};
struct da903x_subdev_info {
int id;
const char *name;
void *platform_data;
};
struct da903x_platform_data {
int num_subdevs;
struct da903x_subdev_info *subdevs;
};
/* bit definitions for DA9030 events */
#define DA9030_EVENT_ONKEY (1 << 0)
#define DA9030_EVENT_PWREN (1 << 1)
#define DA9030_EVENT_EXTON (1 << 2)
#define DA9030_EVENT_CHDET (1 << 3)
#define DA9030_EVENT_TBAT (1 << 4)
#define DA9030_EVENT_VBATMON (1 << 5)
#define DA9030_EVENT_VBATMON_TXON (1 << 6)
#define DA9030_EVENT_CHIOVER (1 << 7)
#define DA9030_EVENT_TCTO (1 << 8)
#define DA9030_EVENT_CCTO (1 << 9)
#define DA9030_EVENT_ADC_READY (1 << 10)
#define DA9030_EVENT_VBUS_4P4 (1 << 11)
#define DA9030_EVENT_VBUS_4P0 (1 << 12)
#define DA9030_EVENT_SESS_VALID (1 << 13)
#define DA9030_EVENT_SRP_DETECT (1 << 14)
#define DA9030_EVENT_WATCHDOG (1 << 15)
#define DA9030_EVENT_LDO15 (1 << 16)
#define DA9030_EVENT_LDO16 (1 << 17)
#define DA9030_EVENT_LDO17 (1 << 18)
#define DA9030_EVENT_LDO18 (1 << 19)
#define DA9030_EVENT_LDO19 (1 << 20)
#define DA9030_EVENT_BUCK2 (1 << 21)
/* bit definitions for DA9034 events */
#define DA9034_EVENT_ONKEY (1 << 0)
#define DA9034_EVENT_EXTON (1 << 2)
#define DA9034_EVENT_CHDET (1 << 3)
#define DA9034_EVENT_TBAT (1 << 4)
#define DA9034_EVENT_VBATMON (1 << 5)
#define DA9034_EVENT_REV_IOVER (1 << 6)
#define DA9034_EVENT_CH_IOVER (1 << 7)
#define DA9034_EVENT_CH_TCTO (1 << 8)
#define DA9034_EVENT_CH_CCTO (1 << 9)
#define DA9034_EVENT_USB_DEV (1 << 10)
#define DA9034_EVENT_OTGCP_IOVER (1 << 11)
#define DA9034_EVENT_VBUS_4P55 (1 << 12)
#define DA9034_EVENT_VBUS_3P8 (1 << 13)
#define DA9034_EVENT_SESS_1P8 (1 << 14)
#define DA9034_EVENT_SRP_READY (1 << 15)
#define DA9034_EVENT_ADC_MAN (1 << 16)
#define DA9034_EVENT_ADC_AUTO4 (1 << 17)
#define DA9034_EVENT_ADC_AUTO5 (1 << 18)
#define DA9034_EVENT_ADC_AUTO6 (1 << 19)
#define DA9034_EVENT_PEN_DOWN (1 << 20)
#define DA9034_EVENT_TSI_READY (1 << 21)
#define DA9034_EVENT_UART_TX (1 << 22)
#define DA9034_EVENT_UART_RX (1 << 23)
#define DA9034_EVENT_HEADSET (1 << 25)
#define DA9034_EVENT_HOOKSWITCH (1 << 26)
#define DA9034_EVENT_WATCHDOG (1 << 27)
extern int da903x_register_notifier(struct device *dev,
struct notifier_block *nb, unsigned int events);
extern int da903x_unregister_notifier(struct device *dev,
struct notifier_block *nb, unsigned int events);
/* Status Query Interface */
#define DA9030_STATUS_ONKEY (1 << 0)
#define DA9030_STATUS_PWREN1 (1 << 1)
#define DA9030_STATUS_EXTON (1 << 2)
#define DA9030_STATUS_CHDET (1 << 3)
#define DA9030_STATUS_TBAT (1 << 4)
#define DA9030_STATUS_VBATMON (1 << 5)
#define DA9030_STATUS_VBATMON_TXON (1 << 6)
#define DA9030_STATUS_MCLKDET (1 << 7)
#define DA9034_STATUS_ONKEY (1 << 0)
#define DA9034_STATUS_EXTON (1 << 2)
#define DA9034_STATUS_CHDET (1 << 3)
#define DA9034_STATUS_TBAT (1 << 4)
#define DA9034_STATUS_VBATMON (1 << 5)
#define DA9034_STATUS_PEN_DOWN (1 << 6)
#define DA9034_STATUS_MCLKDET (1 << 7)
#define DA9034_STATUS_USB_DEV (1 << 8)
#define DA9034_STATUS_HEADSET (1 << 9)
#define DA9034_STATUS_HOOKSWITCH (1 << 10)
#define DA9034_STATUS_REMCON (1 << 11)
#define DA9034_STATUS_VBUS_VALID_4P55 (1 << 12)
#define DA9034_STATUS_VBUS_VALID_3P8 (1 << 13)
#define DA9034_STATUS_SESS_VALID_1P8 (1 << 14)
#define DA9034_STATUS_SRP_READY (1 << 15)
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
*/
extern int da903x_write(struct device *dev, int reg, uint8_t val);
extern int da903x_read(struct device *dev, int reg, 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);
#endif /* __LINUX_PMIC_DA903X_H */
...@@ -15,8 +15,6 @@ ...@@ -15,8 +15,6 @@
#include <linux/mfd/tmio.h> #include <linux/mfd/tmio.h>
struct t7l66xb_platform_data { struct t7l66xb_platform_data {
int (*enable_clk32k)(struct platform_device *dev);
void (*disable_clk32k)(struct platform_device *dev);
int (*enable)(struct platform_device *dev); int (*enable)(struct platform_device *dev);
int (*disable)(struct platform_device *dev); int (*disable)(struct platform_device *dev);
int (*suspend)(struct platform_device *dev); int (*suspend)(struct platform_device *dev);
......
...@@ -11,9 +11,6 @@ ...@@ -11,9 +11,6 @@
#define MFD_TC6387XB_H #define MFD_TC6387XB_H
struct tc6387xb_platform_data { struct tc6387xb_platform_data {
int (*enable_clk32k)(struct platform_device *dev);
void (*disable_clk32k)(struct platform_device *dev);
int (*enable)(struct platform_device *dev); int (*enable)(struct platform_device *dev);
int (*disable)(struct platform_device *dev); int (*disable)(struct platform_device *dev);
int (*suspend)(struct platform_device *dev); int (*suspend)(struct platform_device *dev);
......
...@@ -17,12 +17,12 @@ ...@@ -17,12 +17,12 @@
#ifndef MFD_TC6393XB_H #ifndef MFD_TC6393XB_H
#define MFD_TC6393XB_H #define MFD_TC6393XB_H
#include <linux/fb.h>
/* Also one should provide the CK3P6MI clock */ /* Also one should provide the CK3P6MI clock */
struct tc6393xb_platform_data { struct tc6393xb_platform_data {
u16 scr_pll2cr; /* PLL2 Control */ u16 scr_pll2cr; /* PLL2 Control */
u16 scr_gper; /* GP Enable */ u16 scr_gper; /* GP Enable */
u32 scr_gpo_doecr; /* GPO Data OE Control */
u32 scr_gpo_dsr; /* GPO Data Set */
int (*enable)(struct platform_device *dev); int (*enable)(struct platform_device *dev);
int (*disable)(struct platform_device *dev); int (*disable)(struct platform_device *dev);
...@@ -31,15 +31,28 @@ struct tc6393xb_platform_data { ...@@ -31,15 +31,28 @@ struct tc6393xb_platform_data {
int irq_base; /* base for subdevice irqs */ int irq_base; /* base for subdevice irqs */
int gpio_base; int gpio_base;
int (*setup)(struct platform_device *dev);
void (*teardown)(struct platform_device *dev);
struct tmio_nand_data *nand_data; struct tmio_nand_data *nand_data;
struct tmio_fb_data *fb_data;
unsigned resume_restore : 1; /* make special actions
to preserve the state
on suspend/resume */
}; };
extern int tc6393xb_lcd_mode(struct platform_device *fb,
const struct fb_videomode *mode);
extern int tc6393xb_lcd_set_power(struct platform_device *fb, bool on);
/* /*
* Relative to irq_base * Relative to irq_base
*/ */
#define IRQ_TC6393_NAND 0 #define IRQ_TC6393_NAND 0
#define IRQ_TC6393_MMC 1 #define IRQ_TC6393_MMC 1
#define IRQ_TC6393_OHCI 2
#define IRQ_TC6393_FB 4
#define TC6393XB_NR_IRQS 8 #define TC6393XB_NR_IRQS 8
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment