aboutsummaryrefslogtreecommitdiff
path: root/drivers/mfd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/88pm800.c265
-rw-r--r--drivers/mfd/88pm805.c40
-rw-r--r--drivers/mfd/88pm80x.c65
-rw-r--r--drivers/mfd/88pm860x-core.c134
-rw-r--r--drivers/mfd/Kconfig1589
-rw-r--r--drivers/mfd/Makefile43
-rw-r--r--drivers/mfd/aat2870-core.c29
-rw-r--r--drivers/mfd/ab3100-core.c44
-rw-r--r--drivers/mfd/ab3100-otp.c28
-rw-r--r--drivers/mfd/ab8500-core.c820
-rw-r--r--drivers/mfd/ab8500-debugfs.c2728
-rw-r--r--drivers/mfd/ab8500-gpadc.c659
-rw-r--r--drivers/mfd/ab8500-sysctrl.c185
-rw-r--r--drivers/mfd/abx500-core.c10
-rw-r--r--drivers/mfd/adp5520.c37
-rw-r--r--drivers/mfd/arizona-core.c603
-rw-r--r--drivers/mfd/arizona-i2c.c23
-rw-r--r--drivers/mfd/arizona-irq.c134
-rw-r--r--drivers/mfd/arizona-spi.c19
-rw-r--r--drivers/mfd/arizona.h17
-rw-r--r--drivers/mfd/as3711.c237
-rw-r--r--drivers/mfd/as3722.c453
-rw-r--r--drivers/mfd/asic3.c24
-rw-r--r--drivers/mfd/axp20x.c258
-rw-r--r--drivers/mfd/bcm590xx.c132
-rw-r--r--drivers/mfd/cros_ec.c194
-rw-r--r--drivers/mfd/cros_ec_i2c.c201
-rw-r--r--drivers/mfd/cros_ec_spi.c440
-rw-r--r--drivers/mfd/cs5535-mfd.c15
-rw-r--r--drivers/mfd/da903x.c31
-rw-r--r--drivers/mfd/da9052-core.c282
-rw-r--r--drivers/mfd/da9052-i2c.c83
-rw-r--r--drivers/mfd/da9052-irq.c288
-rw-r--r--drivers/mfd/da9052-spi.c11
-rw-r--r--drivers/mfd/da9055-core.c15
-rw-r--r--drivers/mfd/da9055-i2c.c24
-rw-r--r--drivers/mfd/da9063-core.c188
-rw-r--r--drivers/mfd/da9063-i2c.c182
-rw-r--r--drivers/mfd/da9063-irq.c193
-rw-r--r--drivers/mfd/davinci_voicecodec.c93
-rw-r--r--drivers/mfd/db8500-prcmu.c604
-rw-r--r--drivers/mfd/dbx500-prcmu-regs.h204
-rw-r--r--drivers/mfd/dm355evm_msp.c4
-rw-r--r--drivers/mfd/ezx-pcap.c37
-rw-r--r--drivers/mfd/htc-egpio.c15
-rw-r--r--drivers/mfd/htc-i2cpld.c37
-rw-r--r--drivers/mfd/htc-pasic3.c21
-rw-r--r--drivers/mfd/intel_msic.c41
-rw-r--r--drivers/mfd/ipaq-micro.c482
-rw-r--r--drivers/mfd/janz-cmodio.c25
-rw-r--r--drivers/mfd/jz4740-adc.c35
-rw-r--r--drivers/mfd/kempld-core.c771
-rw-r--r--drivers/mfd/lm3533-core.c34
-rw-r--r--drivers/mfd/lp3943.c167
-rw-r--r--drivers/mfd/lp8788.c8
-rw-r--r--drivers/mfd/lpc_ich.c297
-rw-r--r--drivers/mfd/lpc_sch.c159
-rw-r--r--drivers/mfd/max14577.c478
-rw-r--r--drivers/mfd/max77686.c55
-rw-r--r--drivers/mfd/max77693-irq.c3
-rw-r--r--drivers/mfd/max77693.c65
-rw-r--r--drivers/mfd/max8907.c9
-rw-r--r--drivers/mfd/max8925-core.c155
-rw-r--r--drivers/mfd/max8925-i2c.c57
-rw-r--r--drivers/mfd/max8997.c101
-rw-r--r--drivers/mfd/max8998-irq.c65
-rw-r--r--drivers/mfd/max8998.c82
-rw-r--r--drivers/mfd/mc13xxx-core.c160
-rw-r--r--drivers/mfd/mc13xxx-i2c.c43
-rw-r--r--drivers/mfd/mc13xxx-spi.c59
-rw-r--r--drivers/mfd/mc13xxx.h24
-rw-r--r--drivers/mfd/mcp-sa11x0.c6
-rw-r--r--drivers/mfd/menelaus.c47
-rw-r--r--drivers/mfd/mfd-core.c71
-rw-r--r--drivers/mfd/omap-usb-host.c770
-rw-r--r--drivers/mfd/omap-usb-tll.c356
-rw-r--r--drivers/mfd/omap-usb.h3
-rw-r--r--drivers/mfd/palmas.c329
-rw-r--r--drivers/mfd/pcf50633-adc.c10
-rw-r--r--drivers/mfd/pcf50633-core.c15
-rw-r--r--drivers/mfd/pm8921-core.c397
-rw-r--r--drivers/mfd/pm8xxx-irq.c371
-rw-r--r--drivers/mfd/rc5t583-irq.c3
-rw-r--r--drivers/mfd/rc5t583.c10
-rw-r--r--drivers/mfd/rdc321x-southbridge.c13
-rw-r--r--drivers/mfd/retu-mfd.c327
-rw-r--r--drivers/mfd/rtl8411.c509
-rw-r--r--drivers/mfd/rts5209.c281
-rw-r--r--drivers/mfd/rts5227.c301
-rw-r--r--drivers/mfd/rts5229.c277
-rw-r--r--drivers/mfd/rts5249.c353
-rw-r--r--drivers/mfd/rtsx_pcr.c1368
-rw-r--r--drivers/mfd/rtsx_pcr.h75
-rw-r--r--drivers/mfd/rtsx_usb.c766
-rw-r--r--drivers/mfd/sec-core.c307
-rw-r--r--drivers/mfd/sec-irq.c217
-rw-r--r--drivers/mfd/si476x-cmd.c1555
-rw-r--r--drivers/mfd/si476x-i2c.c886
-rw-r--r--drivers/mfd/si476x-prop.c241
-rw-r--r--drivers/mfd/sm501.c28
-rw-r--r--drivers/mfd/smsc-ece1099.c1
-rw-r--r--drivers/mfd/ssbi.c339
-rw-r--r--drivers/mfd/sta2x11-mfd.c567
-rw-r--r--drivers/mfd/stmpe-i2c.c45
-rw-r--r--drivers/mfd/stmpe-spi.c8
-rw-r--r--drivers/mfd/stmpe.c351
-rw-r--r--drivers/mfd/stmpe.h53
-rw-r--r--drivers/mfd/stw481x.c256
-rw-r--r--drivers/mfd/sun6i-prcm.c134
-rw-r--r--drivers/mfd/syscon.c90
-rw-r--r--drivers/mfd/t7l66xb.c9
-rw-r--r--drivers/mfd/tc3589x.c169
-rw-r--r--drivers/mfd/tc6387xb.c15
-rw-r--r--drivers/mfd/tc6393xb.c21
-rw-r--r--drivers/mfd/ti-ssp.c466
-rw-r--r--drivers/mfd/ti_am335x_tscadc.c379
-rw-r--r--drivers/mfd/timberdale.c109
-rw-r--r--drivers/mfd/tps6105x.c10
-rw-r--r--drivers/mfd/tps65010.c24
-rw-r--r--drivers/mfd/tps6507x.c33
-rw-r--r--drivers/mfd/tps65090.c401
-rw-r--r--drivers/mfd/tps65217.c26
-rw-r--r--drivers/mfd/tps65218.c283
-rw-r--r--drivers/mfd/tps6586x.c266
-rw-r--r--drivers/mfd/tps65910-irq.c260
-rw-r--r--drivers/mfd/tps65910.c256
-rw-r--r--drivers/mfd/tps65911-comparator.c6
-rw-r--r--drivers/mfd/tps65912-core.c8
-rw-r--r--drivers/mfd/tps65912-i2c.c3
-rw-r--r--drivers/mfd/tps65912-irq.c1
-rw-r--r--drivers/mfd/tps65912-spi.c9
-rw-r--r--drivers/mfd/tps80031.c573
-rw-r--r--drivers/mfd/twl-core.c939
-rw-r--r--drivers/mfd/twl4030-audio.c15
-rw-r--r--drivers/mfd/twl4030-irq.c20
-rw-r--r--drivers/mfd/twl4030-madc.c815
-rw-r--r--drivers/mfd/twl4030-power.c544
-rw-r--r--drivers/mfd/twl6030-irq.c386
-rw-r--r--drivers/mfd/twl6040-irq.c205
-rw-r--r--drivers/mfd/twl6040.c (renamed from drivers/mfd/twl6040-core.c)404
-rw-r--r--drivers/mfd/ucb1400_core.c7
-rw-r--r--drivers/mfd/ucb1x00-core.c39
-rw-r--r--drivers/mfd/vexpress-sysreg.c276
-rw-r--r--drivers/mfd/viperboard.c137
-rw-r--r--drivers/mfd/vx855.c10
-rw-r--r--drivers/mfd/wl1273-core.c12
-rw-r--r--drivers/mfd/wm5102-tables.c765
-rw-r--r--drivers/mfd/wm5110-tables.c439
-rw-r--r--drivers/mfd/wm831x-core.c16
-rw-r--r--drivers/mfd/wm831x-i2c.c8
-rw-r--r--drivers/mfd/wm831x-irq.c2
-rw-r--r--drivers/mfd/wm831x-spi.c19
-rw-r--r--drivers/mfd/wm8350-core.c1
-rw-r--r--drivers/mfd/wm8350-i2c.c3
-rw-r--r--drivers/mfd/wm8350-irq.c1
-rw-r--r--drivers/mfd/wm8400-core.c24
-rw-r--r--drivers/mfd/wm8994-core.c312
-rw-r--r--drivers/mfd/wm8994-irq.c102
-rw-r--r--drivers/mfd/wm8997-tables.c1527
159 files changed, 28489 insertions, 8715 deletions
diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c
index ce229ea933d..841717a2842 100644
--- a/drivers/mfd/88pm800.c
+++ b/drivers/mfd/88pm800.c
@@ -22,13 +22,12 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/mfd/88pm80x.h>
#include <linux/slab.h>
-#define PM800_CHIP_ID (0x00)
-
/* Interrupt Registers */
#define PM800_INT_STATUS1 (0x05)
#define PM800_ONKEY_INT_STS1 (1 << 0)
@@ -113,20 +112,11 @@ enum {
PM800_MAX_IRQ,
};
-enum {
- /* Procida */
- PM800_CHIP_A0 = 0x60,
- PM800_CHIP_A1 = 0x61,
- PM800_CHIP_B0 = 0x62,
- PM800_CHIP_C0 = 0x63,
- PM800_CHIP_END = PM800_CHIP_C0,
-
- /* Make sure to update this to the last stepping */
- PM8XXX_CHIP_END = PM800_CHIP_END
-};
+/* PM800: generation identification number */
+#define PM800_CHIP_GEN_ID_NUM 0x3
static const struct i2c_device_id pm80x_id_table[] = {
- {"88PM800", CHIP_PM800},
+ {"88PM800", 0},
{} /* NULL terminated */
};
MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
@@ -158,7 +148,7 @@ static struct resource onkey_resources[] = {
},
};
-static struct mfd_cell onkey_devs[] = {
+static const struct mfd_cell onkey_devs[] = {
{
.name = "88pm80x-onkey",
.num_resources = 1,
@@ -167,6 +157,13 @@ static struct mfd_cell onkey_devs[] = {
},
};
+static const struct mfd_cell regulator_devs[] = {
+ {
+ .name = "88pm80x-regulator",
+ .id = -1,
+ },
+};
+
static const struct regmap_irq pm800_irqs[] = {
/* INT0 */
[PM800_IRQ_ONKEY] = {
@@ -248,7 +245,7 @@ static const struct regmap_irq pm800_irqs[] = {
},
};
-static int __devinit device_gpadc_init(struct pm80x_chip *chip,
+static int device_gpadc_init(struct pm80x_chip *chip,
struct pm80x_platform_data *pdata)
{
struct pm80x_subchip *subchip = chip->subchip;
@@ -315,10 +312,61 @@ out:
return ret;
}
-static int __devinit device_irq_init_800(struct pm80x_chip *chip)
+static int device_onkey_init(struct pm80x_chip *chip,
+ struct pm80x_platform_data *pdata)
+{
+ int ret;
+
+ ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
+ ARRAY_SIZE(onkey_devs), &onkey_resources[0], 0,
+ NULL);
+ if (ret) {
+ dev_err(chip->dev, "Failed to add onkey subdev\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int device_rtc_init(struct pm80x_chip *chip,
+ struct pm80x_platform_data *pdata)
+{
+ int ret;
+
+ if (pdata) {
+ rtc_devs[0].platform_data = pdata->rtc;
+ rtc_devs[0].pdata_size =
+ pdata->rtc ? sizeof(struct pm80x_rtc_pdata) : 0;
+ }
+ ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
+ ARRAY_SIZE(rtc_devs), NULL, 0, NULL);
+ if (ret) {
+ dev_err(chip->dev, "Failed to add rtc subdev\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int device_regulator_init(struct pm80x_chip *chip,
+ struct pm80x_platform_data *pdata)
+{
+ int ret;
+
+ ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
+ ARRAY_SIZE(regulator_devs), NULL, 0, NULL);
+ if (ret) {
+ dev_err(chip->dev, "Failed to add regulator subdev\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int device_irq_init_800(struct pm80x_chip *chip)
{
struct regmap *map = chip->regmap;
- unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+ unsigned long flags = IRQF_ONESHOT;
int data, mask, ret = -EINVAL;
if (!map || !chip->irq) {
@@ -362,6 +410,7 @@ static struct regmap_irq_chip pm800_irq_chip = {
.status_base = PM800_INT_STATUS1,
.mask_base = PM800_INT_ENA_1,
.ack_base = PM800_INT_STATUS1,
+ .mask_invert = 1,
};
static int pm800_pages_init(struct pm80x_chip *chip)
@@ -369,77 +418,72 @@ static int pm800_pages_init(struct pm80x_chip *chip)
struct pm80x_subchip *subchip;
struct i2c_client *client = chip->client;
+ int ret = 0;
+
subchip = chip->subchip;
- /* PM800 block power: i2c addr 0x31 */
- if (subchip->power_page_addr) {
- subchip->power_page =
- i2c_new_dummy(client->adapter, subchip->power_page_addr);
- subchip->regmap_power =
- devm_regmap_init_i2c(subchip->power_page,
- &pm80x_regmap_config);
- i2c_set_clientdata(subchip->power_page, chip);
- } else
- dev_info(chip->dev,
- "PM800 block power 0x31: No power_page_addr\n");
-
- /* PM800 block GPADC: i2c addr 0x32 */
- if (subchip->gpadc_page_addr) {
- subchip->gpadc_page = i2c_new_dummy(client->adapter,
- subchip->gpadc_page_addr);
- subchip->regmap_gpadc =
- devm_regmap_init_i2c(subchip->gpadc_page,
- &pm80x_regmap_config);
- i2c_set_clientdata(subchip->gpadc_page, chip);
- } else
- dev_info(chip->dev,
- "PM800 block GPADC 0x32: No gpadc_page_addr\n");
+ if (!subchip || !subchip->power_page_addr || !subchip->gpadc_page_addr)
+ return -ENODEV;
+
+ /* PM800 block power page */
+ subchip->power_page = i2c_new_dummy(client->adapter,
+ subchip->power_page_addr);
+ if (subchip->power_page == NULL) {
+ ret = -ENODEV;
+ goto out;
+ }
- return 0;
+ subchip->regmap_power = devm_regmap_init_i2c(subchip->power_page,
+ &pm80x_regmap_config);
+ if (IS_ERR(subchip->regmap_power)) {
+ ret = PTR_ERR(subchip->regmap_power);
+ dev_err(chip->dev,
+ "Failed to allocate regmap_power: %d\n", ret);
+ goto out;
+ }
+
+ i2c_set_clientdata(subchip->power_page, chip);
+
+ /* PM800 block GPADC */
+ subchip->gpadc_page = i2c_new_dummy(client->adapter,
+ subchip->gpadc_page_addr);
+ if (subchip->gpadc_page == NULL) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ subchip->regmap_gpadc = devm_regmap_init_i2c(subchip->gpadc_page,
+ &pm80x_regmap_config);
+ if (IS_ERR(subchip->regmap_gpadc)) {
+ ret = PTR_ERR(subchip->regmap_gpadc);
+ dev_err(chip->dev,
+ "Failed to allocate regmap_gpadc: %d\n", ret);
+ goto out;
+ }
+ i2c_set_clientdata(subchip->gpadc_page, chip);
+
+out:
+ return ret;
}
static void pm800_pages_exit(struct pm80x_chip *chip)
{
struct pm80x_subchip *subchip;
- regmap_exit(chip->regmap);
- i2c_unregister_device(chip->client);
-
subchip = chip->subchip;
- if (subchip->power_page) {
- regmap_exit(subchip->regmap_power);
+
+ if (subchip && subchip->power_page)
i2c_unregister_device(subchip->power_page);
- }
- if (subchip->gpadc_page) {
- regmap_exit(subchip->regmap_gpadc);
+
+ if (subchip && subchip->gpadc_page)
i2c_unregister_device(subchip->gpadc_page);
- }
}
-static int __devinit device_800_init(struct pm80x_chip *chip,
+static int device_800_init(struct pm80x_chip *chip,
struct pm80x_platform_data *pdata)
{
- int ret, pmic_id;
+ int ret;
unsigned int val;
- ret = regmap_read(chip->regmap, PM800_CHIP_ID, &val);
- if (ret < 0) {
- dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
- goto out;
- }
-
- pmic_id = val & PM80X_VERSION_MASK;
-
- if ((pmic_id >= PM800_CHIP_A0) && (pmic_id <= PM800_CHIP_END)) {
- chip->version = val;
- dev_info(chip->dev,
- "88PM80x:Marvell 88PM800 (ID:0x%x) detected\n", val);
- } else {
- dev_err(chip->dev,
- "Failed to detect Marvell 88PM800:ChipID[0x%x]\n", val);
- ret = -EINVAL;
- goto out;
- }
-
/*
* alarm wake up bit will be clear in device_irq_init(),
* read before that
@@ -468,27 +512,22 @@ static int __devinit device_800_init(struct pm80x_chip *chip,
goto out;
}
- ret =
- mfd_add_devices(chip->dev, 0, &onkey_devs[0],
- ARRAY_SIZE(onkey_devs), &onkey_resources[0], 0,
- NULL);
- if (ret < 0) {
+ ret = device_onkey_init(chip, pdata);
+ if (ret) {
dev_err(chip->dev, "Failed to add onkey subdev\n");
goto out_dev;
- } else
- dev_info(chip->dev, "[%s]:Added mfd onkey_devs\n", __func__);
+ }
- if (pdata && pdata->rtc) {
- rtc_devs[0].platform_data = pdata->rtc;
- rtc_devs[0].pdata_size = sizeof(struct pm80x_rtc_pdata);
- ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
- ARRAY_SIZE(rtc_devs), NULL, 0, NULL);
- if (ret < 0) {
- dev_err(chip->dev, "Failed to add rtc subdev\n");
- goto out_dev;
- } else
- dev_info(chip->dev,
- "[%s]:Added mfd rtc_devs\n", __func__);
+ ret = device_rtc_init(chip, pdata);
+ if (ret) {
+ dev_err(chip->dev, "Failed to add rtc subdev\n");
+ goto out;
+ }
+
+ ret = device_regulator_init(chip, pdata);
+ if (ret) {
+ dev_err(chip->dev, "Failed to add regulators subdev\n");
+ goto out;
}
return 0;
@@ -499,15 +538,15 @@ out:
return ret;
}
-static int __devinit pm800_probe(struct i2c_client *client,
+static int pm800_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret = 0;
struct pm80x_chip *chip;
- struct pm80x_platform_data *pdata = client->dev.platform_data;
+ struct pm80x_platform_data *pdata = dev_get_platdata(&client->dev);
struct pm80x_subchip *subchip;
- ret = pm80x_init(client, id);
+ ret = pm80x_init(client);
if (ret) {
dev_err(&client->dev, "pm800_init fail\n");
goto out_init;
@@ -524,37 +563,37 @@ static int __devinit pm800_probe(struct i2c_client *client,
goto err_subchip_alloc;
}
- subchip->power_page_addr = pdata->power_page_addr;
- subchip->gpadc_page_addr = pdata->gpadc_page_addr;
+ /* pm800 has 2 addtional pages to support power and gpadc. */
+ subchip->power_page_addr = client->addr + 1;
+ subchip->gpadc_page_addr = client->addr + 2;
chip->subchip = subchip;
- ret = device_800_init(chip, pdata);
+ ret = pm800_pages_init(chip);
if (ret) {
- dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id);
- goto err_800_init;
+ dev_err(&client->dev, "pm800_pages_init failed!\n");
+ goto err_device_init;
}
- ret = pm800_pages_init(chip);
+ ret = device_800_init(chip, pdata);
if (ret) {
- dev_err(&client->dev, "pm800_pages_init failed!\n");
- goto err_page_init;
+ dev_err(chip->dev, "Failed to initialize 88pm800 devices\n");
+ goto err_device_init;
}
- if (pdata->plat_config)
+ if (pdata && pdata->plat_config)
pdata->plat_config(chip, pdata);
-err_page_init:
- mfd_remove_devices(chip->dev);
- device_irq_exit_800(chip);
-err_800_init:
- devm_kfree(&client->dev, subchip);
+ return 0;
+
+err_device_init:
+ pm800_pages_exit(chip);
err_subchip_alloc:
- pm80x_deinit(client);
+ pm80x_deinit();
out_init:
return ret;
}
-static int __devexit pm800_remove(struct i2c_client *client)
+static int pm800_remove(struct i2c_client *client)
{
struct pm80x_chip *chip = i2c_get_clientdata(client);
@@ -562,21 +601,19 @@ static int __devexit pm800_remove(struct i2c_client *client)
device_irq_exit_800(chip);
pm800_pages_exit(chip);
- devm_kfree(&client->dev, chip->subchip);
-
- pm80x_deinit(client);
+ pm80x_deinit();
return 0;
}
static struct i2c_driver pm800_driver = {
.driver = {
- .name = "88PM80X",
+ .name = "88PM800",
.owner = THIS_MODULE,
.pm = &pm80x_pm_ops,
},
.probe = pm800_probe,
- .remove = __devexit_p(pm800_remove),
+ .remove = pm800_remove,
.id_table = pm80x_id_table,
};
diff --git a/drivers/mfd/88pm805.c b/drivers/mfd/88pm805.c
index c20a31136f0..64751c2a1ac 100644
--- a/drivers/mfd/88pm805.c
+++ b/drivers/mfd/88pm805.c
@@ -29,10 +29,8 @@
#include <linux/slab.h>
#include <linux/delay.h>
-#define PM805_CHIP_ID (0x00)
-
static const struct i2c_device_id pm80x_id_table[] = {
- {"88PM805", CHIP_PM805},
+ {"88PM805", 0},
{} /* NULL terminated */
};
MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
@@ -79,7 +77,7 @@ static struct resource codec_resources[] = {
},
};
-static struct mfd_cell codec_devs[] = {
+static const struct mfd_cell codec_devs[] = {
{
.name = "88pm80x-codec",
.num_resources = ARRAY_SIZE(codec_resources),
@@ -135,10 +133,10 @@ static struct regmap_irq pm805_irqs[] = {
},
};
-static int __devinit device_irq_init_805(struct pm80x_chip *chip)
+static int device_irq_init_805(struct pm80x_chip *chip)
{
struct regmap *map = chip->regmap;
- unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+ unsigned long flags = IRQF_ONESHOT;
int data, mask, ret = -EINVAL;
if (!map || !chip->irq) {
@@ -189,10 +187,9 @@ static struct regmap_irq_chip pm805_irq_chip = {
.ack_base = PM805_INT_STATUS1,
};
-static int __devinit device_805_init(struct pm80x_chip *chip)
+static int device_805_init(struct pm80x_chip *chip)
{
int ret = 0;
- unsigned int val;
struct regmap *map = chip->regmap;
if (!map) {
@@ -200,13 +197,6 @@ static int __devinit device_805_init(struct pm80x_chip *chip)
return -EINVAL;
}
- ret = regmap_read(map, PM805_CHIP_ID, &val);
- if (ret < 0) {
- dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
- goto out_irq_init;
- }
- chip->version = val;
-
chip->regmap_irq_chip = &pm805_irq_chip;
ret = device_irq_init_805(chip);
@@ -232,14 +222,14 @@ out_irq_init:
return ret;
}
-static int __devinit pm805_probe(struct i2c_client *client,
+static int pm805_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret = 0;
struct pm80x_chip *chip;
- struct pm80x_platform_data *pdata = client->dev.platform_data;
+ struct pm80x_platform_data *pdata = dev_get_platdata(&client->dev);
- ret = pm80x_init(client, id);
+ ret = pm80x_init(client);
if (ret) {
dev_err(&client->dev, "pm805_init fail!\n");
goto out_init;
@@ -249,39 +239,39 @@ static int __devinit pm805_probe(struct i2c_client *client,
ret = device_805_init(chip);
if (ret) {
- dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id);
+ dev_err(chip->dev, "Failed to initialize 88pm805 devices\n");
goto err_805_init;
}
- if (pdata->plat_config)
+ if (pdata && pdata->plat_config)
pdata->plat_config(chip, pdata);
err_805_init:
- pm80x_deinit(client);
+ pm80x_deinit();
out_init:
return ret;
}
-static int __devexit pm805_remove(struct i2c_client *client)
+static int pm805_remove(struct i2c_client *client)
{
struct pm80x_chip *chip = i2c_get_clientdata(client);
mfd_remove_devices(chip->dev);
device_irq_exit_805(chip);
- pm80x_deinit(client);
+ pm80x_deinit();
return 0;
}
static struct i2c_driver pm805_driver = {
.driver = {
- .name = "88PM80X",
+ .name = "88PM805",
.owner = THIS_MODULE,
.pm = &pm80x_pm_ops,
},
.probe = pm805_probe,
- .remove = __devexit_p(pm805_remove),
+ .remove = pm805_remove,
.id_table = pm80x_id_table,
};
diff --git a/drivers/mfd/88pm80x.c b/drivers/mfd/88pm80x.c
index cd0bf527d76..5e72f65ef94 100644
--- a/drivers/mfd/88pm80x.c
+++ b/drivers/mfd/88pm80x.c
@@ -18,6 +18,23 @@
#include <linux/uaccess.h>
#include <linux/err.h>
+/* 88pm80x chips have same definition for chip id register. */
+#define PM80X_CHIP_ID (0x00)
+#define PM80X_CHIP_ID_NUM(x) (((x) >> 5) & 0x7)
+#define PM80X_CHIP_ID_REVISION(x) ((x) & 0x1F)
+
+struct pm80x_chip_mapping {
+ unsigned int id;
+ int type;
+};
+
+static struct pm80x_chip_mapping chip_mapping[] = {
+ /* 88PM800 chip id number */
+ {0x3, CHIP_PM800},
+ /* 88PM805 chip id number */
+ {0x0, CHIP_PM805},
+};
+
/*
* workaround: some registers needed by pm805 are defined in pm800, so
* need to use this global variable to maintain the relation between
@@ -31,12 +48,13 @@ const struct regmap_config pm80x_regmap_config = {
};
EXPORT_SYMBOL_GPL(pm80x_regmap_config);
-int __devinit pm80x_init(struct i2c_client *client,
- const struct i2c_device_id *id)
+
+int pm80x_init(struct i2c_client *client)
{
struct pm80x_chip *chip;
struct regmap *map;
- int ret = 0;
+ unsigned int val;
+ int i, ret = 0;
chip =
devm_kzalloc(&client->dev, sizeof(struct pm80x_chip), GFP_KERNEL);
@@ -48,13 +66,7 @@ int __devinit pm80x_init(struct i2c_client *client,
ret = PTR_ERR(map);
dev_err(&client->dev, "Failed to allocate register map: %d\n",
ret);
- goto err_regmap_init;
- }
-
- chip->id = id->driver_data;
- if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805) {
- ret = -EINVAL;
- goto err_chip_id;
+ return ret;
}
chip->client = client;
@@ -66,6 +78,25 @@ int __devinit pm80x_init(struct i2c_client *client,
dev_set_drvdata(chip->dev, chip);
i2c_set_clientdata(chip->client, chip);
+ ret = regmap_read(chip->regmap, PM80X_CHIP_ID, &val);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(chip_mapping); i++) {
+ if (chip_mapping[i].id == PM80X_CHIP_ID_NUM(val)) {
+ chip->type = chip_mapping[i].type;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(chip_mapping)) {
+ dev_err(chip->dev,
+ "Failed to detect Marvell 88PM800:ChipID[0x%x]\n", val);
+ return -EINVAL;
+ }
+
device_init_wakeup(&client->dev, 1);
/*
@@ -82,19 +113,11 @@ int __devinit pm80x_init(struct i2c_client *client,
}
return 0;
-
-err_chip_id:
- regmap_exit(map);
-err_regmap_init:
- devm_kfree(&client->dev, chip);
- return ret;
}
EXPORT_SYMBOL_GPL(pm80x_init);
-int pm80x_deinit(struct i2c_client *client)
+int pm80x_deinit(void)
{
- struct pm80x_chip *chip = i2c_get_clientdata(client);
-
/*
* workaround: clear the dependency between pm800 and pm805.
* would remove it after HW chip fixes the issue.
@@ -103,10 +126,6 @@ int pm80x_deinit(struct i2c_client *client)
g_pm80x_chip->companion = NULL;
else
g_pm80x_chip = NULL;
-
- regmap_exit(chip->regmap);
- devm_kfree(&client->dev, chip);
-
return 0;
}
EXPORT_SYMBOL_GPL(pm80x_deinit);
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c
index 8fa86edf70d..bcfc9e85b4a 100644
--- a/drivers/mfd/88pm860x-core.c
+++ b/drivers/mfd/88pm860x-core.c
@@ -28,111 +28,111 @@
#define INT_STATUS_NUM 3
-static struct resource bk0_resources[] __devinitdata = {
+static struct resource bk0_resources[] = {
{2, 2, "duty cycle", IORESOURCE_REG, },
{3, 3, "always on", IORESOURCE_REG, },
{3, 3, "current", IORESOURCE_REG, },
};
-static struct resource bk1_resources[] __devinitdata = {
+static struct resource bk1_resources[] = {
{4, 4, "duty cycle", IORESOURCE_REG, },
{5, 5, "always on", IORESOURCE_REG, },
{5, 5, "current", IORESOURCE_REG, },
};
-static struct resource bk2_resources[] __devinitdata = {
+static struct resource bk2_resources[] = {
{6, 6, "duty cycle", IORESOURCE_REG, },
{7, 7, "always on", IORESOURCE_REG, },
{5, 5, "current", IORESOURCE_REG, },
};
-static struct resource led0_resources[] __devinitdata = {
+static struct resource led0_resources[] = {
/* RGB1 Red LED */
{0xd, 0xd, "control", IORESOURCE_REG, },
{0xc, 0xc, "blink", IORESOURCE_REG, },
};
-static struct resource led1_resources[] __devinitdata = {
+static struct resource led1_resources[] = {
/* RGB1 Green LED */
{0xe, 0xe, "control", IORESOURCE_REG, },
{0xc, 0xc, "blink", IORESOURCE_REG, },
};
-static struct resource led2_resources[] __devinitdata = {
+static struct resource led2_resources[] = {
/* RGB1 Blue LED */
{0xf, 0xf, "control", IORESOURCE_REG, },
{0xc, 0xc, "blink", IORESOURCE_REG, },
};
-static struct resource led3_resources[] __devinitdata = {
+static struct resource led3_resources[] = {
/* RGB2 Red LED */
{0x9, 0x9, "control", IORESOURCE_REG, },
{0x8, 0x8, "blink", IORESOURCE_REG, },
};
-static struct resource led4_resources[] __devinitdata = {
+static struct resource led4_resources[] = {
/* RGB2 Green LED */
{0xa, 0xa, "control", IORESOURCE_REG, },
{0x8, 0x8, "blink", IORESOURCE_REG, },
};
-static struct resource led5_resources[] __devinitdata = {
+static struct resource led5_resources[] = {
/* RGB2 Blue LED */
{0xb, 0xb, "control", IORESOURCE_REG, },
{0x8, 0x8, "blink", IORESOURCE_REG, },
};
-static struct resource buck1_resources[] __devinitdata = {
+static struct resource buck1_resources[] = {
{0x24, 0x24, "buck set", IORESOURCE_REG, },
};
-static struct resource buck2_resources[] __devinitdata = {
+static struct resource buck2_resources[] = {
{0x25, 0x25, "buck set", IORESOURCE_REG, },
};
-static struct resource buck3_resources[] __devinitdata = {
+static struct resource buck3_resources[] = {
{0x26, 0x26, "buck set", IORESOURCE_REG, },
};
-static struct resource ldo1_resources[] __devinitdata = {
+static struct resource ldo1_resources[] = {
{0x10, 0x10, "ldo set", IORESOURCE_REG, },
};
-static struct resource ldo2_resources[] __devinitdata = {
+static struct resource ldo2_resources[] = {
{0x11, 0x11, "ldo set", IORESOURCE_REG, },
};
-static struct resource ldo3_resources[] __devinitdata = {
+static struct resource ldo3_resources[] = {
{0x12, 0x12, "ldo set", IORESOURCE_REG, },
};
-static struct resource ldo4_resources[] __devinitdata = {
+static struct resource ldo4_resources[] = {
{0x13, 0x13, "ldo set", IORESOURCE_REG, },
};
-static struct resource ldo5_resources[] __devinitdata = {
+static struct resource ldo5_resources[] = {
{0x14, 0x14, "ldo set", IORESOURCE_REG, },
};
-static struct resource ldo6_resources[] __devinitdata = {
+static struct resource ldo6_resources[] = {
{0x15, 0x15, "ldo set", IORESOURCE_REG, },
};
-static struct resource ldo7_resources[] __devinitdata = {
+static struct resource ldo7_resources[] = {
{0x16, 0x16, "ldo set", IORESOURCE_REG, },
};
-static struct resource ldo8_resources[] __devinitdata = {
+static struct resource ldo8_resources[] = {
{0x17, 0x17, "ldo set", IORESOURCE_REG, },
};
-static struct resource ldo9_resources[] __devinitdata = {
+static struct resource ldo9_resources[] = {
{0x18, 0x18, "ldo set", IORESOURCE_REG, },
};
-static struct resource ldo10_resources[] __devinitdata = {
+static struct resource ldo10_resources[] = {
{0x19, 0x19, "ldo set", IORESOURCE_REG, },
};
-static struct resource ldo12_resources[] __devinitdata = {
+static struct resource ldo12_resources[] = {
{0x1a, 0x1a, "ldo set", IORESOURCE_REG, },
};
-static struct resource ldo_vibrator_resources[] __devinitdata = {
+static struct resource ldo_vibrator_resources[] = {
{0x28, 0x28, "ldo set", IORESOURCE_REG, },
};
-static struct resource ldo14_resources[] __devinitdata = {
+static struct resource ldo14_resources[] = {
{0x1b, 0x1b, "ldo set", IORESOURCE_REG, },
};
-static struct resource touch_resources[] __devinitdata = {
+static struct resource touch_resources[] = {
{PM8607_IRQ_PEN, PM8607_IRQ_PEN, "touch", IORESOURCE_IRQ,},
};
-static struct resource onkey_resources[] __devinitdata = {
+static struct resource onkey_resources[] = {
{PM8607_IRQ_ONKEY, PM8607_IRQ_ONKEY, "onkey", IORESOURCE_IRQ,},
};
-static struct resource codec_resources[] __devinitdata = {
+static struct resource codec_resources[] = {
/* Headset microphone insertion or removal */
{PM8607_IRQ_MICIN, PM8607_IRQ_MICIN, "micin", IORESOURCE_IRQ,},
/* Hook-switch press or release */
@@ -143,12 +143,12 @@ static struct resource codec_resources[] __devinitdata = {
{PM8607_IRQ_AUDIO_SHORT, PM8607_IRQ_AUDIO_SHORT, "audio-short", IORESOURCE_IRQ,},
};
-static struct resource battery_resources[] __devinitdata = {
+static struct resource battery_resources[] = {
{PM8607_IRQ_CC, PM8607_IRQ_CC, "columb counter", IORESOURCE_IRQ,},
{PM8607_IRQ_BAT, PM8607_IRQ_BAT, "battery", IORESOURCE_IRQ,},
};
-static struct resource charger_resources[] __devinitdata = {
+static struct resource charger_resources[] = {
{PM8607_IRQ_CHG, PM8607_IRQ_CHG, "charger detect", IORESOURCE_IRQ,},
{PM8607_IRQ_CHG_DONE, PM8607_IRQ_CHG_DONE, "charging done", IORESOURCE_IRQ,},
{PM8607_IRQ_CHG_FAIL, PM8607_IRQ_CHG_FAIL, "charging timeout", IORESOURCE_IRQ,},
@@ -158,11 +158,11 @@ static struct resource charger_resources[] __devinitdata = {
{PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage", IORESOURCE_IRQ,},
};
-static struct resource rtc_resources[] __devinitdata = {
+static struct resource rtc_resources[] = {
{PM8607_IRQ_RTC, PM8607_IRQ_RTC, "rtc", IORESOURCE_IRQ,},
};
-static struct mfd_cell bk_devs[] __devinitdata = {
+static struct mfd_cell bk_devs[] = {
{
.name = "88pm860x-backlight",
.id = 0,
@@ -181,7 +181,7 @@ static struct mfd_cell bk_devs[] __devinitdata = {
},
};
-static struct mfd_cell led_devs[] __devinitdata = {
+static struct mfd_cell led_devs[] = {
{
.name = "88pm860x-led",
.id = 0,
@@ -215,7 +215,7 @@ static struct mfd_cell led_devs[] __devinitdata = {
},
};
-static struct mfd_cell reg_devs[] __devinitdata = {
+static struct mfd_cell reg_devs[] = {
{
.name = "88pm860x-regulator",
.id = 0,
@@ -565,7 +565,7 @@ static struct irq_domain_ops pm860x_irq_domain_ops = {
.xlate = irq_domain_xlate_onetwocell,
};
-static int __devinit device_irq_init(struct pm860x_chip *chip,
+static int device_irq_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata)
{
struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
@@ -730,7 +730,7 @@ out:
}
EXPORT_SYMBOL(pm8606_osc_disable);
-static void __devinit device_osc_init(struct i2c_client *i2c)
+static void device_osc_init(struct i2c_client *i2c)
{
struct pm860x_chip *chip = i2c_get_clientdata(i2c);
@@ -745,7 +745,7 @@ static void __devinit device_osc_init(struct i2c_client *i2c)
chip->osc_status = PM8606_REF_GP_OSC_OFF;
}
-static void __devinit device_bk_init(struct pm860x_chip *chip,
+static void device_bk_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata)
{
int ret, i;
@@ -765,7 +765,7 @@ static void __devinit device_bk_init(struct pm860x_chip *chip,
dev_err(chip->dev, "Failed to add backlight subdev\n");
}
-static void __devinit device_led_init(struct pm860x_chip *chip,
+static void device_led_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata)
{
int ret, i;
@@ -787,7 +787,7 @@ static void __devinit device_led_init(struct pm860x_chip *chip,
}
}
-static void __devinit device_regulator_init(struct pm860x_chip *chip,
+static void device_regulator_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata)
{
int ret;
@@ -866,7 +866,7 @@ static void __devinit device_regulator_init(struct pm860x_chip *chip,
}
}
-static void __devinit device_rtc_init(struct pm860x_chip *chip,
+static void device_rtc_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata)
{
int ret;
@@ -885,7 +885,7 @@ static void __devinit device_rtc_init(struct pm860x_chip *chip,
dev_err(chip->dev, "Failed to add rtc subdev\n");
}
-static void __devinit device_touch_init(struct pm860x_chip *chip,
+static void device_touch_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata)
{
int ret;
@@ -904,7 +904,7 @@ static void __devinit device_touch_init(struct pm860x_chip *chip,
dev_err(chip->dev, "Failed to add touch subdev\n");
}
-static void __devinit device_power_init(struct pm860x_chip *chip,
+static void device_power_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata)
{
int ret;
@@ -951,7 +951,7 @@ static void __devinit device_power_init(struct pm860x_chip *chip,
}
}
-static void __devinit device_onkey_init(struct pm860x_chip *chip,
+static void device_onkey_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata)
{
int ret;
@@ -965,7 +965,7 @@ static void __devinit device_onkey_init(struct pm860x_chip *chip,
dev_err(chip->dev, "Failed to add onkey subdev\n");
}
-static void __devinit device_codec_init(struct pm860x_chip *chip,
+static void device_codec_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata)
{
int ret;
@@ -979,7 +979,7 @@ static void __devinit device_codec_init(struct pm860x_chip *chip,
dev_err(chip->dev, "Failed to add codec subdev\n");
}
-static void __devinit device_8607_init(struct pm860x_chip *chip,
+static void device_8607_init(struct pm860x_chip *chip,
struct i2c_client *i2c,
struct pm860x_platform_data *pdata)
{
@@ -1040,7 +1040,7 @@ out:
return;
}
-static void __devinit device_8606_init(struct pm860x_chip *chip,
+static void device_8606_init(struct pm860x_chip *chip,
struct i2c_client *i2c,
struct pm860x_platform_data *pdata)
{
@@ -1049,7 +1049,7 @@ static void __devinit device_8606_init(struct pm860x_chip *chip,
device_led_init(chip, pdata);
}
-static int __devinit pm860x_device_init(struct pm860x_chip *chip,
+static int pm860x_device_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata)
{
chip->core_irq = 0;
@@ -1077,7 +1077,7 @@ static int __devinit pm860x_device_init(struct pm860x_chip *chip,
return 0;
}
-static void __devexit pm860x_device_exit(struct pm860x_chip *chip)
+static void pm860x_device_exit(struct pm860x_chip *chip)
{
device_irq_exit(chip);
mfd_remove_devices(chip->dev);
@@ -1109,7 +1109,7 @@ static struct regmap_config pm860x_regmap_config = {
.val_bits = 8,
};
-static int __devinit pm860x_dt_init(struct device_node *np,
+static int pm860x_dt_init(struct device_node *np,
struct device *dev,
struct pm860x_platform_data *pdata)
{
@@ -1127,10 +1127,10 @@ static int __devinit pm860x_dt_init(struct device_node *np,
return 0;
}
-static int __devinit pm860x_probe(struct i2c_client *client,
+static int pm860x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct pm860x_platform_data *pdata = client->dev.platform_data;
+ struct pm860x_platform_data *pdata = dev_get_platdata(&client->dev);
struct device_node *node = client->dev.of_node;
struct pm860x_chip *chip;
int ret;
@@ -1144,25 +1144,23 @@ static int __devinit pm860x_probe(struct i2c_client *client,
return -ENOMEM;
ret = pm860x_dt_init(node, &client->dev, pdata);
if (ret)
- goto err;
+ return ret;
} else if (!pdata) {
pr_info("No platform data in %s!\n", __func__);
return -EINVAL;
}
- chip = kzalloc(sizeof(struct pm860x_chip), GFP_KERNEL);
- if (chip == NULL) {
- ret = -ENOMEM;
- goto err;
- }
+ chip = devm_kzalloc(&client->dev,
+ sizeof(struct pm860x_chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
chip->id = verify_addr(client);
- chip->regmap = regmap_init_i2c(client, &pm860x_regmap_config);
+ chip->regmap = devm_regmap_init_i2c(client, &pm860x_regmap_config);
if (IS_ERR(chip->regmap)) {
ret = PTR_ERR(chip->regmap);
dev_err(&client->dev, "Failed to allocate register map: %d\n",
ret);
- kfree(chip);
return ret;
}
chip->client = client;
@@ -1181,12 +1179,18 @@ static int __devinit pm860x_probe(struct i2c_client *client,
chip->companion_addr = pdata->companion_addr;
chip->companion = i2c_new_dummy(chip->client->adapter,
chip->companion_addr);
+ if (!chip->companion) {
+ dev_err(&client->dev,
+ "Failed to allocate I2C companion device\n");
+ return -ENODEV;
+ }
chip->regmap_companion = regmap_init_i2c(chip->companion,
&pm860x_regmap_config);
if (IS_ERR(chip->regmap_companion)) {
ret = PTR_ERR(chip->regmap_companion);
dev_err(&chip->companion->dev,
"Failed to allocate register map: %d\n", ret);
+ i2c_unregister_device(chip->companion);
return ret;
}
i2c_set_clientdata(chip->companion, chip);
@@ -1194,13 +1198,9 @@ static int __devinit pm860x_probe(struct i2c_client *client,
pm860x_device_init(chip, pdata);
return 0;
-err:
- if (node)
- devm_kfree(&client->dev, pdata);
- return ret;
}
-static int __devexit pm860x_remove(struct i2c_client *client)
+static int pm860x_remove(struct i2c_client *client)
{
struct pm860x_chip *chip = i2c_get_clientdata(client);
@@ -1209,8 +1209,6 @@ static int __devexit pm860x_remove(struct i2c_client *client)
regmap_exit(chip->regmap_companion);
i2c_unregister_device(chip->companion);
}
- regmap_exit(chip->regmap);
- kfree(chip);
return 0;
}
@@ -1255,10 +1253,10 @@ static struct i2c_driver pm860x_driver = {
.name = "88PM860x",
.owner = THIS_MODULE,
.pm = &pm860x_pm_ops,
- .of_match_table = of_match_ptr(pm860x_dt_ids),
+ .of_match_table = pm860x_dt_ids,
},
.probe = pm860x_probe,
- .remove = __devexit_p(pm860x_remove),
+ .remove = pm860x_remove,
.id_table = pm860x_id_table,
};
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index acab3ef8a31..6cc4b6acc22 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -10,405 +10,117 @@ config MFD_CORE
select IRQ_DOMAIN
default n
-config MFD_88PM860X
- bool "Support Marvell 88PM8606/88PM8607"
- depends on I2C=y && GENERIC_HARDIRQS
- select REGMAP_I2C
+config MFD_CS5535
+ tristate "AMD CS5535 and CS5536 southbridge core functions"
select MFD_CORE
- help
- This supports for Marvell 88PM8606/88PM8607 Power Management IC.
- This includes the I2C driver and the core APIs _only_, you have to
- select individual components like voltage regulators, RTC and
- battery-charger under the corresponding menus.
+ depends on PCI && X86
+ ---help---
+ This is the core driver for CS5535/CS5536 MFD functions. This is
+ necessary for using the board's GPIO and MFGPT functionality.
-config MFD_88PM800
- tristate "Support Marvell 88PM800"
- depends on I2C=y && GENERIC_HARDIRQS
- select REGMAP_I2C
- select REGMAP_IRQ
+config MFD_AS3711
+ bool "AMS AS3711"
select MFD_CORE
- help
- This supports for Marvell 88PM800 Power Management IC.
- This includes the I2C driver and the core APIs _only_, you have to
- select individual components like voltage regulators, RTC and
- battery-charger under the corresponding menus.
-
-config MFD_88PM805
- tristate "Support Marvell 88PM805"
- depends on I2C=y && GENERIC_HARDIRQS
select REGMAP_I2C
select REGMAP_IRQ
- select MFD_CORE
- help
- This supports for Marvell 88PM805 Power Management IC. This includes
- the I2C driver and the core APIs _only_, you have to select individual
- components like codec device, headset/Mic device under the
- corresponding menus.
-
-config MFD_SM501
- tristate "Support for Silicon Motion SM501"
- ---help---
- This is the core driver for the Silicon Motion SM501 multimedia
- companion chip. This device is a multifunction device which may
- provide numerous interfaces including USB host controller, USB gadget,
- asynchronous serial ports, audio functions, and a dual display video
- interface. The device may be connected by PCI or local bus with
- varying functions enabled.
-
-config MFD_SM501_GPIO
- bool "Export GPIO via GPIO layer"
- depends on MFD_SM501 && GPIOLIB
- ---help---
- This option uses the gpio library layer to export the 64 GPIO
- lines on the SM501. The platform data is used to supply the
- base number for the first GPIO line to register.
-
-config MFD_ASIC3
- bool "Support for Compaq ASIC3"
- depends on GENERIC_HARDIRQS && GPIOLIB && ARM
- select MFD_CORE
- ---help---
- This driver supports the ASIC3 multifunction chip found on many
- PDAs (mainly iPAQ and HTC based ones)
-
-config MFD_DAVINCI_VOICECODEC
- tristate
- select MFD_CORE
-
-config MFD_DM355EVM_MSP
- bool "DaVinci DM355 EVM microcontroller"
- depends on I2C=y && MACH_DAVINCI_DM355_EVM
- help
- This driver supports the MSP430 microcontroller used on these
- boards. MSP430 firmware manages resets and power sequencing,
- inputs from buttons and the IR remote, LEDs, an RTC, and more.
-
-config MFD_TI_SSP
- tristate "TI Sequencer Serial Port support"
- depends on ARCH_DAVINCI_TNETV107X
- select MFD_CORE
- ---help---
- Say Y here if you want support for the Sequencer Serial Port
- in a Texas Instruments TNETV107X SoC.
-
- To compile this driver as a module, choose M here: the
- module will be called ti-ssp.
-
-config HTC_EGPIO
- bool "HTC EGPIO support"
- depends on GENERIC_HARDIRQS && GPIOLIB && ARM
- help
- This driver supports the CPLD egpio chip present on
- several HTC phones. It provides basic support for input
- pins, output pins, and irqs.
-
-config HTC_PASIC3
- tristate "HTC PASIC3 LED/DS1WM chip support"
- select MFD_CORE
- help
- This core driver provides register access for the LED/DS1WM
- chips labeled "AIC2" and "AIC3", found on HTC Blueangel and
- HTC Magician devices, respectively. Actual functionality is
- handled by the leds-pasic3 and ds1wm drivers.
-
-config HTC_I2CPLD
- bool "HTC I2C PLD chip support"
- depends on I2C=y && GPIOLIB
- help
- If you say yes here you get support for the supposed CPLD
- found on omap850 HTC devices like the HTC Wizard and HTC Herald.
- This device provides input and output GPIOs through an I2C
- interface to one or more sub-chips.
-
-config UCB1400_CORE
- tristate "Philips UCB1400 Core driver"
- depends on AC97_BUS
- depends on GPIOLIB
+ depends on I2C=y
help
- This enables support for the Philips UCB1400 core functions.
- The UCB1400 is an AC97 audio codec.
+ Support for the AS3711 PMIC from AMS
- To compile this driver as a module, choose M here: the
- module will be called ucb1400_core.
-
-config MFD_LM3533
- tristate "LM3533 Lighting Power chip"
- depends on I2C
+config MFD_AS3722
+ bool "ams AS3722 Power Management IC"
select MFD_CORE
select REGMAP_I2C
+ select REGMAP_IRQ
+ depends on I2C=y && OF
help
- Say yes here to enable support for National Semiconductor / TI
- LM3533 Lighting Power chips.
-
- This driver provides common support for accessing the device;
- additional drivers must be enabled in order to use the LED,
- backlight or ambient-light-sensor functionality of the device.
-
-config TPS6105X
- tristate "TPS61050/61052 Boost Converters"
- depends on I2C
- select REGULATOR
- select MFD_CORE
- select REGULATOR_FIXED_VOLTAGE
- help
- This option enables a driver for the TP61050/TPS61052
- high-power "white LED driver". This boost converter is
- sometimes used for other things than white LEDs, and
- also contains a GPIO pin.
-
-config TPS65010
- tristate "TPS6501x Power Management chips"
- depends on I2C && GPIOLIB
- default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK
- help
- If you say yes here you get support for the TPS6501x series of
- Power Management chips. These include voltage regulators,
- lithium ion/polymer battery charging, and other features that
- are often used in portable devices like cell phones and cameras.
-
- This driver can also be built as a module. If so, the module
- will be called tps65010.
-
-config TPS6507X
- tristate "TPS6507x Power Management / Touch Screen chips"
- select MFD_CORE
- depends on I2C
- help
- If you say yes here you get support for the TPS6507x series of
- Power Management / Touch Screen chips. These include voltage
- regulators, lithium ion/polymer battery charging, touch screen
- and other features that are often used in portable devices.
- This driver can also be built as a module. If so, the module
- will be called tps6507x.
+ The ams AS3722 is a compact system PMU suitable for mobile phones,
+ tablets etc. It has 4 DC/DC step-down regulators, 3 DC/DC step-down
+ controllers, 11 LDOs, RTC, automatic battery, temperature and
+ over current monitoring, GPIOs, ADC and a watchdog.
-config MFD_TPS65217
- tristate "TPS65217 Power Management / White LED chips"
- depends on I2C
- select MFD_CORE
- select REGMAP_I2C
+config PMIC_ADP5520
+ bool "Analog Devices ADP5520/01 MFD PMIC Core Support"
+ depends on I2C=y
help
- If you say yes here you get support for the TPS65217 series of
- Power Management / White LED chips.
- These include voltage regulators, lithium ion/polymer battery
- charger, wled and other features that are often used in portable
- devices.
-
- This driver can also be built as a module. If so, the module
- will be called tps65217.
+ Say yes here to add support for Analog Devices AD5520 and ADP5501,
+ Multifunction Power Management IC. This includes
+ the I2C driver and the core APIs _only_, you have to select
+ individual components like LCD backlight, LEDs, GPIOs and Kepad
+ under the corresponding menus.
-config MFD_TPS6586X
- bool "TPS6586x Power Management chips"
- depends on I2C=y && GENERIC_HARDIRQS
+config MFD_AAT2870_CORE
+ bool "AnalogicTech AAT2870"
select MFD_CORE
- select REGMAP_I2C
- depends on REGULATOR
+ depends on I2C=y && GPIOLIB
help
- If you say yes here you get support for the TPS6586X series of
- Power Management chips.
+ If you say yes here you get support for the AAT2870.
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the
functionality of the device.
- This driver can also be built as a module. If so, the module
- will be called tps6586x.
-
-config MFD_TPS65910
- bool "TPS65910 Power Management chip"
- depends on I2C=y && GPIOLIB
+config MFD_BCM590XX
+ tristate "Broadcom BCM590xx PMUs"
select MFD_CORE
select REGMAP_I2C
- select IRQ_DOMAIN
- help
- if you say yes here you get support for the TPS65910 series of
- Power Management chips.
-
-config MFD_TPS65912
- bool
- depends on GPIOLIB
-
-config MFD_TPS65912_I2C
- bool "TPS65912 Power Management chip with I2C"
- select MFD_CORE
- select MFD_TPS65912
- depends on I2C=y && GPIOLIB
- help
- If you say yes here you get support for the TPS65912 series of
- PM chips with I2C interface.
-
-config MFD_TPS65912_SPI
- bool "TPS65912 Power Management chip with SPI"
- select MFD_CORE
- select MFD_TPS65912
- depends on SPI_MASTER && GPIOLIB
- help
- If you say yes here you get support for the TPS65912 series of
- PM chips with SPI interface.
-
-config MENELAUS
- bool "Texas Instruments TWL92330/Menelaus PM chip"
- depends on I2C=y && ARCH_OMAP2
- help
- If you say yes here you get support for the Texas Instruments
- TWL92330/Menelaus Power Management chip. This include voltage
- regulators, Dual slot memory card transceivers, real-time clock
- and other features that are often used in portable devices like
- cell phones and PDAs.
-
-config TWL4030_CORE
- bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support"
- depends on I2C=y && GENERIC_HARDIRQS
- select IRQ_DOMAIN
- help
- Say yes here if you have TWL4030 / TWL6030 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 TWL4030_MADC
- tristate "Texas Instruments TWL4030 MADC"
- depends on TWL4030_CORE
- help
- This driver provides support for triton TWL4030-MADC. The
- driver supports both RT and SW conversion methods.
-
- This driver can be built as a module. If so it will be
- named twl4030-madc
-
-config TWL4030_POWER
- bool "Support power resources on TWL4030 family chips"
- depends on TWL4030_CORE && ARM
+ depends on I2C
help
- Say yes here if you want to use the power resources on the
- TWL4030 family chips. Most of these resources are regulators,
- which have a separate driver; some are control signals, such
- as clock request handshaking.
+ Support for the BCM590xx PMUs from Broadcom
- This driver uses board-specific data to initialize the resources
- and load scripts controlling which resources are switched off/on
- or reset when a sleep, wakeup or warm reset event occurs.
-
-config MFD_TWL4030_AUDIO
- bool
- depends on TWL4030_CORE
- select MFD_CORE
- default n
-
-config TWL6040_CORE
- bool "Support for TWL6040 audio codec"
- depends on I2C=y && GENERIC_HARDIRQS
+config MFD_AXP20X
+ bool "X-Powers AXP20X"
select MFD_CORE
select REGMAP_I2C
- select IRQ_DOMAIN
- default n
- help
- Say yes here if you want support for Texas Instruments TWL6040 audio
- codec.
- This driver provides common support for accessing the device,
- additional drivers must be enabled in order to use the
- functionality of the device (audio, vibra).
-
-config MFD_STMPE
- bool "Support STMicroelectronics STMPE"
- depends on (I2C=y || SPI_MASTER=y) && GENERIC_HARDIRQS
- select MFD_CORE
- help
- Support for the STMPE family of I/O Expanders from
- STMicroelectronics.
-
- Currently supported devices are:
-
- STMPE811: GPIO, Touchscreen
- STMPE1601: GPIO, Keypad
- STMPE2401: GPIO, Keypad
- STMPE2403: GPIO, Keypad
-
- This driver provides common support for accessing the device,
- additional drivers must be enabled in order to use the functionality
- of the device. Currently available sub drivers are:
-
- GPIO: stmpe-gpio
- Keypad: stmpe-keypad
- Touchscreen: stmpe-ts
-
-menu "STMPE Interface Drivers"
-depends on MFD_STMPE
-
-config STMPE_I2C
- bool "STMPE I2C Inteface"
+ select REGMAP_IRQ
depends on I2C=y
- default y
help
- This is used to enable I2C interface of STMPE
-
-config STMPE_SPI
- bool "STMPE SPI Inteface"
- depends on SPI_MASTER
- help
- This is used to enable SPI interface of STMPE
-endmenu
+ If you say Y here you get support for the X-Powers AXP202 and AXP209.
+ This driver include only the core APIs. You have to select individual
+ components like regulators or the PEK (Power Enable Key) under the
+ corresponding menus.
-config MFD_TC3589X
- bool "Support Toshiba TC35892 and variants"
- depends on I2C=y && GENERIC_HARDIRQS
+config MFD_CROS_EC
+ tristate "ChromeOS Embedded Controller"
select MFD_CORE
help
- Support for the Toshiba TC35892 and variants I/O Expander.
-
- This driver provides common support for accessing the device,
- additional drivers must be enabled in order to use the
- functionality of the device.
+ If you say Y here you get support for the ChromeOS Embedded
+ Controller (EC) providing keyboard, battery and power services.
+ You also need to enable the driver for the bus you are using. The
+ protocol for talking to the EC is defined by the bus driver.
-config MFD_TMIO
- bool
- default n
+config MFD_CROS_EC_I2C
+ tristate "ChromeOS Embedded Controller (I2C)"
+ depends on MFD_CROS_EC && I2C
-config MFD_T7L66XB
- bool "Support Toshiba T7L66XB"
- depends on ARM && HAVE_CLK
- select MFD_CORE
- select MFD_TMIO
help
- Support for Toshiba Mobile IO Controller T7L66XB
+ If you say Y here, you get support for talking to the ChromeOS
+ EC through an I2C bus. This uses a simple byte-level protocol with
+ a checksum. Failing accesses will be retried three times to
+ improve reliability.
-config MFD_SMSC
- bool "Support for the SMSC ECE1099 series chips"
- depends on I2C=y
- select MFD_CORE
- select REGMAP_I2C
- help
- If you say yes here you get support for the
- ece1099 chips from SMSC.
+config MFD_CROS_EC_SPI
+ tristate "ChromeOS Embedded Controller (SPI)"
+ depends on MFD_CROS_EC && SPI && OF
- To compile this driver as a module, choose M here: the
- module will be called smsc.
-
-config MFD_TC6387XB
- bool "Support Toshiba TC6387XB"
- depends on ARM && HAVE_CLK
- select MFD_CORE
- select MFD_TMIO
- help
- Support for Toshiba Mobile IO Controller TC6387XB
+ ---help---
+ If you say Y here, you get support for talking to the ChromeOS EC
+ through a SPI bus, using a byte-level protocol. Since the EC's
+ response time cannot be guaranteed, we support ignoring
+ 'pre-amble' bytes before the response actually starts.
-config MFD_TC6393XB
- bool "Support Toshiba TC6393XB"
- depends on ARM && HAVE_CLK
- select GPIOLIB
+config MFD_ASIC3
+ bool "Compaq ASIC3"
+ depends on GPIOLIB && ARM
select MFD_CORE
- select MFD_TMIO
- help
- Support for Toshiba Mobile IO Controller TC6393XB
+ ---help---
+ This driver supports the ASIC3 multifunction chip found on many
+ PDAs (mainly iPAQ and HTC based ones)
config PMIC_DA903X
bool "Dialog Semiconductor DA9030/DA9034 PMIC Support"
depends on I2C=y
help
- Say yes here to support for Dialog Semiconductor DA9030 (a.k.a
+ Say yes here to add support for Dialog Semiconductor DA9030 (a.k.a
ARAVA) and DA9034 (a.k.a MICCO), these are Power Management IC
usually found on PXA processors-based platforms. This includes
the I2C driver and the core APIs _only_, you have to select
@@ -420,7 +132,7 @@ config PMIC_DA9052
select MFD_CORE
config MFD_DA9052_SPI
- bool "Support Dialog Semiconductor DA9052/53 PMIC variants with SPI"
+ bool "Dialog Semiconductor DA9052/53 PMIC variants with SPI"
select REGMAP_SPI
select REGMAP_IRQ
select PMIC_DA9052
@@ -432,7 +144,7 @@ config MFD_DA9052_SPI
order to use the functionality of the device.
config MFD_DA9052_I2C
- bool "Support Dialog Semiconductor DA9052/53 PMIC variants with I2C"
+ bool "Dialog Semiconductor DA9052/53 PMIC variants with I2C"
select REGMAP_I2C
select REGMAP_IRQ
select PMIC_DA9052
@@ -447,7 +159,6 @@ config MFD_DA9055
bool "Dialog Semiconductor DA9055 PMIC Support"
select REGMAP_I2C
select REGMAP_IRQ
- select PMIC_DA9055
select MFD_CORE
depends on I2C=y
help
@@ -460,34 +171,209 @@ config MFD_DA9055
This driver can be built as a module. If built as a module it will be
called "da9055"
-config PMIC_ADP5520
- bool "Analog Devices ADP5520/01 MFD PMIC Core Support"
+config MFD_DA9063
+ bool "Dialog Semiconductor DA9063 PMIC Support"
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
depends on I2C=y
help
- Say yes here to add support for Analog Devices AD5520 and ADP5501,
- Multifunction Power Management IC. This includes
- the I2C driver and the core APIs _only_, you have to select
- individual components like LCD backlight, LEDs, GPIOs and Kepad
- under the corresponding menus.
+ Say yes here for support for the Dialog Semiconductor DA9063 PMIC.
+ This includes the I2C driver and core APIs.
+ Additional drivers must be enabled in order to use the functionality
+ of the device.
-config MFD_LP8788
- bool "Texas Instruments LP8788 Power Management Unit Driver"
+config MFD_MC13XXX
+ tristate
+ depends on (SPI_MASTER || I2C)
+ select MFD_CORE
+ help
+ Enable support for the Freescale MC13783 and MC13892 PMICs.
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device.
+
+config MFD_MC13XXX_SPI
+ tristate "Freescale MC13783 and MC13892 SPI interface"
+ depends on SPI_MASTER
+ select REGMAP_SPI
+ select MFD_MC13XXX
+ help
+ Select this if your MC13xxx is connected via an SPI bus.
+
+config MFD_MC13XXX_I2C
+ tristate "Freescale MC13892 I2C interface"
+ depends on I2C
+ select REGMAP_I2C
+ select MFD_MC13XXX
+ help
+ Select this if your MC13xxx is connected via an I2C bus.
+
+config HTC_EGPIO
+ bool "HTC EGPIO support"
+ depends on GPIOLIB && ARM
+ help
+ This driver supports the CPLD egpio chip present on
+ several HTC phones. It provides basic support for input
+ pins, output pins, and irqs.
+
+config HTC_PASIC3
+ tristate "HTC PASIC3 LED/DS1WM chip support"
+ select MFD_CORE
+ help
+ This core driver provides register access for the LED/DS1WM
+ chips labeled "AIC2" and "AIC3", found on HTC Blueangel and
+ HTC Magician devices, respectively. Actual functionality is
+ handled by the leds-pasic3 and ds1wm drivers.
+
+config HTC_I2CPLD
+ bool "HTC I2C PLD chip support"
+ depends on I2C=y && GPIOLIB
+ help
+ If you say yes here you get support for the supposed CPLD
+ found on omap850 HTC devices like the HTC Wizard and HTC Herald.
+ This device provides input and output GPIOs through an I2C
+ interface to one or more sub-chips.
+
+config LPC_ICH
+ tristate "Intel ICH LPC"
+ depends on PCI
+ select MFD_CORE
+ help
+ The LPC bridge function of the Intel ICH provides support for
+ many functional units. This driver provides needed support for
+ other drivers to control these functions, currently GPIO and
+ watchdog.
+
+config LPC_SCH
+ tristate "Intel SCH LPC"
+ depends on PCI
+ select MFD_CORE
+ help
+ LPC bridge function of the Intel SCH provides support for
+ System Management Bus and General Purpose I/O.
+
+config MFD_INTEL_MSIC
+ bool "Intel MSIC"
+ depends on INTEL_SCU_IPC
+ select MFD_CORE
+ help
+ Select this option to enable access to Intel MSIC (Avatele
+ Passage) chip. This chip embeds audio, battery, GPIO, etc.
+ devices used in Intel Medfield platforms.
+
+config MFD_IPAQ_MICRO
+ bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
+ depends on SA1100_H3100 || SA1100_H3600
+ select MFD_CORE
+ help
+ Select this to get support for the Microcontroller found in
+ the Compaq iPAQ handheld computers. This is an Atmel
+ AT90LS8535 microcontroller flashed with a special iPAQ
+ firmware using the custom protocol implemented in this driver.
+
+config MFD_JANZ_CMODIO
+ tristate "Janz CMOD-IO PCI MODULbus Carrier Board"
+ select MFD_CORE
+ depends on PCI
+ help
+ This is the core driver for the Janz CMOD-IO PCI MODULbus
+ carrier board. This device is a PCI to MODULbus bridge which may
+ host many different types of MODULbus daughterboards, including
+ CAN and GPIO controllers.
+
+config MFD_JZ4740_ADC
+ bool "Janz JZ4740 ADC core"
+ select MFD_CORE
+ select GENERIC_IRQ_CHIP
+ depends on MACH_JZ4740
+ help
+ Say yes here if you want support for the ADC unit in the JZ4740 SoC.
+ This driver is necessary for jz4740-battery and jz4740-hwmon driver.
+
+config MFD_KEMPLD
+ tristate "Kontron module PLD device"
+ select MFD_CORE
+ help
+ This is the core driver for the PLD (Programmable Logic Device) found
+ on some Kontron ETX and COMexpress (ETXexpress) modules. The PLD
+ device may provide functions like watchdog, GPIO, UART and I2C bus.
+
+ The following modules are supported:
+ * COMe-bHL6
+ * COMe-bIP#
+ * COMe-bPC2 (ETXexpress-PC)
+ * COMe-bSC# (ETXexpress-SC T#)
+ * COMe-cBT6
+ * COMe-cCT6
+ * COMe-cDC2 (microETXexpress-DC)
+ * COMe-cHL6
+ * COMe-cPC2 (microETXexpress-PC)
+ * COMe-mBT10
+ * COMe-mCT10
+ * COMe-mTT10 (nanoETXexpress-TT)
+ * ETX-OH
+
+ This driver can also be built as a module. If so, the module
+ will be called kempld-core.
+
+config MFD_88PM800
+ tristate "Marvell 88PM800"
depends on I2C=y
+ select REGMAP_I2C
+ select REGMAP_IRQ
select MFD_CORE
+ help
+ This supports for Marvell 88PM800 Power Management IC.
+ This includes the I2C driver and the core APIs _only_, you have to
+ select individual components like voltage regulators, RTC and
+ battery-charger under the corresponding menus.
+
+config MFD_88PM805
+ tristate "Marvell 88PM805"
+ depends on I2C=y
select REGMAP_I2C
+ select REGMAP_IRQ
+ select MFD_CORE
+ help
+ This supports for Marvell 88PM805 Power Management IC. This includes
+ the I2C driver and the core APIs _only_, you have to select individual
+ components like codec device, headset/Mic device under the
+ corresponding menus.
+
+config MFD_88PM860X
+ bool "Marvell 88PM8606/88PM8607"
+ depends on I2C=y
+ select REGMAP_I2C
+ select MFD_CORE
+ help
+ This supports for Marvell 88PM8606/88PM8607 Power Management IC.
+ This includes the I2C driver and the core APIs _only_, you have to
+ select individual components like voltage regulators, RTC and
+ battery-charger under the corresponding menus.
+
+config MFD_MAX14577
+ bool "Maxim Semiconductor MAX14577/77836 MUIC + Charger Support"
+ depends on I2C=y
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
select IRQ_DOMAIN
help
- TI LP8788 PMU supports regulators, battery charger, RTC,
- ADC, backlight driver and current sinks.
+ Say yes here to add support for Maxim Semiconductor MAX14577 and
+ MAX77836 Micro-USB ICs with battery charger.
+ This driver provides common support for accessing the device;
+ additional drivers must be enabled in order to use the functionality
+ of the device.
config MFD_MAX77686
bool "Maxim Semiconductor MAX77686 PMIC Support"
- depends on I2C=y && GENERIC_HARDIRQS
+ depends on I2C=y
select MFD_CORE
select REGMAP_I2C
select IRQ_DOMAIN
help
- Say yes here to support for Maxim Semiconductor MAX77686.
+ Say yes here to add support for Maxim Semiconductor MAX77686.
This is a Power Management IC with RTC on chip.
This driver provides common support for accessing the device;
additional drivers must be enabled in order to use the functionality
@@ -495,11 +381,11 @@ config MFD_MAX77686
config MFD_MAX77693
bool "Maxim Semiconductor MAX77693 PMIC Support"
- depends on I2C=y && GENERIC_HARDIRQS
+ depends on I2C=y
select MFD_CORE
select REGMAP_I2C
help
- Say yes here to support for Maxim Semiconductor MAX77693.
+ Say yes here to add support for Maxim Semiconductor MAX77693.
This is a companion Power Management IC with Flash, Haptic, Charger,
and MUIC(Micro USB Interface Controller) controls on chip.
This driver provides common support for accessing the device;
@@ -509,32 +395,32 @@ config MFD_MAX77693
config MFD_MAX8907
tristate "Maxim Semiconductor MAX8907 PMIC Support"
select MFD_CORE
- depends on I2C=y && GENERIC_HARDIRQS
+ depends on I2C=y
select REGMAP_I2C
select REGMAP_IRQ
help
- Say yes here to support for Maxim Semiconductor MAX8907. This is
+ Say yes here to add support for Maxim Semiconductor MAX8907. This is
a Power Management IC. This driver provides common support for
accessing the device; additional drivers must be enabled in order
to use the functionality of the device.
config MFD_MAX8925
bool "Maxim Semiconductor MAX8925 PMIC Support"
- depends on I2C=y && GENERIC_HARDIRQS
+ depends on I2C=y
select MFD_CORE
help
- Say yes here to support for Maxim Semiconductor MAX8925. This is
+ Say yes here to add support for Maxim Semiconductor MAX8925. This is
a Power Management IC. This driver provides common support for
accessing the device, additional drivers must be enabled in order
to use the functionality of the device.
config MFD_MAX8997
bool "Maxim Semiconductor MAX8997/8966 PMIC Support"
- depends on I2C=y && GENERIC_HARDIRQS
+ depends on I2C=y
select MFD_CORE
select IRQ_DOMAIN
help
- Say yes here to support for Maxim Semiconductor MAX8997/8966.
+ Say yes here to add support for Maxim Semiconductor MAX8997/8966.
This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic,
MUIC controls on chip.
This driver provides common support for accessing the device;
@@ -543,185 +429,48 @@ config MFD_MAX8997
config MFD_MAX8998
bool "Maxim Semiconductor MAX8998/National LP3974 PMIC Support"
- depends on I2C=y && GENERIC_HARDIRQS
+ depends on I2C=y
select MFD_CORE
+ select IRQ_DOMAIN
help
- Say yes here to support for Maxim Semiconductor MAX8998 and
+ Say yes here to add support for Maxim Semiconductor MAX8998 and
National Semiconductor LP3974. This is a Power Management IC.
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the functionality
of the device.
-config MFD_SEC_CORE
- bool "SAMSUNG Electronics PMIC Series Support"
- depends on I2C=y && GENERIC_HARDIRQS
- select MFD_CORE
- select REGMAP_I2C
- select REGMAP_IRQ
- help
- Support for the Samsung Electronics MFD series.
- This driver provides common support for accessing the device,
- additional drivers must be enabled in order to use the functionality
- of the device
-
-config MFD_ARIZONA
- select REGMAP
- select REGMAP_IRQ
- select MFD_CORE
- bool
-
-config MFD_ARIZONA_I2C
- tristate "Support Wolfson Microelectronics Arizona platform with I2C"
- select MFD_ARIZONA
- select MFD_CORE
- select REGMAP_I2C
- depends on I2C
- help
- Support for the Wolfson Microelectronics Arizona platform audio SoC
- core functionality controlled via I2C.
-
-config MFD_ARIZONA_SPI
- tristate "Support Wolfson Microelectronics Arizona platform with SPI"
- select MFD_ARIZONA
- select MFD_CORE
- select REGMAP_SPI
+config EZX_PCAP
+ bool "Motorola EZXPCAP Support"
depends on SPI_MASTER
help
- Support for the Wolfson Microelectronics Arizona platform audio SoC
- core functionality controlled via I2C.
-
-config MFD_WM5102
- bool "Support Wolfson Microelectronics WM5102"
- depends on MFD_ARIZONA
- help
- Support for Wolfson Microelectronics WM5102 low power audio SoC
-
-config MFD_WM5110
- bool "Support Wolfson Microelectronics WM5110"
- depends on MFD_ARIZONA
- help
- Support for Wolfson Microelectronics WM5110 low power audio SoC
-
-config MFD_WM8400
- bool "Support Wolfson Microelectronics WM8400"
- select MFD_CORE
- depends on I2C=y
- select REGMAP_I2C
- help
- Support for the Wolfson Microelecronics WM8400 PMIC and audio
- CODEC. This driver provides common support for accessing
- the device, additional drivers must be enabled in order to use
- the functionality of the device.
-
-config MFD_WM831X
- bool
- depends on GENERIC_HARDIRQS
-
-config MFD_WM831X_I2C
- bool "Support Wolfson Microelectronics WM831x/2x PMICs with I2C"
- select MFD_CORE
- select MFD_WM831X
- select REGMAP_I2C
- select IRQ_DOMAIN
- depends on I2C=y && GENERIC_HARDIRQS
- help
- Support for the Wolfson Microelecronics WM831x and WM832x PMICs
- when controlled using I2C. This driver provides common support
- for accessing the device, additional drivers must be enabled in
- order to use the functionality of the device.
+ This enables the PCAP ASIC present on EZX Phones. This is
+ needed for MMC, TouchScreen, Sound, USB, etc..
-config MFD_WM831X_SPI
- bool "Support Wolfson Microelectronics WM831x/2x PMICs with SPI"
+config MFD_VIPERBOARD
+ tristate "Nano River Technologies Viperboard"
select MFD_CORE
- select MFD_WM831X
- select REGMAP_SPI
- select IRQ_DOMAIN
- depends on SPI_MASTER && GENERIC_HARDIRQS
- help
- Support for the Wolfson Microelecronics WM831x and WM832x PMICs
- when controlled using SPI. This driver provides common support
- for accessing the device, additional drivers must be enabled in
- order to use the functionality of the device.
-
-config MFD_WM8350
- bool
- depends on GENERIC_HARDIRQS
-
-config MFD_WM8350_CONFIG_MODE_0
- bool
- depends on MFD_WM8350
-
-config MFD_WM8350_CONFIG_MODE_1
- bool
- depends on MFD_WM8350
-
-config MFD_WM8350_CONFIG_MODE_2
- bool
- depends on MFD_WM8350
-
-config MFD_WM8350_CONFIG_MODE_3
- bool
- depends on MFD_WM8350
-
-config MFD_WM8351_CONFIG_MODE_0
- bool
- depends on MFD_WM8350
-
-config MFD_WM8351_CONFIG_MODE_1
- bool
- depends on MFD_WM8350
-
-config MFD_WM8351_CONFIG_MODE_2
- bool
- depends on MFD_WM8350
-
-config MFD_WM8351_CONFIG_MODE_3
- bool
- depends on MFD_WM8350
-
-config MFD_WM8352_CONFIG_MODE_0
- bool
- depends on MFD_WM8350
-
-config MFD_WM8352_CONFIG_MODE_1
- bool
- depends on MFD_WM8350
-
-config MFD_WM8352_CONFIG_MODE_2
- bool
- depends on MFD_WM8350
-
-config MFD_WM8352_CONFIG_MODE_3
- bool
- depends on MFD_WM8350
-
-config MFD_WM8350_I2C
- bool "Support Wolfson Microelectronics WM8350 with I2C"
- select MFD_WM8350
- depends on I2C=y && GENERIC_HARDIRQS
+ depends on USB
+ default n
help
- The WM8350 is an integrated audio and power management
- subsystem with watchdog and RTC functionality for embedded
- systems. This option enables core support for the WM8350 with
- I2C as the control interface. Additional options must be
- selected to enable support for the functionality of the chip.
+ Say yes here if you want support for Nano River Technologies
+ Viperboard.
+ There are mfd cell drivers available for i2c master, adc and
+ both gpios found on the board. The spi part does not yet
+ have a driver.
+ You need to select the mfd cell drivers separately.
+ The drivers do not support all features the board exposes.
-config MFD_WM8994
- bool "Support Wolfson Microelectronics WM8994"
+config MFD_RETU
+ tristate "Nokia Retu and Tahvo multi-function device"
select MFD_CORE
- select REGMAP_I2C
+ depends on I2C
select REGMAP_IRQ
- depends on I2C=y && GENERIC_HARDIRQS
help
- The WM8994 is a highly integrated hi-fi CODEC designed for
- smartphone applicatiosn. As well as audio functionality it
- has on board GPIO and regulator functionality which is
- supported via the relevant subsystems. This driver provides
- core support for the WM8994, in order to use the actual
- functionaltiy of the device other drivers must be enabled.
+ Retu and Tahvo are a multi-function devices found on Nokia
+ Internet Tablets (770, N800 and N810).
config MFD_PCF50633
- tristate "Support for NXP PCF50633"
+ tristate "NXP PCF50633"
depends on I2C
select REGMAP_I2C
help
@@ -731,48 +480,147 @@ config MFD_PCF50633
so that function-specific drivers can bind to them.
config PCF50633_ADC
- tristate "Support for NXP PCF50633 ADC"
+ tristate "NXP PCF50633 ADC"
depends on MFD_PCF50633
help
Say yes here if you want to include support for ADC in the
NXP PCF50633 chip.
config PCF50633_GPIO
- tristate "Support for NXP PCF50633 GPIO"
+ tristate "NXP PCF50633 GPIO"
depends on MFD_PCF50633
help
Say yes here if you want to include support GPIO for pins on
the PCF50633 chip.
-config MFD_MC13783
- tristate
+config UCB1400_CORE
+ tristate "Philips UCB1400 Core driver"
+ depends on AC97_BUS
+ depends on GPIOLIB
+ help
+ This enables support for the Philips UCB1400 core functions.
+ The UCB1400 is an AC97 audio codec.
-config MFD_MC13XXX
+ To compile this driver as a module, choose M here: the
+ module will be called ucb1400_core.
+
+config MFD_PM8XXX
tristate
- depends on SPI_MASTER || I2C
+
+config MFD_PM8921_CORE
+ tristate "Qualcomm PM8921 PMIC chip"
+ depends on (ARM || HEXAGON)
+ select IRQ_DOMAIN
select MFD_CORE
- select MFD_MC13783
+ select MFD_PM8XXX
+ select REGMAP
help
- Enable support for the Freescale MC13783 and MC13892 PMICs.
- This driver provides common support for accessing the device,
- additional drivers must be enabled in order to use the
- functionality of the device.
+ If you say yes to this option, support will be included for the
+ built-in PM8921 PMIC chip.
-config MFD_MC13XXX_SPI
- tristate "Freescale MC13783 and MC13892 SPI interface"
- depends on SPI_MASTER
- select REGMAP_SPI
- select MFD_MC13XXX
+ This is required if your board has a PM8921 and uses its features,
+ such as: MPPs, GPIOs, regulators, interrupts, and PWM.
+
+ Say M here if you want to include support for PM8921 chip as a module.
+ This will build a module called "pm8921-core".
+
+config MFD_RDC321X
+ tristate "RDC R-321x southbridge"
+ select MFD_CORE
+ depends on PCI
help
- Select this if your MC13xxx is connected via an SPI bus.
+ Say yes here if you want to have support for the RDC R-321x SoC
+ southbridge which provides access to GPIOs and Watchdog using the
+ southbridge PCI device configuration space.
-config MFD_MC13XXX_I2C
- tristate "Freescale MC13892 I2C interface"
+config MFD_RTSX_PCI
+ tristate "Realtek PCI-E card reader"
+ depends on PCI
+ select MFD_CORE
+ help
+ This supports for Realtek PCI-Express card reader including rts5209,
+ rts5229, rtl8411, etc. Realtek card reader supports access to many
+ types of memory cards, such as Memory Stick, Memory Stick Pro,
+ Secure Digital and MultiMediaCard.
+
+config MFD_RTSX_USB
+ tristate "Realtek USB card reader"
+ depends on USB
+ select MFD_CORE
+ help
+ Select this option to get support for Realtek USB 2.0 card readers
+ including RTS5129, RTS5139, RTS5179 and RTS5170.
+ Realtek card reader supports access to many types of memory cards,
+ such as Memory Stick Pro, Secure Digital and MultiMediaCard.
+
+config MFD_RC5T583
+ bool "Ricoh RC5T583 Power Management system device"
+ depends on I2C=y
+ select MFD_CORE
+ select REGMAP_I2C
+ help
+ Select this option to get support for the RICOH583 Power
+ Management system device.
+ This driver provides common support for accessing the device
+ through i2c interface. The device supports multiple sub-devices
+ like GPIO, interrupts, RTC, LDO and DCDC regulators, onkey.
+ Additional drivers must be enabled in order to use the
+ different functionality of the device.
+
+config MFD_SEC_CORE
+ bool "SAMSUNG Electronics PMIC Series Support"
+ depends on I2C=y
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ Support for the Samsung Electronics MFD series.
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the functionality
+ of the device
+
+config MFD_SI476X_CORE
+ tristate "Silicon Laboratories 4761/64/68 AM/FM radio."
depends on I2C
+ select MFD_CORE
select REGMAP_I2C
- select MFD_MC13XXX
help
- Select this if your MC13xxx is connected via an I2C bus.
+ This is the core driver for the SI476x series of AM/FM
+ radio. This MFD driver connects the radio-si476x V4L2 module
+ and the si476x audio codec.
+
+ To compile this driver as a module, choose M here: the
+ module will be called si476x-core.
+
+config MFD_SM501
+ tristate "Silicon Motion SM501"
+ ---help---
+ This is the core driver for the Silicon Motion SM501 multimedia
+ companion chip. This device is a multifunction device which may
+ provide numerous interfaces including USB host controller, USB gadget,
+ asynchronous serial ports, audio functions, and a dual display video
+ interface. The device may be connected by PCI or local bus with
+ varying functions enabled.
+
+config MFD_SM501_GPIO
+ bool "Export GPIO via GPIO layer"
+ depends on MFD_SM501 && GPIOLIB
+ ---help---
+ This option uses the gpio library layer to export the 64 GPIO
+ lines on the SM501. The platform data is used to supply the
+ base number for the first GPIO line to register.
+
+config MFD_SMSC
+ bool "SMSC ECE1099 series chips"
+ depends on I2C=y
+ select MFD_CORE
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for the
+ ece1099 chips from SMSC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called smsc.
config ABX500_CORE
bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
@@ -808,16 +656,10 @@ config AB3100_OTP
programmable memory) support. This exposes a sysfs file to read
out OTP values.
-config EZX_PCAP
- bool "PCAP Support"
- depends on GENERIC_HARDIRQS && SPI_MASTER
- help
- This enables the PCAP ASIC present on EZX Phones. This is
- needed for MMC, TouchScreen, Sound, USB, etc..
-
config AB8500_CORE
bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
- depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU
+ depends on ABX500_CORE && MFD_DB8500_PRCMU
+ select POWER_SUPPLY
select MFD_CORE
select IRQ_DOMAIN
help
@@ -829,14 +671,14 @@ config AB8500_CORE
config AB8500_DEBUG
bool "Enable debug info via debugfs"
- depends on AB8500_CORE && DEBUG_FS
+ depends on AB8500_GPADC && DEBUG_FS
default y if DEBUG_FS
help
Select this option if you want debug information using the debug
filesystem, debugfs.
config AB8500_GPADC
- bool "AB8500 GPADC driver"
+ bool "ST-Ericsson AB8500 GPADC driver"
depends on AB8500_CORE && REGULATOR_AB8500
default y
help
@@ -852,92 +694,116 @@ config MFD_DB8500_PRCMU
system controller running an XP70 microprocessor, which is accessed
through a register map.
-config MFD_CS5535
- tristate "Support for CS5535 and CS5536 southbridge core functions"
+config MFD_STMPE
+ bool "STMicroelectronics STMPE"
+ depends on (I2C=y || SPI_MASTER=y)
+ depends on OF
select MFD_CORE
- depends on PCI && X86
- ---help---
- This is the core driver for CS5535/CS5536 MFD functions. This is
- necessary for using the board's GPIO and MFGPT functionality.
+ help
+ Support for the STMPE family of I/O Expanders from
+ STMicroelectronics.
-config MFD_TIMBERDALE
- tristate "Support for the Timberdale FPGA"
- select MFD_CORE
- depends on PCI && GPIOLIB
- ---help---
- This is the core driver for the timberdale FPGA. This device is a
- multifunction device which exposes numerous platform devices.
+ Currently supported devices are:
- The timberdale FPGA can be found on the Intel Atom development board
- for in-vehicle infontainment, called Russellville.
+ STMPE811: GPIO, Touchscreen
+ STMPE1601: GPIO, Keypad
+ STMPE1801: GPIO, Keypad
+ STMPE2401: GPIO, Keypad
+ STMPE2403: GPIO, Keypad
-config LPC_SCH
- tristate "Intel SCH LPC"
- depends on PCI
- select MFD_CORE
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the functionality
+ of the device. Currently available sub drivers are:
+
+ GPIO: stmpe-gpio
+ Keypad: stmpe-keypad
+ Touchscreen: stmpe-ts
+
+menu "STMicroelectronics STMPE Interface Drivers"
+depends on MFD_STMPE
+
+config STMPE_I2C
+ bool "STMicroelectronics STMPE I2C Interface"
+ depends on I2C=y
+ default y
help
- LPC bridge function of the Intel SCH provides support for
- System Management Bus and General Purpose I/O.
+ This is used to enable I2C interface of STMPE
-config LPC_ICH
- tristate "Intel ICH LPC"
- depends on PCI
- select MFD_CORE
+config STMPE_SPI
+ bool "STMicroelectronics STMPE SPI Interface"
+ depends on SPI_MASTER
help
- The LPC bridge function of the Intel ICH provides support for
- many functional units. This driver provides needed support for
- other drivers to control these functions, currently GPIO and
- watchdog.
+ This is used to enable SPI interface of STMPE
+endmenu
-config MFD_RDC321X
- tristate "Support for RDC-R321x southbridge"
+config MFD_STA2X11
+ bool "STMicroelectronics STA2X11"
+ depends on STA2X11
select MFD_CORE
- depends on PCI
- help
- Say yes here if you want to have support for the RDC R-321x SoC
- southbridge which provides access to GPIOs and Watchdog using the
- southbridge PCI device configuration space.
+ select REGMAP_MMIO
-config MFD_JANZ_CMODIO
- tristate "Support for Janz CMOD-IO PCI MODULbus Carrier Board"
+config MFD_SUN6I_PRCM
+ bool "Allwinner A31 PRCM controller"
+ depends on ARCH_SUNXI
select MFD_CORE
- depends on PCI
help
- This is the core driver for the Janz CMOD-IO PCI MODULbus
- carrier board. This device is a PCI to MODULbus bridge which may
- host many different types of MODULbus daughterboards, including
- CAN and GPIO controllers.
+ Support for the PRCM (Power/Reset/Clock Management) unit available
+ in A31 SoC.
-config MFD_JZ4740_ADC
- bool "Support for the JZ4740 SoC ADC core"
- select MFD_CORE
- select GENERIC_IRQ_CHIP
- depends on MACH_JZ4740
+config MFD_SYSCON
+ bool "System Controller Register R/W Based on Regmap"
+ select REGMAP_MMIO
help
- Say yes here if you want support for the ADC unit in the JZ4740 SoC.
- This driver is necessary for jz4740-battery and jz4740-hwmon driver.
+ Select this option to enable accessing system control registers
+ via regmap.
-config MFD_VX855
- tristate "Support for VIA VX855/VX875 integrated south bridge"
- depends on PCI
+config MFD_DAVINCI_VOICECODEC
+ tristate
+ select MFD_CORE
+ select REGMAP_MMIO
+
+config MFD_TI_AM335X_TSCADC
+ tristate "TI ADC / Touch Screen chip support"
select MFD_CORE
+ select REGMAP
+ select REGMAP_MMIO
help
- Say yes here to enable support for various functions of the
- VIA VX855/VX875 south bridge. You will need to enable the vx855_spi
- and/or vx855_gpio drivers for this to do anything useful.
+ If you say yes here you get support for Texas Instruments series
+ of Touch Screen /ADC chips.
+ To compile this driver as a module, choose M here: the
+ module will be called ti_am335x_tscadc.
-config MFD_WL1273_CORE
- tristate "Support for TI WL1273 FM radio."
+config MFD_DM355EVM_MSP
+ bool "TI DaVinci DM355 EVM microcontroller"
+ depends on I2C=y && MACH_DAVINCI_DM355_EVM
+ help
+ This driver supports the MSP430 microcontroller used on these
+ boards. MSP430 firmware manages resets and power sequencing,
+ inputs from buttons and the IR remote, LEDs, an RTC, and more.
+
+config MFD_LP3943
+ tristate "TI/National Semiconductor LP3943 MFD Driver"
depends on I2C
select MFD_CORE
- default n
+ select REGMAP_I2C
help
- This is the core driver for the TI WL1273 FM radio. This MFD
- driver connects the radio-wl1273 V4L2 module and the wl1273
- audio codec.
+ Support for the TI/National Semiconductor LP3943.
+ This driver consists of GPIO and PWM drivers.
+ With these functionalities, it can be used for LED string control or
+ general usage such like a GPIO controller and a PWM controller.
+
+config MFD_LP8788
+ bool "TI LP8788 Power Management Unit Driver"
+ depends on I2C=y
+ select MFD_CORE
+ select REGMAP_I2C
+ select IRQ_DOMAIN
+ help
+ TI LP8788 PMU supports regulators, battery charger, RTC,
+ ADC, backlight driver and current sinks.
config MFD_OMAP_USB_HOST
- bool "Support OMAP USBHS core and TLL driver"
+ bool "TI OMAP USBHS core and TLL driver"
depends on USB_EHCI_HCD_OMAP || USB_OHCI_HCD_OMAP3
default y
help
@@ -945,42 +811,62 @@ config MFD_OMAP_USB_HOST
This MFD driver does the required setup functionalities for
OMAP USB Host drivers.
-config MFD_PM8XXX
- tristate
+config MFD_PALMAS
+ bool "TI Palmas series chips"
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ depends on I2C=y
+ help
+ If you say yes here you get support for the Palmas
+ series of PMIC chips from Texas Instruments.
-config MFD_PM8921_CORE
- tristate "Qualcomm PM8921 PMIC chip"
- depends on MSM_SSBI
+config TPS6105X
+ tristate "TI TPS61050/61052 Boost Converters"
+ depends on I2C
+ select REGULATOR
select MFD_CORE
- select MFD_PM8XXX
+ select REGULATOR_FIXED_VOLTAGE
help
- If you say yes to this option, support will be included for the
- built-in PM8921 PMIC chip.
+ This option enables a driver for the TP61050/TPS61052
+ high-power "white LED driver". This boost converter is
+ sometimes used for other things than white LEDs, and
+ also contains a GPIO pin.
- This is required if your board has a PM8921 and uses its features,
- such as: MPPs, GPIOs, regulators, interrupts, and PWM.
+config TPS65010
+ tristate "TI TPS6501x Power Management chips"
+ depends on I2C && GPIOLIB
+ default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK
+ help
+ If you say yes here you get support for the TPS6501x series of
+ Power Management chips. These include voltage regulators,
+ lithium ion/polymer battery charging, and other features that
+ are often used in portable devices like cell phones and cameras.
- Say M here if you want to include support for PM8921 chip as a module.
- This will build a module called "pm8921-core".
+ This driver can also be built as a module. If so, the module
+ will be called tps65010.
-config MFD_PM8XXX_IRQ
- bool "Support for Qualcomm PM8xxx IRQ features"
- depends on MFD_PM8XXX
- default y if MFD_PM8XXX
+config TPS6507X
+ tristate "TI TPS6507x Power Management / Touch Screen chips"
+ select MFD_CORE
+ depends on I2C
help
- This is the IRQ driver for Qualcomm PM 8xxx PMIC chips.
-
- This is required to use certain other PM 8xxx features, such as GPIO
- and MPP.
+ If you say yes here you get support for the TPS6507x series of
+ Power Management / Touch Screen chips. These include voltage
+ regulators, lithium ion/polymer battery charging, touch screen
+ and other features that are often used in portable devices.
+ This driver can also be built as a module. If so, the module
+ will be called tps6507x.
config TPS65911_COMPARATOR
tristate
config MFD_TPS65090
- bool "TPS65090 Power Management chips"
- depends on I2C=y && GENERIC_HARDIRQS
+ bool "TI TPS65090 Power Management chips"
+ depends on I2C=y
select MFD_CORE
select REGMAP_I2C
+ select REGMAP_IRQ
help
If you say yes here you get support for the TPS65090 series of
Power Management chips.
@@ -988,64 +874,366 @@ config MFD_TPS65090
additional drivers must be enabled in order to use the
functionality of the device.
-config MFD_AAT2870_CORE
- bool "Support for the AnalogicTech AAT2870"
+config MFD_TPS65217
+ tristate "TI TPS65217 Power Management / White LED chips"
+ depends on I2C
+ select MFD_CORE
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for the TPS65217 series of
+ Power Management / White LED chips.
+ These include voltage regulators, lithium ion/polymer battery
+ charger, wled and other features that are often used in portable
+ devices.
+
+ This driver can also be built as a module. If so, the module
+ will be called tps65217.
+
+config MFD_TPS65218
+ tristate "TI TPS65218 Power Management chips"
+ depends on I2C
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ If you say yes here you get support for the TPS65218 series of
+ Power Management chips.
+ These include voltage regulators, gpio and other features
+ that are often used in portable devices. Only regulator
+ component is currently supported.
+
+ This driver can also be built as a module. If so, the module
+ will be called tps65218.
+
+config MFD_TPS6586X
+ bool "TI TPS6586x Power Management chips"
+ depends on I2C=y
select MFD_CORE
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for the TPS6586X series of
+ Power Management chips.
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device.
+
+ This driver can also be built as a module. If so, the module
+ will be called tps6586x.
+
+config MFD_TPS65910
+ bool "TI TPS65910 Power Management chip"
depends on I2C=y && GPIOLIB
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ select IRQ_DOMAIN
help
- If you say yes here you get support for the AAT2870.
+ if you say yes here you get support for the TPS65910 series of
+ Power Management chips.
+
+config MFD_TPS65912
+ bool "TI TPS65912 Power Management chip"
+ depends on GPIOLIB
+ select MFD_CORE
+ help
+ If you say yes here you get support for the TPS65912 series of
+ PM chips.
+
+config MFD_TPS65912_I2C
+ bool "TI TPS65912 Power Management chip with I2C"
+ select MFD_CORE
+ select MFD_TPS65912
+ depends on I2C=y && GPIOLIB
+ help
+ If you say yes here you get support for the TPS65912 series of
+ PM chips with I2C interface.
+
+config MFD_TPS65912_SPI
+ bool "TI TPS65912 Power Management chip with SPI"
+ select MFD_CORE
+ select MFD_TPS65912
+ depends on SPI_MASTER && GPIOLIB
+ help
+ If you say yes here you get support for the TPS65912 series of
+ PM chips with SPI interface.
+
+config MFD_TPS80031
+ bool "TI TPS80031/TPS80032 Power Management chips"
+ depends on I2C=y
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ If you say yes here you get support for the Texas Instruments
+ TPS80031/ TPS80032 Fully Integrated Power Management with Power
+ Path and Battery Charger. The device provides five configurable
+ step-down converters, 11 general purpose LDOs, USB OTG Module,
+ ADC, RTC, 2 PWM, System Voltage Regulator/Battery Charger with
+ Power Path from USB, 32K clock generator.
+
+config TWL4030_CORE
+ bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 Support"
+ depends on I2C=y
+ select IRQ_DOMAIN
+ select REGMAP_I2C
+ help
+ Say yes here if you have TWL4030 / TWL6030 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 TWL4030_POWER
+ bool "TI TWL4030 power resources"
+ depends on TWL4030_CORE && ARM
+ help
+ Say yes here if you want to use the power resources on the
+ TWL4030 family chips. Most of these resources are regulators,
+ which have a separate driver; some are control signals, such
+ as clock request handshaking.
+
+ This driver uses board-specific data to initialize the resources
+ and load scripts controlling which resources are switched off/on
+ or reset when a sleep, wakeup or warm reset event occurs.
+
+config MFD_TWL4030_AUDIO
+ bool "TI TWL4030 Audio"
+ depends on TWL4030_CORE
+ select MFD_CORE
+ default n
+
+config TWL6040_CORE
+ bool "TI TWL6040 audio codec"
+ depends on I2C=y
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ default n
+ help
+ Say yes here if you want support for Texas Instruments TWL6040 audio
+ codec.
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device (audio, vibra).
+
+config MENELAUS
+ bool "TI TWL92330/Menelaus PM chip"
+ depends on I2C=y && ARCH_OMAP2
+ help
+ If you say yes here you get support for the Texas Instruments
+ TWL92330/Menelaus Power Management chip. This include voltage
+ regulators, Dual slot memory card transceivers, real-time clock
+ and other features that are often used in portable devices like
+ cell phones and PDAs.
+
+config MFD_WL1273_CORE
+ tristate "TI WL1273 FM radio"
+ depends on I2C
+ select MFD_CORE
+ default n
+ help
+ This is the core driver for the TI WL1273 FM radio. This MFD
+ driver connects the radio-wl1273 V4L2 module and the wl1273
+ audio codec.
+
+config MFD_LM3533
+ tristate "TI/National Semiconductor LM3533 Lighting Power chip"
+ depends on I2C
+ select MFD_CORE
+ select REGMAP_I2C
+ help
+ Say yes here to enable support for National Semiconductor / TI
+ LM3533 Lighting Power chips.
+
+ This driver provides common support for accessing the device;
+ additional drivers must be enabled in order to use the LED,
+ backlight or ambient-light-sensor functionality of the device.
+
+config MFD_TIMBERDALE
+ tristate "Timberdale FPGA"
+ select MFD_CORE
+ depends on PCI && GPIOLIB
+ ---help---
+ This is the core driver for the timberdale FPGA. This device is a
+ multifunction device which exposes numerous platform devices.
+
+ The timberdale FPGA can be found on the Intel Atom development board
+ for in-vehicle infontainment, called Russellville.
+
+config MFD_TC3589X
+ bool "Toshiba TC35892 and variants"
+ depends on I2C=y
+ select MFD_CORE
+ help
+ Support for the Toshiba TC35892 and variants I/O Expander.
+
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the
functionality of the device.
-config MFD_INTEL_MSIC
- bool "Support for Intel MSIC"
- depends on INTEL_SCU_IPC
+config MFD_TMIO
+ bool
+ default n
+
+config MFD_T7L66XB
+ bool "Toshiba T7L66XB"
+ depends on ARM && HAVE_CLK
select MFD_CORE
+ select MFD_TMIO
help
- Select this option to enable access to Intel MSIC (Avatele
- Passage) chip. This chip embeds audio, battery, GPIO, etc.
- devices used in Intel Medfield platforms.
+ Support for Toshiba Mobile IO Controller T7L66XB
-config MFD_RC5T583
- bool "Ricoh RC5T583 Power Management system device"
- depends on I2C=y && GENERIC_HARDIRQS
+config MFD_TC6387XB
+ bool "Toshiba TC6387XB"
+ depends on ARM && HAVE_CLK
+ select MFD_CORE
+ select MFD_TMIO
+ help
+ Support for Toshiba Mobile IO Controller TC6387XB
+
+config MFD_TC6393XB
+ bool "Toshiba TC6393XB"
+ depends on ARM && HAVE_CLK
+ select GPIOLIB
+ select MFD_CORE
+ select MFD_TMIO
+ help
+ Support for Toshiba Mobile IO Controller TC6393XB
+
+config MFD_VX855
+ tristate "VIA VX855/VX875 integrated south bridge"
+ depends on PCI
+ select MFD_CORE
+ help
+ Say yes here to enable support for various functions of the
+ VIA VX855/VX875 south bridge. You will need to enable the vx855_spi
+ and/or vx855_gpio drivers for this to do anything useful.
+
+config MFD_ARIZONA
+ select REGMAP
+ select REGMAP_IRQ
+ select MFD_CORE
+ bool
+
+config MFD_ARIZONA_I2C
+ tristate "Wolfson Microelectronics Arizona platform with I2C"
+ select MFD_ARIZONA
select MFD_CORE
select REGMAP_I2C
+ depends on I2C
help
- Select this option to get support for the RICOH583 Power
- Management system device.
- This driver provides common support for accessing the device
- through i2c interface. The device supports multiple sub-devices
- like GPIO, interrupts, RTC, LDO and DCDC regulators, onkey.
- Additional drivers must be enabled in order to use the
- different functionality of the device.
+ Support for the Wolfson Microelectronics Arizona platform audio SoC
+ core functionality controlled via I2C.
-config MFD_STA2X11
- bool "STA2X11 multi function device support"
- depends on STA2X11
+config MFD_ARIZONA_SPI
+ tristate "Wolfson Microelectronics Arizona platform with SPI"
+ select MFD_ARIZONA
select MFD_CORE
+ select REGMAP_SPI
+ depends on SPI_MASTER
+ help
+ Support for the Wolfson Microelectronics Arizona platform audio SoC
+ core functionality controlled via I2C.
-config MFD_SYSCON
- bool "System Controller Register R/W Based on Regmap"
- depends on OF
- select REGMAP_MMIO
+config MFD_WM5102
+ bool "Wolfson Microelectronics WM5102"
+ depends on MFD_ARIZONA
help
- Select this option to enable accessing system control registers
- via regmap.
+ Support for Wolfson Microelectronics WM5102 low power audio SoC
-config MFD_PALMAS
- bool "Support for the TI Palmas series chips"
+config MFD_WM5110
+ bool "Wolfson Microelectronics WM5110"
+ depends on MFD_ARIZONA
+ help
+ Support for Wolfson Microelectronics WM5110 low power audio SoC
+
+config MFD_WM8997
+ bool "Wolfson Microelectronics WM8997"
+ depends on MFD_ARIZONA
+ help
+ Support for Wolfson Microelectronics WM8997 low power audio SoC
+
+config MFD_WM8400
+ bool "Wolfson Microelectronics WM8400"
+ select MFD_CORE
+ depends on I2C=y
+ select REGMAP_I2C
+ help
+ Support for the Wolfson Microelecronics WM8400 PMIC and audio
+ CODEC. This driver provides common support for accessing
+ the device, additional drivers must be enabled in order to use
+ the functionality of the device.
+
+config MFD_WM831X
+ bool
+
+config MFD_WM831X_I2C
+ bool "Wolfson Microelectronics WM831x/2x PMICs with I2C"
+ select MFD_CORE
+ select MFD_WM831X
+ select REGMAP_I2C
+ select IRQ_DOMAIN
+ depends on I2C=y
+ help
+ Support for the Wolfson Microelecronics WM831x and WM832x PMICs
+ when controlled using I2C. This driver provides common support
+ for accessing the device, additional drivers must be enabled in
+ order to use the functionality of the device.
+
+config MFD_WM831X_SPI
+ bool "Wolfson Microelectronics WM831x/2x PMICs with SPI"
+ select MFD_CORE
+ select MFD_WM831X
+ select REGMAP_SPI
+ select IRQ_DOMAIN
+ depends on SPI_MASTER
+ help
+ Support for the Wolfson Microelecronics WM831x and WM832x PMICs
+ when controlled using SPI. This driver provides common support
+ for accessing the device, additional drivers must be enabled in
+ order to use the functionality of the device.
+
+config MFD_WM8350
+ bool
+
+config MFD_WM8350_I2C
+ bool "Wolfson Microelectronics WM8350 with I2C"
+ select MFD_WM8350
+ depends on I2C=y
+ help
+ The WM8350 is an integrated audio and power management
+ subsystem with watchdog and RTC functionality for embedded
+ systems. This option enables core support for the WM8350 with
+ I2C as the control interface. Additional options must be
+ selected to enable support for the functionality of the chip.
+
+config MFD_WM8994
+ bool "Wolfson Microelectronics WM8994"
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
depends on I2C=y
help
- If you say yes here you get support for the Palmas
- series of PMIC chips from Texas Instruments.
+ The WM8994 is a highly integrated hi-fi CODEC designed for
+ smartphone applicatiosn. As well as audio functionality it
+ has on board GPIO and regulator functionality which is
+ supported via the relevant subsystems. This driver provides
+ core support for the WM8994, in order to use the actual
+ functionaltiy of the device other drivers must be enabled.
-endmenu
-endif
+config MFD_STW481X
+ tristate "Support for ST Microelectronics STw481x"
+ depends on I2C && ARCH_NOMADIK
+ select REGMAP_I2C
+ select MFD_CORE
+ help
+ Select this option to enable the STw481x chip driver used
+ in various ST Microelectronics and ST-Ericsson embedded
+ Nomadik series.
menu "Multimedia Capabilities Port drivers"
depends on ARCH_SA1100
@@ -1061,7 +1249,7 @@ config MCP_SA11X0
# Chip drivers
config MCP_UCB1200
- bool "Support for UCB1200 / UCB1300"
+ tristate "Support for UCB1200 / UCB1300"
depends on MCP_SA11X0
select MCP
@@ -1070,3 +1258,18 @@ config MCP_UCB1200_TS
depends on MCP_UCB1200 && INPUT
endmenu
+
+config MFD_VEXPRESS_SYSREG
+ bool "Versatile Express System Registers"
+ depends on VEXPRESS_CONFIG && GPIOLIB
+ default y
+ select CLKSRC_MMIO
+ select GPIO_GENERIC_PLATFORM
+ select MFD_CORE
+ select MFD_SYSCON
+ help
+ System Registers are the platform configuration block
+ on the ARM Ltd. Versatile Express board.
+
+endmenu
+endif
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index d8ccb630ddb..8afedba535c 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -8,6 +8,14 @@ obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o
obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
obj-$(CONFIG_MFD_SM501) += sm501.o
obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
+obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
+obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o
+obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o
+obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o
+
+rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o
+obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o
+obj-$(CONFIG_MFD_RTSX_USB) += rtsx_usb.o
obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
@@ -15,12 +23,13 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
-obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o
+obj-$(CONFIG_MFD_TI_AM335X_TSCADC) += ti_am335x_tscadc.o
obj-$(CONFIG_MFD_STA2X11) += sta2x11-mfd.o
obj-$(CONFIG_MFD_STMPE) += stmpe.o
obj-$(CONFIG_STMPE_I2C) += stmpe-i2c.o
obj-$(CONFIG_STMPE_SPI) += stmpe-spi.o
+obj-$(CONFIG_MFD_SUN6I_PRCM) += sun6i-prcm.o
obj-$(CONFIG_MFD_TC3589X) += tc3589x.o
obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o
obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o
@@ -36,6 +45,9 @@ endif
ifneq ($(CONFIG_MFD_WM5110),n)
obj-$(CONFIG_MFD_ARIZONA) += wm5110-tables.o
endif
+ifneq ($(CONFIG_MFD_WM8997),n)
+obj-$(CONFIG_MFD_ARIZONA) += wm8997-tables.o
+endif
obj-$(CONFIG_MFD_WM8400) += wm8400-core.o
wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o
wm831x-objs += wm831x-auxadc.o
@@ -52,18 +64,19 @@ obj-$(CONFIG_TPS6105X) += tps6105x.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_TPS6507X) += tps6507x.o
obj-$(CONFIG_MFD_TPS65217) += tps65217.o
-obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o
+obj-$(CONFIG_MFD_TPS65218) += tps65218.o
+obj-$(CONFIG_MFD_TPS65910) += tps65910.o
tps65912-objs := tps65912-core.o tps65912-irq.o
obj-$(CONFIG_MFD_TPS65912) += tps65912.o
obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o
obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o
+obj-$(CONFIG_MFD_TPS80031) += tps80031.o
obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
-obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o
-obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o
+obj-$(CONFIG_TWL6040_CORE) += twl6040.o
obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
@@ -86,15 +99,22 @@ obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o
obj-$(CONFIG_PMIC_DA903X) += da903x.o
+obj-$(CONFIG_PMIC_DA9052) += da9052-irq.o
obj-$(CONFIG_PMIC_DA9052) += da9052-core.o
obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o
obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
+obj-$(CONFIG_MFD_AXP20X) += axp20x.o
+obj-$(CONFIG_MFD_LP3943) += lp3943.o
obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o
da9055-objs := da9055-core.o da9055-i2c.o
obj-$(CONFIG_MFD_DA9055) += da9055.o
+da9063-objs := da9063-core.o da9063-irq.o da9063-i2c.o
+obj-$(CONFIG_MFD_DA9063) += da9063.o
+
+obj-$(CONFIG_MFD_MAX14577) += max14577.o
obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o
obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o
obj-$(CONFIG_MFD_MAX8907) += max8907.o
@@ -117,6 +137,7 @@ obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o
obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o
obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o
obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
+obj-$(CONFIG_MFD_KEMPLD) += kempld-core.o
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
obj-$(CONFIG_LPC_ICH) += lpc_ich.o
obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
@@ -125,16 +146,26 @@ obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o
obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o
obj-$(CONFIG_MFD_VX855) += vx855.o
obj-$(CONFIG_MFD_WL1273_CORE) += wl1273-core.o
+
+si476x-core-y := si476x-cmd.o si476x-prop.o si476x-i2c.o
+obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o
+
obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o
-obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o
-obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
+obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
obj-$(CONFIG_MFD_TPS65090) += tps65090.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
obj-$(CONFIG_MFD_PALMAS) += palmas.o
+obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o
obj-$(CONFIG_MFD_SYSCON) += syscon.o
obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o
+obj-$(CONFIG_MFD_VEXPRESS_SYSREG) += vexpress-sysreg.o
+obj-$(CONFIG_MFD_RETU) += retu-mfd.o
+obj-$(CONFIG_MFD_AS3711) += as3711.o
+obj-$(CONFIG_MFD_AS3722) += as3722.o
+obj-$(CONFIG_MFD_STW481X) += stw481x.o
+obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o
diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c
index f1beb4971f8..14d9542a4ee 100644
--- a/drivers/mfd/aat2870-core.c
+++ b/drivers/mfd/aat2870-core.c
@@ -293,7 +293,7 @@ static ssize_t aat2870_reg_write_file(struct file *file,
unsigned long addr, val;
int ret;
- buf_size = min(count, (sizeof(buf)-1));
+ buf_size = min(count, (size_t)(sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size)) {
dev_err(aat2870->dev, "Failed to copy from user\n");
return -EFAULT;
@@ -312,8 +312,9 @@ static ssize_t aat2870_reg_write_file(struct file *file,
while (*start == ' ')
start++;
- if (strict_strtoul(start, 16, &val))
- return -EINVAL;
+ ret = kstrtoul(start, 16, &val);
+ if (ret)
+ return ret;
ret = aat2870->write(aat2870, (u8)addr, (u8)val);
if (ret)
@@ -362,17 +363,17 @@ static inline void aat2870_uninit_debugfs(struct aat2870_data *aat2870)
static int aat2870_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct aat2870_platform_data *pdata = client->dev.platform_data;
+ struct aat2870_platform_data *pdata = dev_get_platdata(&client->dev);
struct aat2870_data *aat2870;
int i, j;
int ret = 0;
- aat2870 = kzalloc(sizeof(struct aat2870_data), GFP_KERNEL);
+ aat2870 = devm_kzalloc(&client->dev, sizeof(struct aat2870_data),
+ GFP_KERNEL);
if (!aat2870) {
dev_err(&client->dev,
"Failed to allocate memory for aat2870\n");
- ret = -ENOMEM;
- goto out;
+ return -ENOMEM;
}
aat2870->dev = &client->dev;
@@ -400,12 +401,12 @@ static int aat2870_i2c_probe(struct i2c_client *client,
aat2870->init(aat2870);
if (aat2870->en_pin >= 0) {
- ret = gpio_request_one(aat2870->en_pin, GPIOF_OUT_INIT_HIGH,
- "aat2870-en");
+ ret = devm_gpio_request_one(&client->dev, aat2870->en_pin,
+ GPIOF_OUT_INIT_HIGH, "aat2870-en");
if (ret < 0) {
dev_err(&client->dev,
"Failed to request GPIO %d\n", aat2870->en_pin);
- goto out_kfree;
+ return ret;
}
}
@@ -436,11 +437,6 @@ static int aat2870_i2c_probe(struct i2c_client *client,
out_disable:
aat2870_disable(aat2870);
- if (aat2870->en_pin >= 0)
- gpio_free(aat2870->en_pin);
-out_kfree:
- kfree(aat2870);
-out:
return ret;
}
@@ -452,11 +448,8 @@ static int aat2870_i2c_remove(struct i2c_client *client)
mfd_remove_devices(aat2870->dev);
aat2870_disable(aat2870);
- if (aat2870->en_pin >= 0)
- gpio_free(aat2870->en_pin);
if (aat2870->uninit)
aat2870->uninit(aat2870);
- kfree(aat2870);
return 0;
}
diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c
index 2b3dde571a5..b348ae52062 100644
--- a/drivers/mfd/ab3100-core.c
+++ b/drivers/mfd/ab3100-core.c
@@ -491,7 +491,7 @@ static ssize_t ab3100_get_set_reg(struct file *file,
char buf[32];
ssize_t buf_size;
int regp;
- unsigned long user_reg;
+ u8 user_reg;
int err;
int i = 0;
@@ -514,34 +514,29 @@ static ssize_t ab3100_get_set_reg(struct file *file,
/*
* Advance pointer to end of string then terminate
* the register string. This is needed to satisfy
- * the strict_strtoul() function.
+ * the kstrtou8() function.
*/
while ((i < buf_size) && (buf[i] != ' '))
i++;
buf[i] = '\0';
- err = strict_strtoul(&buf[regp], 16, &user_reg);
+ err = kstrtou8(&buf[regp], 16, &user_reg);
if (err)
return err;
- if (user_reg > 0xff)
- return -EINVAL;
/* Either we read or we write a register here */
if (!priv->mode) {
/* Reading */
- u8 reg = (u8) user_reg;
u8 regvalue;
- ab3100_get_register_interruptible(ab3100, reg, &regvalue);
+ ab3100_get_register_interruptible(ab3100, user_reg, &regvalue);
dev_info(ab3100->dev,
"debug read AB3100 reg[0x%02x]: 0x%02x\n",
- reg, regvalue);
+ user_reg, regvalue);
} else {
int valp;
- unsigned long user_value;
- u8 reg = (u8) user_reg;
- u8 value;
+ u8 user_value;
u8 regvalue;
/*
@@ -557,20 +552,17 @@ static ssize_t ab3100_get_set_reg(struct file *file,
i++;
buf[i] = '\0';
- err = strict_strtoul(&buf[valp], 16, &user_value);
+ err = kstrtou8(&buf[valp], 16, &user_value);
if (err)
return err;
- if (user_reg > 0xff)
- return -EINVAL;
- value = (u8) user_value;
- ab3100_set_register_interruptible(ab3100, reg, value);
- ab3100_get_register_interruptible(ab3100, reg, &regvalue);
+ ab3100_set_register_interruptible(ab3100, user_reg, user_value);
+ ab3100_get_register_interruptible(ab3100, user_reg, &regvalue);
dev_info(ab3100->dev,
"debug write reg[0x%02x] with 0x%02x, "
"after readback: 0x%02x\n",
- reg, value, regvalue);
+ user_reg, user_value, regvalue);
}
return buf_size;
}
@@ -661,8 +653,7 @@ struct ab3100_init_setting {
u8 setting;
};
-static const struct ab3100_init_setting __devinitconst
-ab3100_init_settings[] = {
+static const struct ab3100_init_setting ab3100_init_settings[] = {
{
.abreg = AB3100_MCA,
.setting = 0x01
@@ -708,7 +699,7 @@ ab3100_init_settings[] = {
},
};
-static int __devinit ab3100_setup(struct ab3100 *ab3100)
+static int ab3100_setup(struct ab3100 *ab3100)
{
int err = 0;
int i;
@@ -754,6 +745,7 @@ static struct mfd_cell ab3100_devs[] = {
},
{
.name = "ab3100-regulators",
+ .of_compatible = "stericsson,ab3100-regulators",
.id = -1,
},
{
@@ -803,7 +795,7 @@ struct ab_family_id {
char *name;
};
-static const struct ab_family_id ids[] __devinitconst = {
+static const struct ab_family_id ids[] = {
/* AB3100 */
{
.id = 0xc0,
@@ -857,12 +849,12 @@ static const struct ab_family_id ids[] __devinitconst = {
},
};
-static int __devinit ab3100_probe(struct i2c_client *client,
+static int ab3100_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ab3100 *ab3100;
struct ab3100_platform_data *ab3100_plf_data =
- client->dev.platform_data;
+ dev_get_platdata(&client->dev);
int err;
int i;
@@ -962,7 +954,7 @@ static int __devinit ab3100_probe(struct i2c_client *client,
return err;
}
-static int __devexit ab3100_remove(struct i2c_client *client)
+static int ab3100_remove(struct i2c_client *client)
{
struct ab3100 *ab3100 = i2c_get_clientdata(client);
@@ -986,7 +978,7 @@ static struct i2c_driver ab3100_driver = {
},
.id_table = ab3100_id,
.probe = ab3100_probe,
- .remove = __devexit_p(ab3100_remove),
+ .remove = ab3100_remove,
};
static int __init ab3100_i2c_init(void)
diff --git a/drivers/mfd/ab3100-otp.c b/drivers/mfd/ab3100-otp.c
index 8440010eb2b..c9af16cc731 100644
--- a/drivers/mfd/ab3100-otp.c
+++ b/drivers/mfd/ab3100-otp.c
@@ -187,7 +187,7 @@ static int __init ab3100_otp_probe(struct platform_device *pdev)
int err = 0;
int i;
- otp = kzalloc(sizeof(struct ab3100_otp), GFP_KERNEL);
+ otp = devm_kzalloc(&pdev->dev, sizeof(struct ab3100_otp), GFP_KERNEL);
if (!otp) {
dev_err(&pdev->dev, "could not allocate AB3100 OTP device\n");
return -ENOMEM;
@@ -199,7 +199,7 @@ static int __init ab3100_otp_probe(struct platform_device *pdev)
err = ab3100_otp_read(otp);
if (err)
- goto err_otp_read;
+ return err;
dev_info(&pdev->dev, "AB3100 OTP readout registered\n");
@@ -208,22 +208,19 @@ static int __init ab3100_otp_probe(struct platform_device *pdev)
err = device_create_file(&pdev->dev,
&ab3100_otp_attrs[i]);
if (err)
- goto err_create_file;
+ goto err;
}
/* debugfs entries */
err = ab3100_otp_init_debugfs(&pdev->dev, otp);
if (err)
- goto err_init_debugfs;
+ goto err;
return 0;
-err_init_debugfs:
-err_create_file:
+err:
while (--i >= 0)
device_remove_file(&pdev->dev, &ab3100_otp_attrs[i]);
-err_otp_read:
- kfree(otp);
return err;
}
@@ -236,7 +233,6 @@ static int __exit ab3100_otp_remove(struct platform_device *pdev)
device_remove_file(&pdev->dev,
&ab3100_otp_attrs[i]);
ab3100_otp_exit_debugfs(otp);
- kfree(otp);
return 0;
}
@@ -248,19 +244,7 @@ static struct platform_driver ab3100_otp_driver = {
.remove = __exit_p(ab3100_otp_remove),
};
-static int __init ab3100_otp_init(void)
-{
- return platform_driver_probe(&ab3100_otp_driver,
- ab3100_otp_probe);
-}
-
-static void __exit ab3100_otp_exit(void)
-{
- platform_driver_unregister(&ab3100_otp_driver);
-}
-
-module_init(ab3100_otp_init);
-module_exit(ab3100_otp_exit);
+module_platform_driver_probe(ab3100_otp_driver, ab3100_otp_probe);
MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
MODULE_DESCRIPTION("AB3100 OTP Readout Driver");
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index 1667c77b5cd..cf2e6a198c6 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -19,6 +19,7 @@
#include <linux/mfd/core.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/mfd/dbx500-prcmu.h>
#include <linux/regulator/ab8500.h>
#include <linux/of.h>
@@ -94,6 +95,7 @@
#define AB8500_IT_MASK22_REG 0x55
#define AB8500_IT_MASK23_REG 0x56
#define AB8500_IT_MASK24_REG 0x57
+#define AB8500_IT_MASK25_REG 0x58
/*
* latch hierarchy registers
@@ -101,15 +103,25 @@
#define AB8500_IT_LATCHHIER1_REG 0x60
#define AB8500_IT_LATCHHIER2_REG 0x61
#define AB8500_IT_LATCHHIER3_REG 0x62
+#define AB8540_IT_LATCHHIER4_REG 0x63
#define AB8500_IT_LATCHHIER_NUM 3
+#define AB8540_IT_LATCHHIER_NUM 4
#define AB8500_REV_REG 0x80
#define AB8500_IC_NAME_REG 0x82
#define AB8500_SWITCH_OFF_STATUS 0x00
#define AB8500_TURN_ON_STATUS 0x00
+#define AB8505_TURN_ON_STATUS_2 0x04
+#define AB8500_CH_USBCH_STAT1_REG 0x02
+#define VBUS_DET_DBNC100 0x02
+#define VBUS_DET_DBNC1 0x01
+
+static DEFINE_SPINLOCK(on_stat_lock);
+static u8 turn_on_stat_mask = 0xFF;
+static u8 turn_on_stat_set;
static bool no_bm; /* No battery management */
module_param(no_bm, bool, S_IRUGO);
@@ -129,9 +141,15 @@ static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = {
0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21,
};
-/* AB9540 support */
+/* AB9540 / AB8505 support */
static const int ab9540_irq_regoffset[AB9540_NUM_IRQ_REGS] = {
- 0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, 12, 13, 24,
+ 0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, 12, 13, 24, 5, 22, 23
+};
+
+/* AB8540 support */
+static const int ab8540_irq_regoffset[AB8540_NUM_IRQ_REGS] = {
+ 0, 1, 2, 3, 4, -1, -1, -1, -1, 11, 18, 19, 20, 21, 12, 13, 24, 5, 22, 23,
+ 25, 26, 27, 28, 29, 30, 31,
};
static const char ab8500_version_str[][7] = {
@@ -319,6 +337,7 @@ static struct abx500_ops ab8500_ops = {
.mask_and_set_register = ab8500_mask_and_set_register,
.event_registers_startup_state_get = NULL,
.startup_irq_enabled = NULL,
+ .dump_all_banks = ab8500_dump_all_banks,
};
static void ab8500_irq_lock(struct irq_data *data)
@@ -350,6 +369,9 @@ static void ab8500_irq_sync_unlock(struct irq_data *data)
is_ab8500_1p1_or_earlier(ab8500))
continue;
+ if (ab8500->irq_reg_offset[i] < 0)
+ continue;
+
ab8500->oldmask[i] = new;
reg = AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i];
@@ -367,16 +389,48 @@ static void ab8500_irq_mask(struct irq_data *data)
int mask = 1 << (offset % 8);
ab8500->mask[index] |= mask;
+
+ /* The AB8500 GPIOs have two interrupts each (rising & falling). */
+ if (offset >= AB8500_INT_GPIO6R && offset <= AB8500_INT_GPIO41R)
+ ab8500->mask[index + 2] |= mask;
+ if (offset >= AB9540_INT_GPIO50R && offset <= AB9540_INT_GPIO54R)
+ ab8500->mask[index + 1] |= mask;
+ if (offset == AB8540_INT_GPIO43R || offset == AB8540_INT_GPIO44R)
+ /* Here the falling IRQ is one bit lower */
+ ab8500->mask[index] |= (mask << 1);
}
static void ab8500_irq_unmask(struct irq_data *data)
{
struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
+ unsigned int type = irqd_get_trigger_type(data);
int offset = data->hwirq;
int index = offset / 8;
int mask = 1 << (offset % 8);
- ab8500->mask[index] &= ~mask;
+ if (type & IRQ_TYPE_EDGE_RISING)
+ ab8500->mask[index] &= ~mask;
+
+ /* The AB8500 GPIOs have two interrupts each (rising & falling). */
+ if (type & IRQ_TYPE_EDGE_FALLING) {
+ if (offset >= AB8500_INT_GPIO6R && offset <= AB8500_INT_GPIO41R)
+ ab8500->mask[index + 2] &= ~mask;
+ else if (offset >= AB9540_INT_GPIO50R && offset <= AB9540_INT_GPIO54R)
+ ab8500->mask[index + 1] &= ~mask;
+ else if (offset == AB8540_INT_GPIO43R || offset == AB8540_INT_GPIO44R)
+ /* Here the falling IRQ is one bit lower */
+ ab8500->mask[index] &= ~(mask << 1);
+ else
+ ab8500->mask[index] &= ~mask;
+ } else {
+ /* Satisfies the case where type is not set. */
+ ab8500->mask[index] &= ~mask;
+ }
+}
+
+static int ab8500_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ return 0;
}
static struct irq_chip ab8500_irq_chip = {
@@ -386,32 +440,59 @@ static struct irq_chip ab8500_irq_chip = {
.irq_mask = ab8500_irq_mask,
.irq_disable = ab8500_irq_mask,
.irq_unmask = ab8500_irq_unmask,
+ .irq_set_type = ab8500_irq_set_type,
};
+static void update_latch_offset(u8 *offset, int i)
+{
+ /* Fix inconsistent ITFromLatch25 bit mapping... */
+ if (unlikely(*offset == 17))
+ *offset = 24;
+ /* Fix inconsistent ab8540 bit mapping... */
+ if (unlikely(*offset == 16))
+ *offset = 25;
+ if ((i==3) && (*offset >= 24))
+ *offset += 2;
+}
+
static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500,
int latch_offset, u8 latch_val)
{
- int int_bit = __ffs(latch_val);
- int line, i;
+ int int_bit, line, i;
- do {
- int_bit = __ffs(latch_val);
+ for (i = 0; i < ab8500->mask_size; i++)
+ if (ab8500->irq_reg_offset[i] == latch_offset)
+ break;
- for (i = 0; i < ab8500->mask_size; i++)
- if (ab8500->irq_reg_offset[i] == latch_offset)
- break;
+ if (i >= ab8500->mask_size) {
+ dev_err(ab8500->dev, "Register offset 0x%2x not declared\n",
+ latch_offset);
+ return -ENXIO;
+ }
- if (i >= ab8500->mask_size) {
- dev_err(ab8500->dev, "Register offset 0x%2x not declared\n",
- latch_offset);
- return -ENXIO;
- }
+ /* ignore masked out interrupts */
+ latch_val &= ~ab8500->mask[i];
+ while (latch_val) {
+ int_bit = __ffs(latch_val);
line = (i << 3) + int_bit;
latch_val &= ~(1 << int_bit);
- handle_nested_irq(ab8500->irq_base + line);
- } while (latch_val);
+ /*
+ * This handles the falling edge hwirqs from the GPIO
+ * lines. Route them back to the line registered for the
+ * rising IRQ, as this is merely a flag for the same IRQ
+ * in linux terms.
+ */
+ if (line >= AB8500_INT_GPIO6F && line <= AB8500_INT_GPIO41F)
+ line -= 16;
+ if (line >= AB9540_INT_GPIO50F && line <= AB9540_INT_GPIO54F)
+ line -= 8;
+ if (line == AB8540_INT_GPIO43F || line == AB8540_INT_GPIO44F)
+ line += 1;
+
+ handle_nested_irq(irq_create_mapping(ab8500->domain, line));
+ }
return 0;
}
@@ -426,9 +507,7 @@ static int ab8500_handle_hierarchical_latch(struct ab8500 *ab8500,
latch_bit = __ffs(hier_val);
latch_offset = (hier_offset << 3) + latch_bit;
- /* Fix inconsistent ITFromLatch25 bit mapping... */
- if (unlikely(latch_offset == 17))
- latch_offset = 24;
+ update_latch_offset(&latch_offset, hier_offset);
status = get_register_interruptible(ab8500,
AB8500_INTERRUPT,
@@ -456,7 +535,7 @@ static irqreturn_t ab8500_hierarchical_irq(int irq, void *dev)
dev_vdbg(ab8500->dev, "interrupt\n");
/* Hierarchical interrupt version */
- for (i = 0; i < AB8500_IT_LATCHHIER_NUM; i++) {
+ for (i = 0; i < (ab8500->it_latchhier_num); i++) {
int status;
u8 hier_val;
@@ -472,62 +551,6 @@ static irqreturn_t ab8500_hierarchical_irq(int irq, void *dev)
return IRQ_HANDLED;
}
-/**
- * ab8500_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ
- *
- * @ab8500: ab8500_irq controller to operate on.
- * @irq: index of the interrupt requested in the chip IRQs
- *
- * Useful for drivers to request their own IRQs.
- */
-static int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq)
-{
- if (!ab8500)
- return -EINVAL;
-
- return irq_create_mapping(ab8500->domain, irq);
-}
-
-static irqreturn_t ab8500_irq(int irq, void *dev)
-{
- struct ab8500 *ab8500 = dev;
- int i;
-
- dev_vdbg(ab8500->dev, "interrupt\n");
-
- atomic_inc(&ab8500->transfer_ongoing);
-
- for (i = 0; i < ab8500->mask_size; i++) {
- int regoffset = ab8500->irq_reg_offset[i];
- int status;
- u8 value;
-
- /*
- * Interrupt register 12 doesn't exist prior to AB8500 version
- * 2.0
- */
- if (regoffset == 11 && is_ab8500_1p1_or_earlier(ab8500))
- continue;
-
- status = get_register_interruptible(ab8500, AB8500_INTERRUPT,
- AB8500_IT_LATCH1_REG + regoffset, &value);
- if (status < 0 || value == 0)
- continue;
-
- do {
- int bit = __ffs(value);
- int line = i * 8 + bit;
- int virq = ab8500_irq_get_virq(ab8500, line);
-
- handle_nested_irq(virq);
- value &= ~(1 << bit);
-
- } while (value);
- }
- atomic_dec(&ab8500->transfer_ongoing);
- return IRQ_HANDLED;
-}
-
static int ab8500_irq_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hwirq)
{
@@ -558,22 +581,19 @@ static int ab8500_irq_init(struct ab8500 *ab8500, struct device_node *np)
{
int num_irqs;
- if (is_ab9540(ab8500))
+ if (is_ab8540(ab8500))
+ num_irqs = AB8540_NR_IRQS;
+ else if (is_ab9540(ab8500))
num_irqs = AB9540_NR_IRQS;
else if (is_ab8505(ab8500))
num_irqs = AB8505_NR_IRQS;
else
num_irqs = AB8500_NR_IRQS;
- if (ab8500->irq_base) {
- ab8500->domain = irq_domain_add_legacy(
- NULL, num_irqs, ab8500->irq_base,
- 0, &ab8500_irq_ops, ab8500);
- }
- else {
- ab8500->domain = irq_domain_add_linear(
- np, num_irqs, &ab8500_irq_ops, ab8500);
- }
+ /* If ->irq_base is zero this will give a linear mapping */
+ ab8500->domain = irq_domain_add_simple(ab8500->dev->of_node,
+ num_irqs, 0,
+ &ab8500_irq_ops, ab8500);
if (!ab8500->domain) {
dev_err(ab8500->dev, "Failed to create irqdomain\n");
@@ -591,45 +611,22 @@ int ab8500_suspend(struct ab8500 *ab8500)
return 0;
}
-/* AB8500 GPIO Resources */
-static struct resource __devinitdata ab8500_gpio_resources[] = {
+static struct resource ab8500_gpadc_resources[] = {
{
- .name = "GPIO_INT6",
- .start = AB8500_INT_GPIO6R,
- .end = AB8500_INT_GPIO41F,
- .flags = IORESOURCE_IRQ,
- }
-};
-
-/* AB9540 GPIO Resources */
-static struct resource __devinitdata ab9540_gpio_resources[] = {
- {
- .name = "GPIO_INT6",
- .start = AB8500_INT_GPIO6R,
- .end = AB8500_INT_GPIO41F,
+ .name = "HW_CONV_END",
+ .start = AB8500_INT_GP_HW_ADC_CONV_END,
+ .end = AB8500_INT_GP_HW_ADC_CONV_END,
.flags = IORESOURCE_IRQ,
},
{
- .name = "GPIO_INT14",
- .start = AB9540_INT_GPIO50R,
- .end = AB9540_INT_GPIO54R,
+ .name = "SW_CONV_END",
+ .start = AB8500_INT_GP_SW_ADC_CONV_END,
+ .end = AB8500_INT_GP_SW_ADC_CONV_END,
.flags = IORESOURCE_IRQ,
},
- {
- .name = "GPIO_INT15",
- .start = AB9540_INT_GPIO50F,
- .end = AB9540_INT_GPIO54F,
- .flags = IORESOURCE_IRQ,
- }
};
-static struct resource __devinitdata ab8500_gpadc_resources[] = {
- {
- .name = "HW_CONV_END",
- .start = AB8500_INT_GP_HW_ADC_CONV_END,
- .end = AB8500_INT_GP_HW_ADC_CONV_END,
- .flags = IORESOURCE_IRQ,
- },
+static struct resource ab8505_gpadc_resources[] = {
{
.name = "SW_CONV_END",
.start = AB8500_INT_GP_SW_ADC_CONV_END,
@@ -638,7 +635,7 @@ static struct resource __devinitdata ab8500_gpadc_resources[] = {
},
};
-static struct resource __devinitdata ab8500_rtc_resources[] = {
+static struct resource ab8500_rtc_resources[] = {
{
.name = "60S",
.start = AB8500_INT_RTC_60S,
@@ -653,7 +650,22 @@ static struct resource __devinitdata ab8500_rtc_resources[] = {
},
};
-static struct resource __devinitdata ab8500_poweronkey_db_resources[] = {
+static struct resource ab8540_rtc_resources[] = {
+ {
+ .name = "1S",
+ .start = AB8540_INT_RTC_1S,
+ .end = AB8540_INT_RTC_1S,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ALARM",
+ .start = AB8500_INT_RTC_ALARM,
+ .end = AB8500_INT_RTC_ALARM,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource ab8500_poweronkey_db_resources[] = {
{
.name = "ONKEY_DBF",
.start = AB8500_INT_PON_KEY1DB_F,
@@ -668,7 +680,7 @@ static struct resource __devinitdata ab8500_poweronkey_db_resources[] = {
},
};
-static struct resource __devinitdata ab8500_av_acc_detect_resources[] = {
+static struct resource ab8500_av_acc_detect_resources[] = {
{
.name = "ACC_DETECT_1DB_F",
.start = AB8500_INT_ACC_DETECT_1DB_F,
@@ -707,7 +719,7 @@ static struct resource __devinitdata ab8500_av_acc_detect_resources[] = {
},
};
-static struct resource __devinitdata ab8500_charger_resources[] = {
+static struct resource ab8500_charger_resources[] = {
{
.name = "MAIN_CH_UNPLUG_DET",
.start = AB8500_INT_MAIN_CH_UNPLUG_DET,
@@ -786,9 +798,15 @@ static struct resource __devinitdata ab8500_charger_resources[] = {
.end = AB8500_INT_CH_WD_EXP,
.flags = IORESOURCE_IRQ,
},
+ {
+ .name = "VBUS_CH_DROP_END",
+ .start = AB8500_INT_VBUS_CH_DROP_END,
+ .end = AB8500_INT_VBUS_CH_DROP_END,
+ .flags = IORESOURCE_IRQ,
+ },
};
-static struct resource __devinitdata ab8500_btemp_resources[] = {
+static struct resource ab8500_btemp_resources[] = {
{
.name = "BAT_CTRL_INDB",
.start = AB8500_INT_BAT_CTRL_INDB,
@@ -821,7 +839,7 @@ static struct resource __devinitdata ab8500_btemp_resources[] = {
},
};
-static struct resource __devinitdata ab8500_fg_resources[] = {
+static struct resource ab8500_fg_resources[] = {
{
.name = "NCONV_ACCU",
.start = AB8500_INT_CCN_CONV_ACC,
@@ -860,10 +878,19 @@ static struct resource __devinitdata ab8500_fg_resources[] = {
},
};
-static struct resource __devinitdata ab8500_chargalg_resources[] = {};
+static struct resource ab8500_chargalg_resources[] = {};
#ifdef CONFIG_DEBUG_FS
-static struct resource __devinitdata ab8500_debug_resources[] = {
+static struct resource ab8500_debug_resources[] = {
+ {
+ .name = "IRQ_AB8500",
+ /*
+ * Number will be filled in. NOTE: this is deliberately
+ * not flagged as an IRQ in ordet to avoid remapping using
+ * the irqdomain in the MFD core, so that this IRQ passes
+ * unremapped to the debug code.
+ */
+ },
{
.name = "IRQ_FIRST",
.start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
@@ -879,7 +906,7 @@ static struct resource __devinitdata ab8500_debug_resources[] = {
};
#endif
-static struct resource __devinitdata ab8500_usb_resources[] = {
+static struct resource ab8500_usb_resources[] = {
{
.name = "ID_WAKEUP_R",
.start = AB8500_INT_ID_WAKEUP_R,
@@ -924,7 +951,7 @@ static struct resource __devinitdata ab8500_usb_resources[] = {
},
};
-static struct resource __devinitdata ab8505_iddet_resources[] = {
+static struct resource ab8505_iddet_resources[] = {
{
.name = "KeyDeglitch",
.start = AB8505_INT_KEYDEGLITCH,
@@ -955,18 +982,77 @@ static struct resource __devinitdata ab8505_iddet_resources[] = {
.end = AB8505_INT_KEYSTUCK,
.flags = IORESOURCE_IRQ,
},
+ {
+ .name = "VBUS_DET_R",
+ .start = AB8500_INT_VBUS_DET_R,
+ .end = AB8500_INT_VBUS_DET_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "VBUS_DET_F",
+ .start = AB8500_INT_VBUS_DET_F,
+ .end = AB8500_INT_VBUS_DET_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ID_DET_PLUGR",
+ .start = AB8500_INT_ID_DET_PLUGR,
+ .end = AB8500_INT_ID_DET_PLUGR,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ID_DET_PLUGF",
+ .start = AB8500_INT_ID_DET_PLUGF,
+ .end = AB8500_INT_ID_DET_PLUGF,
+ .flags = IORESOURCE_IRQ,
+ },
};
-static struct resource __devinitdata ab8500_temp_resources[] = {
+static struct resource ab8500_temp_resources[] = {
{
- .name = "AB8500_TEMP_WARM",
+ .name = "ABX500_TEMP_WARM",
.start = AB8500_INT_TEMP_WARM,
.end = AB8500_INT_TEMP_WARM,
.flags = IORESOURCE_IRQ,
},
};
-static struct mfd_cell __devinitdata abx500_common_devs[] = {
+static const struct mfd_cell ab8500_bm_devs[] = {
+ {
+ .name = "ab8500-charger",
+ .of_compatible = "stericsson,ab8500-charger",
+ .num_resources = ARRAY_SIZE(ab8500_charger_resources),
+ .resources = ab8500_charger_resources,
+ .platform_data = &ab8500_bm_data,
+ .pdata_size = sizeof(ab8500_bm_data),
+ },
+ {
+ .name = "ab8500-btemp",
+ .of_compatible = "stericsson,ab8500-btemp",
+ .num_resources = ARRAY_SIZE(ab8500_btemp_resources),
+ .resources = ab8500_btemp_resources,
+ .platform_data = &ab8500_bm_data,
+ .pdata_size = sizeof(ab8500_bm_data),
+ },
+ {
+ .name = "ab8500-fg",
+ .of_compatible = "stericsson,ab8500-fg",
+ .num_resources = ARRAY_SIZE(ab8500_fg_resources),
+ .resources = ab8500_fg_resources,
+ .platform_data = &ab8500_bm_data,
+ .pdata_size = sizeof(ab8500_bm_data),
+ },
+ {
+ .name = "ab8500-chargalg",
+ .of_compatible = "stericsson,ab8500-chargalg",
+ .num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
+ .resources = ab8500_chargalg_resources,
+ .platform_data = &ab8500_bm_data,
+ .pdata_size = sizeof(ab8500_bm_data),
+ },
+};
+
+static const struct mfd_cell ab8500_devs[] = {
#ifdef CONFIG_DEBUG_FS
{
.name = "ab8500-debug",
@@ -980,10 +1066,18 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = {
.of_compatible = "stericsson,ab8500-sysctrl",
},
{
+ .name = "ab8500-ext-regulator",
+ .of_compatible = "stericsson,ab8500-ext-regulator",
+ },
+ {
.name = "ab8500-regulator",
.of_compatible = "stericsson,ab8500-regulator",
},
{
+ .name = "abx500-clk",
+ .of_compatible = "stericsson,abx500-clk",
+ },
+ {
.name = "ab8500-gpadc",
.of_compatible = "stericsson,ab8500-gpadc",
.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
@@ -1002,6 +1096,7 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = {
.resources = ab8500_av_acc_detect_resources,
},
{
+
.name = "ab8500-poweron-key",
.of_compatible = "stericsson,ab8500-poweron-key",
.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
@@ -1023,81 +1118,220 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = {
.id = 3,
},
{
- .name = "ab8500-leds",
- .of_compatible = "stericsson,ab8500-leds",
- },
- {
.name = "ab8500-denc",
.of_compatible = "stericsson,ab8500-denc",
},
{
- .name = "ab8500-temp",
- .of_compatible = "stericsson,ab8500-temp",
+ .name = "pinctrl-ab8500",
+ .of_compatible = "stericsson,ab8500-gpio",
+ },
+ {
+ .name = "abx500-temp",
+ .of_compatible = "stericsson,abx500-temp",
.num_resources = ARRAY_SIZE(ab8500_temp_resources),
.resources = ab8500_temp_resources,
},
+ {
+ .name = "ab8500-usb",
+ .of_compatible = "stericsson,ab8500-usb",
+ .num_resources = ARRAY_SIZE(ab8500_usb_resources),
+ .resources = ab8500_usb_resources,
+ },
+ {
+ .name = "ab8500-codec",
+ .of_compatible = "stericsson,ab8500-codec",
+ },
};
-static struct mfd_cell __devinitdata ab8500_bm_devs[] = {
+static const struct mfd_cell ab9540_devs[] = {
+#ifdef CONFIG_DEBUG_FS
{
- .name = "ab8500-charger",
- .num_resources = ARRAY_SIZE(ab8500_charger_resources),
- .resources = ab8500_charger_resources,
+ .name = "ab8500-debug",
+ .num_resources = ARRAY_SIZE(ab8500_debug_resources),
+ .resources = ab8500_debug_resources,
},
+#endif
{
- .name = "ab8500-btemp",
- .num_resources = ARRAY_SIZE(ab8500_btemp_resources),
- .resources = ab8500_btemp_resources,
+ .name = "ab8500-sysctrl",
},
{
- .name = "ab8500-fg",
- .num_resources = ARRAY_SIZE(ab8500_fg_resources),
- .resources = ab8500_fg_resources,
+ .name = "ab8500-ext-regulator",
},
{
- .name = "ab8500-chargalg",
- .num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
- .resources = ab8500_chargalg_resources,
+ .name = "ab8500-regulator",
+ },
+ {
+ .name = "abx500-clk",
+ .of_compatible = "stericsson,abx500-clk",
+ },
+ {
+ .name = "ab8500-gpadc",
+ .of_compatible = "stericsson,ab8500-gpadc",
+ .num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
+ .resources = ab8500_gpadc_resources,
+ },
+ {
+ .name = "ab8500-rtc",
+ .num_resources = ARRAY_SIZE(ab8500_rtc_resources),
+ .resources = ab8500_rtc_resources,
+ },
+ {
+ .name = "ab8500-acc-det",
+ .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
+ .resources = ab8500_av_acc_detect_resources,
+ },
+ {
+ .name = "ab8500-poweron-key",
+ .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
+ .resources = ab8500_poweronkey_db_resources,
+ },
+ {
+ .name = "ab8500-pwm",
+ .id = 1,
+ },
+ {
+ .name = "abx500-temp",
+ .num_resources = ARRAY_SIZE(ab8500_temp_resources),
+ .resources = ab8500_temp_resources,
+ },
+ {
+ .name = "pinctrl-ab9540",
+ .of_compatible = "stericsson,ab9540-gpio",
+ },
+ {
+ .name = "ab9540-usb",
+ .num_resources = ARRAY_SIZE(ab8500_usb_resources),
+ .resources = ab8500_usb_resources,
+ },
+ {
+ .name = "ab9540-codec",
+ },
+ {
+ .name = "ab-iddet",
+ .num_resources = ARRAY_SIZE(ab8505_iddet_resources),
+ .resources = ab8505_iddet_resources,
},
};
-static struct mfd_cell __devinitdata ab8500_devs[] = {
+/* Device list for ab8505 */
+static const struct mfd_cell ab8505_devs[] = {
+#ifdef CONFIG_DEBUG_FS
{
- .name = "ab8500-gpio",
- .of_compatible = "stericsson,ab8500-gpio",
- .num_resources = ARRAY_SIZE(ab8500_gpio_resources),
- .resources = ab8500_gpio_resources,
+ .name = "ab8500-debug",
+ .num_resources = ARRAY_SIZE(ab8500_debug_resources),
+ .resources = ab8500_debug_resources,
+ },
+#endif
+ {
+ .name = "ab8500-sysctrl",
+ },
+ {
+ .name = "ab8500-regulator",
+ },
+ {
+ .name = "abx500-clk",
+ .of_compatible = "stericsson,abx500-clk",
+ },
+ {
+ .name = "ab8500-gpadc",
+ .of_compatible = "stericsson,ab8500-gpadc",
+ .num_resources = ARRAY_SIZE(ab8505_gpadc_resources),
+ .resources = ab8505_gpadc_resources,
+ },
+ {
+ .name = "ab8500-rtc",
+ .num_resources = ARRAY_SIZE(ab8500_rtc_resources),
+ .resources = ab8500_rtc_resources,
+ },
+ {
+ .name = "ab8500-acc-det",
+ .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
+ .resources = ab8500_av_acc_detect_resources,
+ },
+ {
+ .name = "ab8500-poweron-key",
+ .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
+ .resources = ab8500_poweronkey_db_resources,
+ },
+ {
+ .name = "ab8500-pwm",
+ .id = 1,
+ },
+ {
+ .name = "pinctrl-ab8505",
},
{
.name = "ab8500-usb",
- .of_compatible = "stericsson,ab8500-usb",
.num_resources = ARRAY_SIZE(ab8500_usb_resources),
.resources = ab8500_usb_resources,
},
{
.name = "ab8500-codec",
- .of_compatible = "stericsson,ab8500-codec",
+ },
+ {
+ .name = "ab-iddet",
+ .num_resources = ARRAY_SIZE(ab8505_iddet_resources),
+ .resources = ab8505_iddet_resources,
},
};
-static struct mfd_cell __devinitdata ab9540_devs[] = {
+static const struct mfd_cell ab8540_devs[] = {
+#ifdef CONFIG_DEBUG_FS
{
- .name = "ab8500-gpio",
- .num_resources = ARRAY_SIZE(ab9540_gpio_resources),
- .resources = ab9540_gpio_resources,
+ .name = "ab8500-debug",
+ .num_resources = ARRAY_SIZE(ab8500_debug_resources),
+ .resources = ab8500_debug_resources,
},
+#endif
{
- .name = "ab9540-usb",
+ .name = "ab8500-sysctrl",
+ },
+ {
+ .name = "ab8500-ext-regulator",
+ },
+ {
+ .name = "ab8500-regulator",
+ },
+ {
+ .name = "abx500-clk",
+ .of_compatible = "stericsson,abx500-clk",
+ },
+ {
+ .name = "ab8500-gpadc",
+ .of_compatible = "stericsson,ab8500-gpadc",
+ .num_resources = ARRAY_SIZE(ab8505_gpadc_resources),
+ .resources = ab8505_gpadc_resources,
+ },
+ {
+ .name = "ab8500-acc-det",
+ .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
+ .resources = ab8500_av_acc_detect_resources,
+ },
+ {
+ .name = "ab8500-poweron-key",
+ .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
+ .resources = ab8500_poweronkey_db_resources,
+ },
+ {
+ .name = "ab8500-pwm",
+ .id = 1,
+ },
+ {
+ .name = "abx500-temp",
+ .num_resources = ARRAY_SIZE(ab8500_temp_resources),
+ .resources = ab8500_temp_resources,
+ },
+ {
+ .name = "pinctrl-ab8540",
+ },
+ {
+ .name = "ab8540-usb",
.num_resources = ARRAY_SIZE(ab8500_usb_resources),
.resources = ab8500_usb_resources,
},
{
- .name = "ab9540-codec",
+ .name = "ab8540-codec",
},
-};
-
-/* Device list common to ab9540 and ab8505 */
-static struct mfd_cell __devinitdata ab9540_ab8505_devs[] = {
{
.name = "ab-iddet",
.num_resources = ARRAY_SIZE(ab8505_iddet_resources),
@@ -1105,12 +1339,31 @@ static struct mfd_cell __devinitdata ab9540_ab8505_devs[] = {
},
};
+static const struct mfd_cell ab8540_cut1_devs[] = {
+ {
+ .name = "ab8500-rtc",
+ .of_compatible = "stericsson,ab8500-rtc",
+ .num_resources = ARRAY_SIZE(ab8500_rtc_resources),
+ .resources = ab8500_rtc_resources,
+ },
+};
+
+static const struct mfd_cell ab8540_cut2_devs[] = {
+ {
+ .name = "ab8540-rtc",
+ .of_compatible = "stericsson,ab8540-rtc",
+ .num_resources = ARRAY_SIZE(ab8540_rtc_resources),
+ .resources = ab8540_rtc_resources,
+ },
+};
+
static ssize_t show_chip_id(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ab8500 *ab8500;
ab8500 = dev_get_drvdata(dev);
+
return sprintf(buf, "%#x\n", ab8500 ? ab8500->chip_id : -EINVAL);
}
@@ -1140,6 +1393,15 @@ static ssize_t show_switch_off_status(struct device *dev,
return sprintf(buf, "%#x\n", value);
}
+/* use mask and set to override the register turn_on_stat value */
+void ab8500_override_turn_on_stat(u8 mask, u8 set)
+{
+ spin_lock(&on_stat_lock);
+ turn_on_stat_mask = mask;
+ turn_on_stat_set = set;
+ spin_unlock(&on_stat_lock);
+}
+
/*
* ab8500 has turned on due to (TURN_ON_STATUS):
* 0x01 PORnVbat
@@ -1163,9 +1425,38 @@ static ssize_t show_turn_on_status(struct device *dev,
AB8500_TURN_ON_STATUS, &value);
if (ret < 0)
return ret;
+
+ /*
+ * In L9540, turn_on_status register is not updated correctly if
+ * the device is rebooted with AC/USB charger connected. Due to
+ * this, the device boots android instead of entering into charge
+ * only mode. Read the AC/USB status register to detect the charger
+ * presence and update the turn on status manually.
+ */
+ if (is_ab9540(ab8500)) {
+ spin_lock(&on_stat_lock);
+ value = (value & turn_on_stat_mask) | turn_on_stat_set;
+ spin_unlock(&on_stat_lock);
+ }
+
return sprintf(buf, "%#x\n", value);
}
+static ssize_t show_turn_on_status_2(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ u8 value;
+ struct ab8500 *ab8500;
+
+ ab8500 = dev_get_drvdata(dev);
+ ret = get_register_interruptible(ab8500, AB8500_SYS_CTRL1_BLOCK,
+ AB8505_TURN_ON_STATUS_2, &value);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%#x\n", (value & 0x1));
+}
+
static ssize_t show_ab9540_dbbrstn(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1222,6 +1513,7 @@ exit:
static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL);
static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL);
static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL);
+static DEVICE_ATTR(turn_on_status_2, S_IRUGO, show_turn_on_status_2, NULL);
static DEVICE_ATTR(dbbrstn, S_IRUGO | S_IWUSR,
show_ab9540_dbbrstn, store_ab9540_dbbrstn);
@@ -1232,6 +1524,11 @@ static struct attribute *ab8500_sysfs_entries[] = {
NULL,
};
+static struct attribute *ab8505_sysfs_entries[] = {
+ &dev_attr_turn_on_status_2.attr,
+ NULL,
+};
+
static struct attribute *ab9540_sysfs_entries[] = {
&dev_attr_chip_id.attr,
&dev_attr_switch_off_status.attr,
@@ -1244,11 +1541,15 @@ static struct attribute_group ab8500_attr_group = {
.attrs = ab8500_sysfs_entries,
};
+static struct attribute_group ab8505_attr_group = {
+ .attrs = ab8505_sysfs_entries,
+};
+
static struct attribute_group ab9540_attr_group = {
.attrs = ab9540_sysfs_entries,
};
-static int __devinit ab8500_probe(struct platform_device *pdev)
+static int ab8500_probe(struct platform_device *pdev)
{
static char *switch_off_status[] = {
"Swoff bit programming",
@@ -1259,6 +1560,15 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
"Battery level lower than power on reset threshold",
"Power on key 1 pressed longer than 10 seconds",
"DB8500 thermal shutdown"};
+ static char *turn_on_status[] = {
+ "Battery rising (Vbat)",
+ "Power On Key 1 dbF",
+ "Power On Key 2 dbF",
+ "RTC Alarm",
+ "Main Charger Detect",
+ "Vbus Detect (USB)",
+ "USB ID Detect",
+ "UART Factory Mode Detect"};
struct ab8500_platform_data *plat = dev_get_platdata(&pdev->dev);
const struct platform_device_id *platid = platform_get_device_id(pdev);
enum ab8500_version version = AB8500_VERSION_UNDEFINED;
@@ -1269,19 +1579,16 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
int i;
u8 value;
- ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL);
+ ab8500 = devm_kzalloc(&pdev->dev, sizeof *ab8500, GFP_KERNEL);
if (!ab8500)
return -ENOMEM;
- if (plat)
- ab8500->irq_base = plat->irq_base;
-
ab8500->dev = &pdev->dev;
resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!resource) {
- ret = -ENODEV;
- goto out_free_ab8500;
+ dev_err(&pdev->dev, "no IRQ resource\n");
+ return -ENODEV;
}
ab8500->irq = resource->start;
@@ -1304,8 +1611,10 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
else {
ret = get_register_interruptible(ab8500, AB8500_MISC,
AB8500_IC_NAME_REG, &value);
- if (ret < 0)
- goto out_free_ab8500;
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not probe HW\n");
+ return ret;
+ }
ab8500->version = value;
}
@@ -1313,7 +1622,7 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
ret = get_register_interruptible(ab8500, AB8500_MISC,
AB8500_REV_REG, &value);
if (ret < 0)
- goto out_free_ab8500;
+ return ret;
ab8500->chip_id = value;
@@ -1322,22 +1631,28 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
ab8500->chip_id >> 4,
ab8500->chip_id & 0x0F);
- /* Configure AB8500 or AB9540 IRQ */
- if (is_ab9540(ab8500) || is_ab8505(ab8500)) {
+ /* Configure AB8540 */
+ if (is_ab8540(ab8500)) {
+ ab8500->mask_size = AB8540_NUM_IRQ_REGS;
+ ab8500->irq_reg_offset = ab8540_irq_regoffset;
+ ab8500->it_latchhier_num = AB8540_IT_LATCHHIER_NUM;
+ }/* Configure AB8500 or AB9540 IRQ */
+ else if (is_ab9540(ab8500) || is_ab8505(ab8500)) {
ab8500->mask_size = AB9540_NUM_IRQ_REGS;
ab8500->irq_reg_offset = ab9540_irq_regoffset;
+ ab8500->it_latchhier_num = AB8500_IT_LATCHHIER_NUM;
} else {
ab8500->mask_size = AB8500_NUM_IRQ_REGS;
ab8500->irq_reg_offset = ab8500_irq_regoffset;
+ ab8500->it_latchhier_num = AB8500_IT_LATCHHIER_NUM;
}
- ab8500->mask = kzalloc(ab8500->mask_size, GFP_KERNEL);
+ ab8500->mask = devm_kzalloc(&pdev->dev, ab8500->mask_size, GFP_KERNEL);
if (!ab8500->mask)
return -ENOMEM;
- ab8500->oldmask = kzalloc(ab8500->mask_size, GFP_KERNEL);
- if (!ab8500->oldmask) {
- ret = -ENOMEM;
- goto out_freemask;
- }
+ ab8500->oldmask = devm_kzalloc(&pdev->dev, ab8500->mask_size, GFP_KERNEL);
+ if (!ab8500->oldmask)
+ return -ENOMEM;
+
/*
* ab8500 has switched off due to (SWITCH_OFF_STATUS):
* 0x01 Swoff bit programming
@@ -1368,10 +1683,36 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
} else {
printk(KERN_CONT " None\n");
}
+ ret = get_register_interruptible(ab8500, AB8500_SYS_CTRL1_BLOCK,
+ AB8500_TURN_ON_STATUS, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(ab8500->dev, "turn on reason(s) (%#x): ", value);
+
+ if (value) {
+ for (i = 0; i < ARRAY_SIZE(turn_on_status); i++) {
+ if (value & 1)
+ printk("\"%s\" ", turn_on_status[i]);
+ value = value >> 1;
+ }
+ printk("\n");
+ } else {
+ printk("None\n");
+ }
if (plat && plat->init)
plat->init(ab8500);
+ if (is_ab9540(ab8500)) {
+ ret = get_register_interruptible(ab8500, AB8500_CHARGER,
+ AB8500_CH_USBCH_STAT1_REG, &value);
+ if (ret < 0)
+ return ret;
+ if ((value & VBUS_DET_DBNC1) && (value & VBUS_DET_DBNC100))
+ ab8500_override_turn_on_stat(~AB8500_POW_KEY_1_ON,
+ AB8500_VBUS_DET);
+ }
+
/* Clear and mask all interrupts */
for (i = 0; i < ab8500->mask_size; i++) {
/*
@@ -1382,6 +1723,9 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
is_ab8500_1p1_or_earlier(ab8500))
continue;
+ if (ab8500->irq_reg_offset[i] < 0)
+ continue;
+
get_register_interruptible(ab8500, AB8500_INTERRUPT,
AB8500_IT_LATCH1_REG + ab8500->irq_reg_offset[i],
&value);
@@ -1391,103 +1735,101 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
ret = abx500_register_ops(ab8500->dev, &ab8500_ops);
if (ret)
- goto out_freeoldmask;
+ return ret;
for (i = 0; i < ab8500->mask_size; i++)
ab8500->mask[i] = ab8500->oldmask[i] = 0xff;
ret = ab8500_irq_init(ab8500, np);
if (ret)
- goto out_freeoldmask;
-
- /* Activate this feature only in ab9540 */
- /* till tests are done on ab8500 1p2 or later*/
- if (is_ab9540(ab8500)) {
- ret = request_threaded_irq(ab8500->irq, NULL,
- ab8500_hierarchical_irq,
- IRQF_ONESHOT | IRQF_NO_SUSPEND,
- "ab8500", ab8500);
- }
- else {
- ret = request_threaded_irq(ab8500->irq, NULL,
- ab8500_irq,
- IRQF_ONESHOT | IRQF_NO_SUSPEND,
- "ab8500", ab8500);
- if (ret)
- goto out_freeoldmask;
- }
+ return ret;
- ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
- ARRAY_SIZE(abx500_common_devs), NULL,
- ab8500->irq_base, ab8500->domain);
+ ret = devm_request_threaded_irq(&pdev->dev, ab8500->irq, NULL,
+ ab8500_hierarchical_irq,
+ IRQF_ONESHOT | IRQF_NO_SUSPEND,
+ "ab8500", ab8500);
if (ret)
- goto out_freeirq;
+ return ret;
+
+#if CONFIG_DEBUG_FS
+ /* Pass to debugfs */
+ ab8500_debug_resources[0].start = ab8500->irq;
+ ab8500_debug_resources[0].end = ab8500->irq;
+#endif
if (is_ab9540(ab8500))
ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
ARRAY_SIZE(ab9540_devs), NULL,
- ab8500->irq_base, ab8500->domain);
+ 0, ab8500->domain);
+ else if (is_ab8540(ab8500)) {
+ ret = mfd_add_devices(ab8500->dev, 0, ab8540_devs,
+ ARRAY_SIZE(ab8540_devs), NULL,
+ 0, ab8500->domain);
+ if (ret)
+ return ret;
+
+ if (is_ab8540_1p2_or_earlier(ab8500))
+ ret = mfd_add_devices(ab8500->dev, 0, ab8540_cut1_devs,
+ ARRAY_SIZE(ab8540_cut1_devs), NULL,
+ 0, ab8500->domain);
+ else /* ab8540 >= cut2 */
+ ret = mfd_add_devices(ab8500->dev, 0, ab8540_cut2_devs,
+ ARRAY_SIZE(ab8540_cut2_devs), NULL,
+ 0, ab8500->domain);
+ } else if (is_ab8505(ab8500))
+ ret = mfd_add_devices(ab8500->dev, 0, ab8505_devs,
+ ARRAY_SIZE(ab8505_devs), NULL,
+ 0, ab8500->domain);
else
ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
ARRAY_SIZE(ab8500_devs), NULL,
- ab8500->irq_base, ab8500->domain);
+ 0, ab8500->domain);
if (ret)
- goto out_freeirq;
-
- if (is_ab9540(ab8500) || is_ab8505(ab8500))
- ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
- ARRAY_SIZE(ab9540_ab8505_devs), NULL,
- ab8500->irq_base, ab8500->domain);
- if (ret)
- goto out_freeirq;
+ return ret;
if (!no_bm) {
/* Add battery management devices */
ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs,
ARRAY_SIZE(ab8500_bm_devs), NULL,
- ab8500->irq_base, ab8500->domain);
+ 0, ab8500->domain);
if (ret)
dev_err(ab8500->dev, "error adding bm devices\n");
}
- if (is_ab9540(ab8500))
+ if (((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
+ ab8500->chip_id >= AB8500_CUT2P0) || is_ab8540(ab8500))
ret = sysfs_create_group(&ab8500->dev->kobj,
&ab9540_attr_group);
else
ret = sysfs_create_group(&ab8500->dev->kobj,
&ab8500_attr_group);
- if (ret)
- dev_err(ab8500->dev, "error creating sysfs entries\n");
- return ret;
+ if ((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
+ ab8500->chip_id >= AB8500_CUT2P0)
+ ret = sysfs_create_group(&ab8500->dev->kobj,
+ &ab8505_attr_group);
-out_freeirq:
- free_irq(ab8500->irq, ab8500);
-out_freeoldmask:
- kfree(ab8500->oldmask);
-out_freemask:
- kfree(ab8500->mask);
-out_free_ab8500:
- kfree(ab8500);
+ if (ret)
+ dev_err(ab8500->dev, "error creating sysfs entries\n");
return ret;
}
-static int __devexit ab8500_remove(struct platform_device *pdev)
+static int ab8500_remove(struct platform_device *pdev)
{
struct ab8500 *ab8500 = platform_get_drvdata(pdev);
- if (is_ab9540(ab8500))
+ if (((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
+ ab8500->chip_id >= AB8500_CUT2P0) || is_ab8540(ab8500))
sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group);
else
sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group);
- mfd_remove_devices(ab8500->dev);
- free_irq(ab8500->irq, ab8500);
+ if ((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
+ ab8500->chip_id >= AB8500_CUT2P0)
+ sysfs_remove_group(&ab8500->dev->kobj, &ab8505_attr_group);
- kfree(ab8500->oldmask);
- kfree(ab8500->mask);
- kfree(ab8500);
+ mfd_remove_devices(ab8500->dev);
return 0;
}
@@ -1506,7 +1848,7 @@ static struct platform_driver ab8500_core_driver = {
.owner = THIS_MODULE,
},
.probe = ab8500_probe,
- .remove = __devexit_p(ab8500_remove),
+ .remove = ab8500_remove,
.id_table = ab8500_id,
};
diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index c4cb806978a..d1a22aae2df 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -4,6 +4,72 @@
* Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson.
* License Terms: GNU General Public License v2
*/
+/*
+ * AB8500 register access
+ * ======================
+ *
+ * read:
+ * # echo BANK > <debugfs>/ab8500/register-bank
+ * # echo ADDR > <debugfs>/ab8500/register-address
+ * # cat <debugfs>/ab8500/register-value
+ *
+ * write:
+ * # echo BANK > <debugfs>/ab8500/register-bank
+ * # echo ADDR > <debugfs>/ab8500/register-address
+ * # echo VALUE > <debugfs>/ab8500/register-value
+ *
+ * read all registers from a bank:
+ * # echo BANK > <debugfs>/ab8500/register-bank
+ * # cat <debugfs>/ab8500/all-bank-register
+ *
+ * BANK target AB8500 register bank
+ * ADDR target AB8500 register address
+ * VALUE decimal or 0x-prefixed hexadecimal
+ *
+ *
+ * User Space notification on AB8500 IRQ
+ * =====================================
+ *
+ * Allows user space entity to be notified when target AB8500 IRQ occurs.
+ * When subscribed, a sysfs entry is created in ab8500.i2c platform device.
+ * One can pool this file to get target IRQ occurence information.
+ *
+ * subscribe to an AB8500 IRQ:
+ * # echo IRQ > <debugfs>/ab8500/irq-subscribe
+ *
+ * unsubscribe from an AB8500 IRQ:
+ * # echo IRQ > <debugfs>/ab8500/irq-unsubscribe
+ *
+ *
+ * AB8500 register formated read/write access
+ * ==========================================
+ *
+ * Read: read data, data>>SHIFT, data&=MASK, output data
+ * [0xABCDEF98] shift=12 mask=0xFFF => 0x00000CDE
+ * Write: read data, data &= ~(MASK<<SHIFT), data |= (VALUE<<SHIFT), write data
+ * [0xABCDEF98] shift=12 mask=0xFFF value=0x123 => [0xAB123F98]
+ *
+ * Usage:
+ * # echo "CMD [OPTIONS] BANK ADRESS [VALUE]" > $debugfs/ab8500/hwreg
+ *
+ * CMD read read access
+ * write write access
+ *
+ * BANK target reg bank
+ * ADDRESS target reg address
+ * VALUE (write) value to be updated
+ *
+ * OPTIONS
+ * -d|-dec (read) output in decimal
+ * -h|-hexa (read) output in 0x-hexa (default)
+ * -l|-w|-b 32bit (default), 16bit or 8bit reg access
+ * -m|-mask MASK 0x-hexa mask (default 0xFFFFFFFF)
+ * -s|-shift SHIFT bit shift value (read:left, write:right)
+ * -o|-offset OFFSET address offset to add to ADDRESS value
+ *
+ * Warning: bit shift operation is applied to bit-mask.
+ * Warning: bit shift direction depends on read or right command.
+ */
#include <linux/seq_file.h>
#include <linux/uaccess.h>
@@ -11,13 +77,37 @@
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/kobject.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/string.h>
+#include <linux/ctype.h>
+#endif
static u32 debug_bank;
static u32 debug_address;
+static int irq_ab8500;
+static int irq_first;
+static int irq_last;
+static u32 *irq_count;
+static int num_irqs;
+
+static struct device_attribute **dev_attr;
+static char **event_name;
+
+static u8 avg_sample = SAMPLE_16;
+static u8 trig_edge = RISING_EDGE;
+static u8 conv_type = ADC_SW;
+static u8 trig_timer;
+
/**
* struct ab8500_reg_range
* @first: the first address of the range
@@ -42,15 +132,37 @@ struct ab8500_prcmu_ranges {
const struct ab8500_reg_range *range;
};
+/* hwreg- "mask" and "shift" entries ressources */
+struct hwreg_cfg {
+ u32 bank; /* target bank */
+ u32 addr; /* target address */
+ uint fmt; /* format */
+ uint mask; /* read/write mask, applied before any bit shift */
+ int shift; /* bit shift (read:right shift, write:left shift */
+};
+/* fmt bit #0: 0=hexa, 1=dec */
+#define REG_FMT_DEC(c) ((c)->fmt & 0x1)
+#define REG_FMT_HEX(c) (!REG_FMT_DEC(c))
+
+static struct hwreg_cfg hwreg_cfg = {
+ .addr = 0, /* default: invalid phys addr */
+ .fmt = 0, /* default: 32bit access, hex output */
+ .mask = 0xFFFFFFFF, /* default: no mask */
+ .shift = 0, /* default: no bit shift */
+};
+
#define AB8500_NAME_STRING "ab8500"
-#define AB8500_NUM_BANKS 22
+#define AB8500_ADC_NAME_STRING "gpadc"
+#define AB8500_NUM_BANKS 24
#define AB8500_REV_REG 0x80
-static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
+static struct ab8500_prcmu_ranges *debug_ranges;
+
+static struct ab8500_prcmu_ranges ab8500_debug_ranges[AB8500_NUM_BANKS] = {
[0x0] = {
.num_ranges = 0,
- .range = 0,
+ .range = NULL,
},
[AB8500_SYS_CTRL1_BLOCK] = {
.num_ranges = 3,
@@ -215,7 +327,7 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
},
},
[AB8500_CHARGER] = {
- .num_ranges = 8,
+ .num_ranges = 9,
.range = (struct ab8500_reg_range[]) {
{
.first = 0x00,
@@ -249,6 +361,10 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
.first = 0xC0,
.last = 0xC2,
},
+ {
+ .first = 0xf5,
+ .last = 0xf6,
+ },
},
},
[AB8500_GAS_GAUGE] = {
@@ -268,6 +384,24 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
},
},
},
+ [AB8500_DEVELOPMENT] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x00,
+ },
+ },
+ },
+ [AB8500_DEBUG] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x05,
+ .last = 0x07,
+ },
+ },
+ },
[AB8500_AUDIO] = {
.num_ranges = 1,
.range = (struct ab8500_reg_range[]) {
@@ -354,15 +488,805 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
},
};
-static int ab8500_registers_print(struct seq_file *s, void *p)
+static struct ab8500_prcmu_ranges ab8505_debug_ranges[AB8500_NUM_BANKS] = {
+ [0x0] = {
+ .num_ranges = 0,
+ .range = NULL,
+ },
+ [AB8500_SYS_CTRL1_BLOCK] = {
+ .num_ranges = 5,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x04,
+ },
+ {
+ .first = 0x42,
+ .last = 0x42,
+ },
+ {
+ .first = 0x52,
+ .last = 0x52,
+ },
+ {
+ .first = 0x54,
+ .last = 0x57,
+ },
+ {
+ .first = 0x80,
+ .last = 0x83,
+ },
+ },
+ },
+ [AB8500_SYS_CTRL2_BLOCK] = {
+ .num_ranges = 5,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0D,
+ },
+ {
+ .first = 0x0F,
+ .last = 0x17,
+ },
+ {
+ .first = 0x20,
+ .last = 0x20,
+ },
+ {
+ .first = 0x30,
+ .last = 0x30,
+ },
+ {
+ .first = 0x32,
+ .last = 0x3A,
+ },
+ },
+ },
+ [AB8500_REGU_CTRL1] = {
+ .num_ranges = 3,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x00,
+ },
+ {
+ .first = 0x03,
+ .last = 0x11,
+ },
+ {
+ .first = 0x80,
+ .last = 0x86,
+ },
+ },
+ },
+ [AB8500_REGU_CTRL2] = {
+ .num_ranges = 6,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x06,
+ },
+ {
+ .first = 0x08,
+ .last = 0x15,
+ },
+ {
+ .first = 0x17,
+ .last = 0x19,
+ },
+ {
+ .first = 0x1B,
+ .last = 0x1D,
+ },
+ {
+ .first = 0x1F,
+ .last = 0x30,
+ },
+ {
+ .first = 0x40,
+ .last = 0x48,
+ },
+ /* 0x80-0x8B is SIM registers and should
+ * not be accessed from here */
+ },
+ },
+ [AB8500_USB] = {
+ .num_ranges = 3,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x80,
+ .last = 0x83,
+ },
+ {
+ .first = 0x87,
+ .last = 0x8A,
+ },
+ {
+ .first = 0x91,
+ .last = 0x94,
+ },
+ },
+ },
+ [AB8500_TVOUT] = {
+ .num_ranges = 0,
+ .range = NULL,
+ },
+ [AB8500_DBI] = {
+ .num_ranges = 0,
+ .range = NULL,
+ },
+ [AB8500_ECI_AV_ACC] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x80,
+ .last = 0x82,
+ },
+ },
+ },
+ [AB8500_RESERVED] = {
+ .num_ranges = 0,
+ .range = NULL,
+ },
+ [AB8500_GPADC] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x08,
+ },
+ },
+ },
+ [AB8500_CHARGER] = {
+ .num_ranges = 9,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x02,
+ .last = 0x03,
+ },
+ {
+ .first = 0x05,
+ .last = 0x05,
+ },
+ {
+ .first = 0x40,
+ .last = 0x44,
+ },
+ {
+ .first = 0x50,
+ .last = 0x57,
+ },
+ {
+ .first = 0x60,
+ .last = 0x60,
+ },
+ {
+ .first = 0xA0,
+ .last = 0xA7,
+ },
+ {
+ .first = 0xAF,
+ .last = 0xB2,
+ },
+ {
+ .first = 0xC0,
+ .last = 0xC2,
+ },
+ {
+ .first = 0xF5,
+ .last = 0xF5,
+ },
+ },
+ },
+ [AB8500_GAS_GAUGE] = {
+ .num_ranges = 3,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x00,
+ },
+ {
+ .first = 0x07,
+ .last = 0x0A,
+ },
+ {
+ .first = 0x10,
+ .last = 0x14,
+ },
+ },
+ },
+ [AB8500_AUDIO] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x83,
+ },
+ },
+ },
+ [AB8500_INTERRUPT] = {
+ .num_ranges = 11,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x04,
+ },
+ {
+ .first = 0x06,
+ .last = 0x07,
+ },
+ {
+ .first = 0x09,
+ .last = 0x09,
+ },
+ {
+ .first = 0x0B,
+ .last = 0x0C,
+ },
+ {
+ .first = 0x12,
+ .last = 0x15,
+ },
+ {
+ .first = 0x18,
+ .last = 0x18,
+ },
+ /* Latch registers should not be read here */
+ {
+ .first = 0x40,
+ .last = 0x44,
+ },
+ {
+ .first = 0x46,
+ .last = 0x49,
+ },
+ {
+ .first = 0x4B,
+ .last = 0x4D,
+ },
+ {
+ .first = 0x52,
+ .last = 0x55,
+ },
+ {
+ .first = 0x58,
+ .last = 0x58,
+ },
+ /* LatchHier registers should not be read here */
+ },
+ },
+ [AB8500_RTC] = {
+ .num_ranges = 2,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x14,
+ },
+ {
+ .first = 0x16,
+ .last = 0x17,
+ },
+ },
+ },
+ [AB8500_MISC] = {
+ .num_ranges = 8,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x06,
+ },
+ {
+ .first = 0x10,
+ .last = 0x16,
+ },
+ {
+ .first = 0x20,
+ .last = 0x26,
+ },
+ {
+ .first = 0x30,
+ .last = 0x36,
+ },
+ {
+ .first = 0x40,
+ .last = 0x46,
+ },
+ {
+ .first = 0x50,
+ .last = 0x50,
+ },
+ {
+ .first = 0x60,
+ .last = 0x6B,
+ },
+ {
+ .first = 0x80,
+ .last = 0x82,
+ },
+ },
+ },
+ [AB8500_DEVELOPMENT] = {
+ .num_ranges = 2,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x00,
+ },
+ {
+ .first = 0x05,
+ .last = 0x05,
+ },
+ },
+ },
+ [AB8500_DEBUG] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x05,
+ .last = 0x07,
+ },
+ },
+ },
+ [AB8500_PROD_TEST] = {
+ .num_ranges = 0,
+ .range = NULL,
+ },
+ [AB8500_STE_TEST] = {
+ .num_ranges = 0,
+ .range = NULL,
+ },
+ [AB8500_OTP_EMUL] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x01,
+ .last = 0x15,
+ },
+ },
+ },
+};
+
+static struct ab8500_prcmu_ranges ab8540_debug_ranges[AB8500_NUM_BANKS] = {
+ [AB8500_M_FSM_RANK] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0B,
+ },
+ },
+ },
+ [AB8500_SYS_CTRL1_BLOCK] = {
+ .num_ranges = 6,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x04,
+ },
+ {
+ .first = 0x42,
+ .last = 0x42,
+ },
+ {
+ .first = 0x50,
+ .last = 0x54,
+ },
+ {
+ .first = 0x57,
+ .last = 0x57,
+ },
+ {
+ .first = 0x80,
+ .last = 0x83,
+ },
+ {
+ .first = 0x90,
+ .last = 0x90,
+ },
+ },
+ },
+ [AB8500_SYS_CTRL2_BLOCK] = {
+ .num_ranges = 5,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0D,
+ },
+ {
+ .first = 0x0F,
+ .last = 0x10,
+ },
+ {
+ .first = 0x20,
+ .last = 0x21,
+ },
+ {
+ .first = 0x32,
+ .last = 0x3C,
+ },
+ {
+ .first = 0x40,
+ .last = 0x42,
+ },
+ },
+ },
+ [AB8500_REGU_CTRL1] = {
+ .num_ranges = 4,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x03,
+ .last = 0x15,
+ },
+ {
+ .first = 0x20,
+ .last = 0x20,
+ },
+ {
+ .first = 0x80,
+ .last = 0x85,
+ },
+ {
+ .first = 0x87,
+ .last = 0x88,
+ },
+ },
+ },
+ [AB8500_REGU_CTRL2] = {
+ .num_ranges = 8,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x06,
+ },
+ {
+ .first = 0x08,
+ .last = 0x15,
+ },
+ {
+ .first = 0x17,
+ .last = 0x19,
+ },
+ {
+ .first = 0x1B,
+ .last = 0x1D,
+ },
+ {
+ .first = 0x1F,
+ .last = 0x2F,
+ },
+ {
+ .first = 0x31,
+ .last = 0x3A,
+ },
+ {
+ .first = 0x43,
+ .last = 0x44,
+ },
+ {
+ .first = 0x48,
+ .last = 0x49,
+ },
+ },
+ },
+ [AB8500_USB] = {
+ .num_ranges = 3,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x80,
+ .last = 0x83,
+ },
+ {
+ .first = 0x87,
+ .last = 0x8A,
+ },
+ {
+ .first = 0x91,
+ .last = 0x94,
+ },
+ },
+ },
+ [AB8500_TVOUT] = {
+ .num_ranges = 0,
+ .range = NULL
+ },
+ [AB8500_DBI] = {
+ .num_ranges = 4,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x07,
+ },
+ {
+ .first = 0x10,
+ .last = 0x11,
+ },
+ {
+ .first = 0x20,
+ .last = 0x21,
+ },
+ {
+ .first = 0x30,
+ .last = 0x43,
+ },
+ },
+ },
+ [AB8500_ECI_AV_ACC] = {
+ .num_ranges = 2,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x03,
+ },
+ {
+ .first = 0x80,
+ .last = 0x82,
+ },
+ },
+ },
+ [AB8500_RESERVED] = {
+ .num_ranges = 0,
+ .range = NULL,
+ },
+ [AB8500_GPADC] = {
+ .num_ranges = 4,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x01,
+ },
+ {
+ .first = 0x04,
+ .last = 0x06,
+ },
+ {
+ .first = 0x09,
+ .last = 0x0A,
+ },
+ {
+ .first = 0x10,
+ .last = 0x14,
+ },
+ },
+ },
+ [AB8500_CHARGER] = {
+ .num_ranges = 10,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x00,
+ },
+ {
+ .first = 0x02,
+ .last = 0x05,
+ },
+ {
+ .first = 0x40,
+ .last = 0x44,
+ },
+ {
+ .first = 0x50,
+ .last = 0x57,
+ },
+ {
+ .first = 0x60,
+ .last = 0x60,
+ },
+ {
+ .first = 0x70,
+ .last = 0x70,
+ },
+ {
+ .first = 0xA0,
+ .last = 0xA9,
+ },
+ {
+ .first = 0xAF,
+ .last = 0xB2,
+ },
+ {
+ .first = 0xC0,
+ .last = 0xC6,
+ },
+ {
+ .first = 0xF5,
+ .last = 0xF5,
+ },
+ },
+ },
+ [AB8500_GAS_GAUGE] = {
+ .num_ranges = 3,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x00,
+ },
+ {
+ .first = 0x07,
+ .last = 0x0A,
+ },
+ {
+ .first = 0x10,
+ .last = 0x14,
+ },
+ },
+ },
+ [AB8500_AUDIO] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x9f,
+ },
+ },
+ },
+ [AB8500_INTERRUPT] = {
+ .num_ranges = 6,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x05,
+ },
+ {
+ .first = 0x0B,
+ .last = 0x0D,
+ },
+ {
+ .first = 0x12,
+ .last = 0x20,
+ },
+ /* Latch registers should not be read here */
+ {
+ .first = 0x40,
+ .last = 0x45,
+ },
+ {
+ .first = 0x4B,
+ .last = 0x4D,
+ },
+ {
+ .first = 0x52,
+ .last = 0x60,
+ },
+ /* LatchHier registers should not be read here */
+ },
+ },
+ [AB8500_RTC] = {
+ .num_ranges = 3,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x07,
+ },
+ {
+ .first = 0x0B,
+ .last = 0x18,
+ },
+ {
+ .first = 0x20,
+ .last = 0x25,
+ },
+ },
+ },
+ [AB8500_MISC] = {
+ .num_ranges = 9,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x06,
+ },
+ {
+ .first = 0x10,
+ .last = 0x16,
+ },
+ {
+ .first = 0x20,
+ .last = 0x26,
+ },
+ {
+ .first = 0x30,
+ .last = 0x36,
+ },
+ {
+ .first = 0x40,
+ .last = 0x49,
+ },
+ {
+ .first = 0x50,
+ .last = 0x50,
+ },
+ {
+ .first = 0x60,
+ .last = 0x6B,
+ },
+ {
+ .first = 0x70,
+ .last = 0x74,
+ },
+ {
+ .first = 0x80,
+ .last = 0x82,
+ },
+ },
+ },
+ [AB8500_DEVELOPMENT] = {
+ .num_ranges = 3,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x01,
+ },
+ {
+ .first = 0x06,
+ .last = 0x06,
+ },
+ {
+ .first = 0x10,
+ .last = 0x21,
+ },
+ },
+ },
+ [AB8500_DEBUG] = {
+ .num_ranges = 3,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x01,
+ .last = 0x0C,
+ },
+ {
+ .first = 0x0E,
+ .last = 0x11,
+ },
+ {
+ .first = 0x80,
+ .last = 0x81,
+ },
+ },
+ },
+ [AB8500_PROD_TEST] = {
+ .num_ranges = 0,
+ .range = NULL,
+ },
+ [AB8500_STE_TEST] = {
+ .num_ranges = 0,
+ .range = NULL,
+ },
+ [AB8500_OTP_EMUL] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x3F,
+ },
+ },
+ },
+};
+
+
+static irqreturn_t ab8500_debug_handler(int irq, void *data)
{
- struct device *dev = s->private;
- unsigned int i;
- u32 bank = debug_bank;
+ char buf[16];
+ struct kobject *kobj = (struct kobject *)data;
+ unsigned int irq_abb = irq - irq_first;
- seq_printf(s, AB8500_NAME_STRING " register values:\n");
+ if (irq_abb < num_irqs)
+ irq_count[irq_abb]++;
+ /*
+ * This makes it possible to use poll for events (POLLPRI | POLLERR)
+ * from userspace on sysfs file named <irq-nr>
+ */
+ sprintf(buf, "%d", irq);
+ sysfs_notify(kobj, NULL, buf);
+
+ return IRQ_HANDLED;
+}
+
+/* Prints to seq_file or log_buf */
+static int ab8500_registers_print(struct device *dev, u32 bank,
+ struct seq_file *s)
+{
+ unsigned int i;
- seq_printf(s, " bank %u:\n", bank);
for (i = 0; i < debug_ranges[bank].num_ranges; i++) {
u32 reg;
@@ -379,22 +1303,39 @@ static int ab8500_registers_print(struct seq_file *s, void *p)
return err;
}
- err = seq_printf(s, " [%u/0x%02X]: 0x%02X\n", bank,
- reg, value);
- if (err < 0) {
- dev_err(dev, "seq_printf overflow\n");
- /* Error is not returned here since
- * the output is wanted in any case */
- return 0;
+ if (s) {
+ err = seq_printf(s, " [0x%02X/0x%02X]: 0x%02X\n",
+ bank, reg, value);
+ if (err < 0) {
+ /* Error is not returned here since
+ * the output is wanted in any case */
+ return 0;
+ }
+ } else {
+ printk(KERN_INFO" [0x%02X/0x%02X]: 0x%02X\n",
+ bank, reg, value);
}
}
}
return 0;
}
+static int ab8500_print_bank_registers(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ u32 bank = debug_bank;
+
+ seq_printf(s, AB8500_NAME_STRING " register values:\n");
+
+ seq_printf(s, " bank 0x%02X:\n", bank);
+
+ ab8500_registers_print(dev, bank, s);
+ return 0;
+}
+
static int ab8500_registers_open(struct inode *inode, struct file *file)
{
- return single_open(file, ab8500_registers_print, inode->i_private);
+ return single_open(file, ab8500_print_bank_registers, inode->i_private);
}
static const struct file_operations ab8500_registers_fops = {
@@ -405,9 +1346,122 @@ static const struct file_operations ab8500_registers_fops = {
.owner = THIS_MODULE,
};
+static int ab8500_print_all_banks(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ unsigned int i;
+ int err;
+
+ seq_printf(s, AB8500_NAME_STRING " register values:\n");
+
+ for (i = 0; i < AB8500_NUM_BANKS; i++) {
+ err = seq_printf(s, " bank 0x%02X:\n", i);
+
+ ab8500_registers_print(dev, i, s);
+ }
+ return 0;
+}
+
+/* Dump registers to kernel log */
+void ab8500_dump_all_banks(struct device *dev)
+{
+ unsigned int i;
+
+ printk(KERN_INFO"ab8500 register values:\n");
+
+ for (i = 1; i < AB8500_NUM_BANKS; i++) {
+ printk(KERN_INFO" bank 0x%02X:\n", i);
+ ab8500_registers_print(dev, i, NULL);
+ }
+}
+
+/* Space for 500 registers. */
+#define DUMP_MAX_REGS 700
+static struct ab8500_register_dump
+{
+ u8 bank;
+ u8 reg;
+ u8 value;
+} ab8500_complete_register_dump[DUMP_MAX_REGS];
+
+extern int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size);
+
+/* This shall only be called upon kernel panic! */
+void ab8500_dump_all_banks_to_mem(void)
+{
+ int i, r = 0;
+ u8 bank;
+ int err = 0;
+
+ pr_info("Saving all ABB registers at \"ab8500_complete_register_dump\" "
+ "for crash analyze.\n");
+
+ for (bank = 0; bank < AB8500_NUM_BANKS; bank++) {
+ for (i = 0; i < debug_ranges[bank].num_ranges; i++) {
+ u8 reg;
+
+ for (reg = debug_ranges[bank].range[i].first;
+ reg <= debug_ranges[bank].range[i].last;
+ reg++) {
+ u8 value;
+
+ err = prcmu_abb_read(bank, reg, &value, 1);
+
+ if (err < 0)
+ goto out;
+
+ ab8500_complete_register_dump[r].bank = bank;
+ ab8500_complete_register_dump[r].reg = reg;
+ ab8500_complete_register_dump[r].value = value;
+
+ r++;
+
+ if (r >= DUMP_MAX_REGS) {
+ pr_err("%s: too many register to dump!\n",
+ __func__);
+ err = -EINVAL;
+ goto out;
+ }
+ }
+ }
+ }
+out:
+ if (err >= 0)
+ pr_info("Saved all ABB registers.\n");
+ else
+ pr_info("Failed to save all ABB registers.\n");
+}
+
+static int ab8500_all_banks_open(struct inode *inode, struct file *file)
+{
+ struct seq_file *s;
+ int err;
+
+ err = single_open(file, ab8500_print_all_banks, inode->i_private);
+ if (!err) {
+ /* Default buf size in seq_read is not enough */
+ s = (struct seq_file *)file->private_data;
+ s->size = (PAGE_SIZE * 2);
+ s->buf = kmalloc(s->size, GFP_KERNEL);
+ if (!s->buf) {
+ single_release(inode, file);
+ err = -ENOMEM;
+ }
+ }
+ return err;
+}
+
+static const struct file_operations ab8500_all_banks_fops = {
+ .open = ab8500_all_banks_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
static int ab8500_bank_print(struct seq_file *s, void *p)
{
- return seq_printf(s, "%d\n", debug_bank);
+ return seq_printf(s, "0x%02X\n", debug_bank);
}
static int ab8500_bank_open(struct inode *inode, struct file *file)
@@ -423,7 +1477,6 @@ static ssize_t ab8500_bank_write(struct file *file,
unsigned long user_bank;
int err;
- /* Get userspace string and assure termination */
err = kstrtoul_from_user(user_buf, count, 0, &user_bank);
if (err)
return err;
@@ -449,14 +1502,13 @@ static int ab8500_address_open(struct inode *inode, struct file *file)
}
static ssize_t ab8500_address_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
unsigned long user_address;
int err;
- /* Get userspace string and assure termination */
err = kstrtoul_from_user(user_buf, count, 0, &user_address);
if (err)
return err;
@@ -466,6 +1518,7 @@ static ssize_t ab8500_address_write(struct file *file,
return -EINVAL;
}
debug_address = user_address;
+
return count;
}
@@ -493,14 +1546,13 @@ static int ab8500_val_open(struct inode *inode, struct file *file)
}
static ssize_t ab8500_val_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
unsigned long user_val;
int err;
- /* Get userspace string and assure termination */
err = kstrtoul_from_user(user_buf, count, 0, &user_val);
if (err)
return err;
@@ -519,6 +1571,1311 @@ static ssize_t ab8500_val_write(struct file *file,
return count;
}
+/*
+ * Interrupt status
+ */
+static u32 num_interrupts[AB8500_MAX_NR_IRQS];
+static u32 num_wake_interrupts[AB8500_MAX_NR_IRQS];
+static int num_interrupt_lines;
+
+bool __attribute__((weak)) suspend_test_wake_cause_interrupt_is_mine(u32 my_int)
+{
+ return false;
+}
+
+void ab8500_debug_register_interrupt(int line)
+{
+ if (line < num_interrupt_lines) {
+ num_interrupts[line]++;
+ if (suspend_test_wake_cause_interrupt_is_mine(irq_ab8500))
+ num_wake_interrupts[line]++;
+ }
+}
+
+static int ab8500_interrupts_print(struct seq_file *s, void *p)
+{
+ int line;
+
+ seq_printf(s, "name: number: number of: wake:\n");
+
+ for (line = 0; line < num_interrupt_lines; line++) {
+ struct irq_desc *desc = irq_to_desc(line + irq_first);
+
+ seq_printf(s, "%3i: %6i %4i", line,
+ num_interrupts[line],
+ num_wake_interrupts[line]);
+
+ if (desc && desc->name)
+ seq_printf(s, "-%-8s", desc->name);
+ if (desc && desc->action) {
+ struct irqaction *action = desc->action;
+
+ seq_printf(s, " %s", action->name);
+ while ((action = action->next) != NULL)
+ seq_printf(s, ", %s", action->name);
+ }
+ seq_putc(s, '\n');
+ }
+
+ return 0;
+}
+
+static int ab8500_interrupts_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_interrupts_print, inode->i_private);
+}
+
+/*
+ * - HWREG DB8500 formated routines
+ */
+static int ab8500_hwreg_print(struct seq_file *s, void *d)
+{
+ struct device *dev = s->private;
+ int ret;
+ u8 regvalue;
+
+ ret = abx500_get_register_interruptible(dev,
+ (u8)hwreg_cfg.bank, (u8)hwreg_cfg.addr, &regvalue);
+ if (ret < 0) {
+ dev_err(dev, "abx500_get_reg fail %d, %d\n",
+ ret, __LINE__);
+ return -EINVAL;
+ }
+
+ if (hwreg_cfg.shift >= 0)
+ regvalue >>= hwreg_cfg.shift;
+ else
+ regvalue <<= -hwreg_cfg.shift;
+ regvalue &= hwreg_cfg.mask;
+
+ if (REG_FMT_DEC(&hwreg_cfg))
+ seq_printf(s, "%d\n", regvalue);
+ else
+ seq_printf(s, "0x%02X\n", regvalue);
+ return 0;
+}
+
+static int ab8500_hwreg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_hwreg_print, inode->i_private);
+}
+
+#define AB8500_SUPPLY_CONTROL_CONFIG_1 0x01
+#define AB8500_SUPPLY_CONTROL_REG 0x00
+#define AB8500_FIRST_SIM_REG 0x80
+#define AB8500_LAST_SIM_REG 0x8B
+#define AB8505_LAST_SIM_REG 0x8C
+
+static int ab8500_print_modem_registers(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ struct ab8500 *ab8500;
+ int err;
+ u8 value;
+ u8 orig_value;
+ u32 bank = AB8500_REGU_CTRL2;
+ u32 last_sim_reg = AB8500_LAST_SIM_REG;
+ u32 reg;
+
+ ab8500 = dev_get_drvdata(dev->parent);
+ dev_warn(dev, "WARNING! This operation can interfer with modem side\n"
+ "and should only be done with care\n");
+
+ err = abx500_get_register_interruptible(dev,
+ AB8500_REGU_CTRL1, AB8500_SUPPLY_CONTROL_REG, &orig_value);
+ if (err < 0) {
+ dev_err(dev, "ab->read fail %d\n", err);
+ return err;
+ }
+ /* Config 1 will allow APE side to read SIM registers */
+ err = abx500_set_register_interruptible(dev,
+ AB8500_REGU_CTRL1, AB8500_SUPPLY_CONTROL_REG,
+ AB8500_SUPPLY_CONTROL_CONFIG_1);
+ if (err < 0) {
+ dev_err(dev, "ab->write fail %d\n", err);
+ return err;
+ }
+
+ seq_printf(s, " bank 0x%02X:\n", bank);
+
+ if (is_ab9540(ab8500) || is_ab8505(ab8500))
+ last_sim_reg = AB8505_LAST_SIM_REG;
+
+ for (reg = AB8500_FIRST_SIM_REG; reg <= last_sim_reg; reg++) {
+ err = abx500_get_register_interruptible(dev,
+ bank, reg, &value);
+ if (err < 0) {
+ dev_err(dev, "ab->read fail %d\n", err);
+ return err;
+ }
+ err = seq_printf(s, " [0x%02X/0x%02X]: 0x%02X\n",
+ bank, reg, value);
+ }
+ err = abx500_set_register_interruptible(dev,
+ AB8500_REGU_CTRL1, AB8500_SUPPLY_CONTROL_REG, orig_value);
+ if (err < 0) {
+ dev_err(dev, "ab->write fail %d\n", err);
+ return err;
+ }
+ return 0;
+}
+
+static int ab8500_modem_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_print_modem_registers, inode->i_private);
+}
+
+static const struct file_operations ab8500_modem_fops = {
+ .open = ab8500_modem_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p)
+{
+ int bat_ctrl_raw;
+ int bat_ctrl_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ bat_ctrl_raw = ab8500_gpadc_read_raw(gpadc, BAT_CTRL,
+ avg_sample, trig_edge, trig_timer, conv_type);
+ bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ BAT_CTRL, bat_ctrl_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ bat_ctrl_convert, bat_ctrl_raw);
+}
+
+static int ab8500_gpadc_bat_ctrl_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_bat_ctrl_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_bat_ctrl_fops = {
+ .open = ab8500_gpadc_bat_ctrl_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p)
+{
+ int btemp_ball_raw;
+ int btemp_ball_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ btemp_ball_raw = ab8500_gpadc_read_raw(gpadc, BTEMP_BALL,
+ avg_sample, trig_edge, trig_timer, conv_type);
+ btemp_ball_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL,
+ btemp_ball_raw);
+
+ return seq_printf(s,
+ "%d,0x%X\n", btemp_ball_convert, btemp_ball_raw);
+}
+
+static int ab8500_gpadc_btemp_ball_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_btemp_ball_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_btemp_ball_fops = {
+ .open = ab8500_gpadc_btemp_ball_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p)
+{
+ int main_charger_v_raw;
+ int main_charger_v_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ main_charger_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_V,
+ avg_sample, trig_edge, trig_timer, conv_type);
+ main_charger_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ MAIN_CHARGER_V, main_charger_v_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ main_charger_v_convert, main_charger_v_raw);
+}
+
+static int ab8500_gpadc_main_charger_v_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_main_charger_v_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_main_charger_v_fops = {
+ .open = ab8500_gpadc_main_charger_v_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p)
+{
+ int acc_detect1_raw;
+ int acc_detect1_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ acc_detect1_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT1,
+ avg_sample, trig_edge, trig_timer, conv_type);
+ acc_detect1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ACC_DETECT1,
+ acc_detect1_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ acc_detect1_convert, acc_detect1_raw);
+}
+
+static int ab8500_gpadc_acc_detect1_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_acc_detect1_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_acc_detect1_fops = {
+ .open = ab8500_gpadc_acc_detect1_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p)
+{
+ int acc_detect2_raw;
+ int acc_detect2_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ acc_detect2_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT2,
+ avg_sample, trig_edge, trig_timer, conv_type);
+ acc_detect2_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ ACC_DETECT2, acc_detect2_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ acc_detect2_convert, acc_detect2_raw);
+}
+
+static int ab8500_gpadc_acc_detect2_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_acc_detect2_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_acc_detect2_fops = {
+ .open = ab8500_gpadc_acc_detect2_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p)
+{
+ int aux1_raw;
+ int aux1_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ aux1_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX1,
+ avg_sample, trig_edge, trig_timer, conv_type);
+ aux1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX1,
+ aux1_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ aux1_convert, aux1_raw);
+}
+
+static int ab8500_gpadc_aux1_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_aux1_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_aux1_fops = {
+ .open = ab8500_gpadc_aux1_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p)
+{
+ int aux2_raw;
+ int aux2_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ aux2_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX2,
+ avg_sample, trig_edge, trig_timer, conv_type);
+ aux2_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX2,
+ aux2_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ aux2_convert, aux2_raw);
+}
+
+static int ab8500_gpadc_aux2_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_aux2_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_aux2_fops = {
+ .open = ab8500_gpadc_aux2_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p)
+{
+ int main_bat_v_raw;
+ int main_bat_v_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ main_bat_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_BAT_V,
+ avg_sample, trig_edge, trig_timer, conv_type);
+ main_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V,
+ main_bat_v_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ main_bat_v_convert, main_bat_v_raw);
+}
+
+static int ab8500_gpadc_main_bat_v_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_main_bat_v_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_main_bat_v_fops = {
+ .open = ab8500_gpadc_main_bat_v_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p)
+{
+ int vbus_v_raw;
+ int vbus_v_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ vbus_v_raw = ab8500_gpadc_read_raw(gpadc, VBUS_V,
+ avg_sample, trig_edge, trig_timer, conv_type);
+ vbus_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBUS_V,
+ vbus_v_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ vbus_v_convert, vbus_v_raw);
+}
+
+static int ab8500_gpadc_vbus_v_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_vbus_v_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_vbus_v_fops = {
+ .open = ab8500_gpadc_vbus_v_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p)
+{
+ int main_charger_c_raw;
+ int main_charger_c_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ main_charger_c_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_C,
+ avg_sample, trig_edge, trig_timer, conv_type);
+ main_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ MAIN_CHARGER_C, main_charger_c_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ main_charger_c_convert, main_charger_c_raw);
+}
+
+static int ab8500_gpadc_main_charger_c_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_main_charger_c_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_main_charger_c_fops = {
+ .open = ab8500_gpadc_main_charger_c_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p)
+{
+ int usb_charger_c_raw;
+ int usb_charger_c_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ usb_charger_c_raw = ab8500_gpadc_read_raw(gpadc, USB_CHARGER_C,
+ avg_sample, trig_edge, trig_timer, conv_type);
+ usb_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ USB_CHARGER_C, usb_charger_c_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ usb_charger_c_convert, usb_charger_c_raw);
+}
+
+static int ab8500_gpadc_usb_charger_c_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_usb_charger_c_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_usb_charger_c_fops = {
+ .open = ab8500_gpadc_usb_charger_c_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p)
+{
+ int bk_bat_v_raw;
+ int bk_bat_v_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ bk_bat_v_raw = ab8500_gpadc_read_raw(gpadc, BK_BAT_V,
+ avg_sample, trig_edge, trig_timer, conv_type);
+ bk_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ BK_BAT_V, bk_bat_v_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ bk_bat_v_convert, bk_bat_v_raw);
+}
+
+static int ab8500_gpadc_bk_bat_v_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_bk_bat_v_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_bk_bat_v_fops = {
+ .open = ab8500_gpadc_bk_bat_v_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p)
+{
+ int die_temp_raw;
+ int die_temp_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ die_temp_raw = ab8500_gpadc_read_raw(gpadc, DIE_TEMP,
+ avg_sample, trig_edge, trig_timer, conv_type);
+ die_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, DIE_TEMP,
+ die_temp_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ die_temp_convert, die_temp_raw);
+}
+
+static int ab8500_gpadc_die_temp_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_die_temp_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_die_temp_fops = {
+ .open = ab8500_gpadc_die_temp_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_usb_id_print(struct seq_file *s, void *p)
+{
+ int usb_id_raw;
+ int usb_id_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ usb_id_raw = ab8500_gpadc_read_raw(gpadc, USB_ID,
+ avg_sample, trig_edge, trig_timer, conv_type);
+ usb_id_convert = ab8500_gpadc_ad_to_voltage(gpadc, USB_ID,
+ usb_id_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ usb_id_convert, usb_id_raw);
+}
+
+static int ab8500_gpadc_usb_id_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_usb_id_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_usb_id_fops = {
+ .open = ab8500_gpadc_usb_id_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8540_gpadc_xtal_temp_print(struct seq_file *s, void *p)
+{
+ int xtal_temp_raw;
+ int xtal_temp_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ xtal_temp_raw = ab8500_gpadc_read_raw(gpadc, XTAL_TEMP,
+ avg_sample, trig_edge, trig_timer, conv_type);
+ xtal_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, XTAL_TEMP,
+ xtal_temp_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ xtal_temp_convert, xtal_temp_raw);
+}
+
+static int ab8540_gpadc_xtal_temp_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8540_gpadc_xtal_temp_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8540_gpadc_xtal_temp_fops = {
+ .open = ab8540_gpadc_xtal_temp_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8540_gpadc_vbat_true_meas_print(struct seq_file *s, void *p)
+{
+ int vbat_true_meas_raw;
+ int vbat_true_meas_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ vbat_true_meas_raw = ab8500_gpadc_read_raw(gpadc, VBAT_TRUE_MEAS,
+ avg_sample, trig_edge, trig_timer, conv_type);
+ vbat_true_meas_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBAT_TRUE_MEAS,
+ vbat_true_meas_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ vbat_true_meas_convert, vbat_true_meas_raw);
+}
+
+static int ab8540_gpadc_vbat_true_meas_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8540_gpadc_vbat_true_meas_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8540_gpadc_vbat_true_meas_fops = {
+ .open = ab8540_gpadc_vbat_true_meas_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8540_gpadc_bat_ctrl_and_ibat_print(struct seq_file *s, void *p)
+{
+ int bat_ctrl_raw;
+ int bat_ctrl_convert;
+ int ibat_raw;
+ int ibat_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ bat_ctrl_raw = ab8500_gpadc_double_read_raw(gpadc, BAT_CTRL_AND_IBAT,
+ avg_sample, trig_edge, trig_timer, conv_type, &ibat_raw);
+
+ bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc, BAT_CTRL,
+ bat_ctrl_raw);
+ ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL,
+ ibat_raw);
+
+ return seq_printf(s, "%d,0x%X\n" "%d,0x%X\n",
+ bat_ctrl_convert, bat_ctrl_raw,
+ ibat_convert, ibat_raw);
+}
+
+static int ab8540_gpadc_bat_ctrl_and_ibat_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8540_gpadc_bat_ctrl_and_ibat_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8540_gpadc_bat_ctrl_and_ibat_fops = {
+ .open = ab8540_gpadc_bat_ctrl_and_ibat_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8540_gpadc_vbat_meas_and_ibat_print(struct seq_file *s, void *p)
+{
+ int vbat_meas_raw;
+ int vbat_meas_convert;
+ int ibat_raw;
+ int ibat_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ vbat_meas_raw = ab8500_gpadc_double_read_raw(gpadc, VBAT_MEAS_AND_IBAT,
+ avg_sample, trig_edge, trig_timer, conv_type, &ibat_raw);
+ vbat_meas_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V,
+ vbat_meas_raw);
+ ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL,
+ ibat_raw);
+
+ return seq_printf(s, "%d,0x%X\n" "%d,0x%X\n",
+ vbat_meas_convert, vbat_meas_raw,
+ ibat_convert, ibat_raw);
+}
+
+static int ab8540_gpadc_vbat_meas_and_ibat_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8540_gpadc_vbat_meas_and_ibat_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8540_gpadc_vbat_meas_and_ibat_fops = {
+ .open = ab8540_gpadc_vbat_meas_and_ibat_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8540_gpadc_vbat_true_meas_and_ibat_print(struct seq_file *s, void *p)
+{
+ int vbat_true_meas_raw;
+ int vbat_true_meas_convert;
+ int ibat_raw;
+ int ibat_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ vbat_true_meas_raw = ab8500_gpadc_double_read_raw(gpadc,
+ VBAT_TRUE_MEAS_AND_IBAT, avg_sample, trig_edge,
+ trig_timer, conv_type, &ibat_raw);
+ vbat_true_meas_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ VBAT_TRUE_MEAS, vbat_true_meas_raw);
+ ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL,
+ ibat_raw);
+
+ return seq_printf(s, "%d,0x%X\n" "%d,0x%X\n",
+ vbat_true_meas_convert, vbat_true_meas_raw,
+ ibat_convert, ibat_raw);
+}
+
+static int ab8540_gpadc_vbat_true_meas_and_ibat_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8540_gpadc_vbat_true_meas_and_ibat_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8540_gpadc_vbat_true_meas_and_ibat_fops = {
+ .open = ab8540_gpadc_vbat_true_meas_and_ibat_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8540_gpadc_bat_temp_and_ibat_print(struct seq_file *s, void *p)
+{
+ int bat_temp_raw;
+ int bat_temp_convert;
+ int ibat_raw;
+ int ibat_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ bat_temp_raw = ab8500_gpadc_double_read_raw(gpadc, BAT_TEMP_AND_IBAT,
+ avg_sample, trig_edge, trig_timer, conv_type, &ibat_raw);
+ bat_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL,
+ bat_temp_raw);
+ ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL,
+ ibat_raw);
+
+ return seq_printf(s, "%d,0x%X\n" "%d,0x%X\n",
+ bat_temp_convert, bat_temp_raw,
+ ibat_convert, ibat_raw);
+}
+
+static int ab8540_gpadc_bat_temp_and_ibat_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8540_gpadc_bat_temp_and_ibat_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8540_gpadc_bat_temp_and_ibat_fops = {
+ .open = ab8540_gpadc_bat_temp_and_ibat_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8540_gpadc_otp_cal_print(struct seq_file *s, void *p)
+{
+ struct ab8500_gpadc *gpadc;
+ u16 vmain_l, vmain_h, btemp_l, btemp_h;
+ u16 vbat_l, vbat_h, ibat_l, ibat_h;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ ab8540_gpadc_get_otp(gpadc, &vmain_l, &vmain_h, &btemp_l, &btemp_h,
+ &vbat_l, &vbat_h, &ibat_l, &ibat_h);
+ return seq_printf(s, "VMAIN_L:0x%X\n"
+ "VMAIN_H:0x%X\n"
+ "BTEMP_L:0x%X\n"
+ "BTEMP_H:0x%X\n"
+ "VBAT_L:0x%X\n"
+ "VBAT_H:0x%X\n"
+ "IBAT_L:0x%X\n"
+ "IBAT_H:0x%X\n",
+ vmain_l, vmain_h, btemp_l, btemp_h, vbat_l, vbat_h, ibat_l, ibat_h);
+}
+
+static int ab8540_gpadc_otp_cal_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8540_gpadc_otp_cal_print, inode->i_private);
+}
+
+static const struct file_operations ab8540_gpadc_otp_calib_fops = {
+ .open = ab8540_gpadc_otp_cal_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_avg_sample_print(struct seq_file *s, void *p)
+{
+ return seq_printf(s, "%d\n", avg_sample);
+}
+
+static int ab8500_gpadc_avg_sample_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_avg_sample_print,
+ inode->i_private);
+}
+
+static ssize_t ab8500_gpadc_avg_sample_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ unsigned long user_avg_sample;
+ int err;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &user_avg_sample);
+ if (err)
+ return err;
+
+ if ((user_avg_sample == SAMPLE_1) || (user_avg_sample == SAMPLE_4)
+ || (user_avg_sample == SAMPLE_8)
+ || (user_avg_sample == SAMPLE_16)) {
+ avg_sample = (u8) user_avg_sample;
+ } else {
+ dev_err(dev, "debugfs error input: "
+ "should be egal to 1, 4, 8 or 16\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static const struct file_operations ab8500_gpadc_avg_sample_fops = {
+ .open = ab8500_gpadc_avg_sample_open,
+ .read = seq_read,
+ .write = ab8500_gpadc_avg_sample_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_trig_edge_print(struct seq_file *s, void *p)
+{
+ return seq_printf(s, "%d\n", trig_edge);
+}
+
+static int ab8500_gpadc_trig_edge_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_trig_edge_print,
+ inode->i_private);
+}
+
+static ssize_t ab8500_gpadc_trig_edge_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ unsigned long user_trig_edge;
+ int err;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &user_trig_edge);
+ if (err)
+ return err;
+
+ if ((user_trig_edge == RISING_EDGE)
+ || (user_trig_edge == FALLING_EDGE)) {
+ trig_edge = (u8) user_trig_edge;
+ } else {
+ dev_err(dev, "Wrong input:\n"
+ "Enter 0. Rising edge\n"
+ "Enter 1. Falling edge\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static const struct file_operations ab8500_gpadc_trig_edge_fops = {
+ .open = ab8500_gpadc_trig_edge_open,
+ .read = seq_read,
+ .write = ab8500_gpadc_trig_edge_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_trig_timer_print(struct seq_file *s, void *p)
+{
+ return seq_printf(s, "%d\n", trig_timer);
+}
+
+static int ab8500_gpadc_trig_timer_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_trig_timer_print,
+ inode->i_private);
+}
+
+static ssize_t ab8500_gpadc_trig_timer_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ unsigned long user_trig_timer;
+ int err;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &user_trig_timer);
+ if (err)
+ return err;
+
+ if ((user_trig_timer >= 0) && (user_trig_timer <= 255)) {
+ trig_timer = (u8) user_trig_timer;
+ } else {
+ dev_err(dev, "debugfs error input: "
+ "should be beetween 0 to 255\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static const struct file_operations ab8500_gpadc_trig_timer_fops = {
+ .open = ab8500_gpadc_trig_timer_open,
+ .read = seq_read,
+ .write = ab8500_gpadc_trig_timer_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_conv_type_print(struct seq_file *s, void *p)
+{
+ return seq_printf(s, "%d\n", conv_type);
+}
+
+static int ab8500_gpadc_conv_type_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_conv_type_print,
+ inode->i_private);
+}
+
+static ssize_t ab8500_gpadc_conv_type_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ unsigned long user_conv_type;
+ int err;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &user_conv_type);
+ if (err)
+ return err;
+
+ if ((user_conv_type == ADC_SW)
+ || (user_conv_type == ADC_HW)) {
+ conv_type = (u8) user_conv_type;
+ } else {
+ dev_err(dev, "Wrong input:\n"
+ "Enter 0. ADC SW conversion\n"
+ "Enter 1. ADC HW conversion\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static const struct file_operations ab8500_gpadc_conv_type_fops = {
+ .open = ab8500_gpadc_conv_type_open,
+ .read = seq_read,
+ .write = ab8500_gpadc_conv_type_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * return length of an ASCII numerical value, 0 is string is not a
+ * numerical value.
+ * string shall start at value 1st char.
+ * string can be tailed with \0 or space or newline chars only.
+ * value can be decimal or hexadecimal (prefixed 0x or 0X).
+ */
+static int strval_len(char *b)
+{
+ char *s = b;
+ if ((*s == '0') && ((*(s+1) == 'x') || (*(s+1) == 'X'))) {
+ s += 2;
+ for (; *s && (*s != ' ') && (*s != '\n'); s++) {
+ if (!isxdigit(*s))
+ return 0;
+ }
+ } else {
+ if (*s == '-')
+ s++;
+ for (; *s && (*s != ' ') && (*s != '\n'); s++) {
+ if (!isdigit(*s))
+ return 0;
+ }
+ }
+ return (int) (s-b);
+}
+
+/*
+ * parse hwreg input data.
+ * update global hwreg_cfg only if input data syntax is ok.
+ */
+static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg,
+ struct device *dev)
+{
+ uint write, val = 0;
+ u8 regvalue;
+ int ret;
+ struct hwreg_cfg loc = {
+ .bank = 0, /* default: invalid phys addr */
+ .addr = 0, /* default: invalid phys addr */
+ .fmt = 0, /* default: 32bit access, hex output */
+ .mask = 0xFFFFFFFF, /* default: no mask */
+ .shift = 0, /* default: no bit shift */
+ };
+
+ /* read or write ? */
+ if (!strncmp(b, "read ", 5)) {
+ write = 0;
+ b += 5;
+ } else if (!strncmp(b, "write ", 6)) {
+ write = 1;
+ b += 6;
+ } else
+ return -EINVAL;
+
+ /* OPTIONS -l|-w|-b -s -m -o */
+ while ((*b == ' ') || (*b == '-')) {
+ if (*(b-1) != ' ') {
+ b++;
+ continue;
+ }
+ if ((!strncmp(b, "-d ", 3)) ||
+ (!strncmp(b, "-dec ", 5))) {
+ b += (*(b+2) == ' ') ? 3 : 5;
+ loc.fmt |= (1<<0);
+ } else if ((!strncmp(b, "-h ", 3)) ||
+ (!strncmp(b, "-hex ", 5))) {
+ b += (*(b+2) == ' ') ? 3 : 5;
+ loc.fmt &= ~(1<<0);
+ } else if ((!strncmp(b, "-m ", 3)) ||
+ (!strncmp(b, "-mask ", 6))) {
+ b += (*(b+2) == ' ') ? 3 : 6;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.mask = simple_strtoul(b, &b, 0);
+ } else if ((!strncmp(b, "-s ", 3)) ||
+ (!strncmp(b, "-shift ", 7))) {
+ b += (*(b+2) == ' ') ? 3 : 7;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.shift = simple_strtol(b, &b, 0);
+ } else {
+ return -EINVAL;
+ }
+ }
+ /* get arg BANK and ADDRESS */
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.bank = simple_strtoul(b, &b, 0);
+ while (*b == ' ')
+ b++;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.addr = simple_strtoul(b, &b, 0);
+
+ if (write) {
+ while (*b == ' ')
+ b++;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ val = simple_strtoul(b, &b, 0);
+ }
+
+ /* args are ok, update target cfg (mainly for read) */
+ *cfg = loc;
+
+#ifdef ABB_HWREG_DEBUG
+ pr_warn("HWREG request: %s, %s, addr=0x%08X, mask=0x%X, shift=%d"
+ "value=0x%X\n", (write) ? "write" : "read",
+ REG_FMT_DEC(cfg) ? "decimal" : "hexa",
+ cfg->addr, cfg->mask, cfg->shift, val);
+#endif
+
+ if (!write)
+ return 0;
+
+ ret = abx500_get_register_interruptible(dev,
+ (u8)cfg->bank, (u8)cfg->addr, &regvalue);
+ if (ret < 0) {
+ dev_err(dev, "abx500_get_reg fail %d, %d\n",
+ ret, __LINE__);
+ return -EINVAL;
+ }
+
+ if (cfg->shift >= 0) {
+ regvalue &= ~(cfg->mask << (cfg->shift));
+ val = (val & cfg->mask) << (cfg->shift);
+ } else {
+ regvalue &= ~(cfg->mask >> (-cfg->shift));
+ val = (val & cfg->mask) >> (-cfg->shift);
+ }
+ val = val | regvalue;
+
+ ret = abx500_set_register_interruptible(dev,
+ (u8)cfg->bank, (u8)cfg->addr, (u8)val);
+ if (ret < 0) {
+ pr_err("abx500_set_reg failed %d, %d", ret, __LINE__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static ssize_t ab8500_hwreg_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ char buf[128];
+ int buf_size, ret;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ /* get args and process */
+ ret = hwreg_common_write(buf, &hwreg_cfg, dev);
+ return (ret) ? ret : buf_size;
+}
+
+/*
+ * - irq subscribe/unsubscribe stuff
+ */
+static int ab8500_subscribe_unsubscribe_print(struct seq_file *s, void *p)
+{
+ seq_printf(s, "%d\n", irq_first);
+
+ return 0;
+}
+
+static int ab8500_subscribe_unsubscribe_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_subscribe_unsubscribe_print,
+ inode->i_private);
+}
+
+/*
+ * Userspace should use poll() on this file. When an event occur
+ * the blocking poll will be released.
+ */
+static ssize_t show_irq(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long name;
+ unsigned int irq_index;
+ int err;
+
+ err = kstrtoul(attr->attr.name, 0, &name);
+ if (err)
+ return err;
+
+ irq_index = name - irq_first;
+ if (irq_index >= num_irqs)
+ return -EINVAL;
+ else
+ return sprintf(buf, "%u\n", irq_count[irq_index]);
+}
+
+static ssize_t ab8500_subscribe_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ unsigned long user_val;
+ int err;
+ unsigned int irq_index;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &user_val);
+ if (err)
+ return err;
+
+ if (user_val < irq_first) {
+ dev_err(dev, "debugfs error input < %d\n", irq_first);
+ return -EINVAL;
+ }
+ if (user_val > irq_last) {
+ dev_err(dev, "debugfs error input > %d\n", irq_last);
+ return -EINVAL;
+ }
+
+ irq_index = user_val - irq_first;
+ if (irq_index >= num_irqs)
+ return -EINVAL;
+
+ /*
+ * This will create a sysfs file named <irq-nr> which userspace can
+ * use to select or poll and get the AB8500 events
+ */
+ dev_attr[irq_index] = kmalloc(sizeof(struct device_attribute),
+ GFP_KERNEL);
+ if (!dev_attr[irq_index])
+ return -ENOMEM;
+
+ event_name[irq_index] = kmalloc(count, GFP_KERNEL);
+ if (!event_name[irq_index])
+ return -ENOMEM;
+
+ sprintf(event_name[irq_index], "%lu", user_val);
+ dev_attr[irq_index]->show = show_irq;
+ dev_attr[irq_index]->store = NULL;
+ dev_attr[irq_index]->attr.name = event_name[irq_index];
+ dev_attr[irq_index]->attr.mode = S_IRUGO;
+ err = sysfs_create_file(&dev->kobj, &dev_attr[irq_index]->attr);
+ if (err < 0) {
+ printk(KERN_ERR "sysfs_create_file failed %d\n", err);
+ return err;
+ }
+
+ err = request_threaded_irq(user_val, NULL, ab8500_debug_handler,
+ IRQF_SHARED | IRQF_NO_SUSPEND,
+ "ab8500-debug", &dev->kobj);
+ if (err < 0) {
+ printk(KERN_ERR "request_threaded_irq failed %d, %lu\n",
+ err, user_val);
+ sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr);
+ return err;
+ }
+
+ return count;
+}
+
+static ssize_t ab8500_unsubscribe_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ unsigned long user_val;
+ int err;
+ unsigned int irq_index;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &user_val);
+ if (err)
+ return err;
+
+ if (user_val < irq_first) {
+ dev_err(dev, "debugfs error input < %d\n", irq_first);
+ return -EINVAL;
+ }
+ if (user_val > irq_last) {
+ dev_err(dev, "debugfs error input > %d\n", irq_last);
+ return -EINVAL;
+ }
+
+ irq_index = user_val - irq_first;
+ if (irq_index >= num_irqs)
+ return -EINVAL;
+
+ /* Set irq count to 0 when unsubscribe */
+ irq_count[irq_index] = 0;
+
+ if (dev_attr[irq_index])
+ sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr);
+
+
+ free_irq(user_val, &dev->kobj);
+ kfree(event_name[irq_index]);
+ kfree(dev_attr[irq_index]);
+
+ return count;
+}
+
+/*
+ * - several deubgfs nodes fops
+ */
+
static const struct file_operations ab8500_bank_fops = {
.open = ab8500_bank_open,
.write = ab8500_bank_write,
@@ -546,64 +2903,305 @@ static const struct file_operations ab8500_val_fops = {
.owner = THIS_MODULE,
};
+static const struct file_operations ab8500_interrupts_fops = {
+ .open = ab8500_interrupts_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_subscribe_fops = {
+ .open = ab8500_subscribe_unsubscribe_open,
+ .write = ab8500_subscribe_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_unsubscribe_fops = {
+ .open = ab8500_subscribe_unsubscribe_open,
+ .write = ab8500_unsubscribe_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_hwreg_fops = {
+ .open = ab8500_hwreg_open,
+ .write = ab8500_hwreg_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
static struct dentry *ab8500_dir;
-static struct dentry *ab8500_reg_file;
-static struct dentry *ab8500_bank_file;
-static struct dentry *ab8500_address_file;
-static struct dentry *ab8500_val_file;
+static struct dentry *ab8500_gpadc_dir;
-static int __devinit ab8500_debug_probe(struct platform_device *plf)
+static int ab8500_debug_probe(struct platform_device *plf)
{
+ struct dentry *file;
+ struct ab8500 *ab8500;
+ struct resource *res;
debug_bank = AB8500_MISC;
debug_address = AB8500_REV_REG & 0x00FF;
+ ab8500 = dev_get_drvdata(plf->dev.parent);
+ num_irqs = ab8500->mask_size;
+
+ irq_count = devm_kzalloc(&plf->dev,
+ sizeof(*irq_count)*num_irqs, GFP_KERNEL);
+ if (!irq_count)
+ return -ENOMEM;
+
+ dev_attr = devm_kzalloc(&plf->dev,
+ sizeof(*dev_attr)*num_irqs,GFP_KERNEL);
+ if (!dev_attr)
+ return -ENOMEM;
+
+ event_name = devm_kzalloc(&plf->dev,
+ sizeof(*event_name)*num_irqs, GFP_KERNEL);
+ if (!event_name)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(plf, 0, "IRQ_AB8500");
+ if (!res) {
+ dev_err(&plf->dev, "AB8500 irq not found, err %d\n",
+ irq_first);
+ return ENXIO;
+ }
+ irq_ab8500 = res->start;
+
+ irq_first = platform_get_irq_byname(plf, "IRQ_FIRST");
+ if (irq_first < 0) {
+ dev_err(&plf->dev, "First irq not found, err %d\n",
+ irq_first);
+ return irq_first;
+ }
+
+ irq_last = platform_get_irq_byname(plf, "IRQ_LAST");
+ if (irq_last < 0) {
+ dev_err(&plf->dev, "Last irq not found, err %d\n",
+ irq_last);
+ return irq_last;
+ }
+
ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL);
if (!ab8500_dir)
- goto exit_no_debugfs;
+ goto err;
+
+ ab8500_gpadc_dir = debugfs_create_dir(AB8500_ADC_NAME_STRING,
+ ab8500_dir);
+ if (!ab8500_gpadc_dir)
+ goto err;
+
+ file = debugfs_create_file("all-bank-registers", S_IRUGO,
+ ab8500_dir, &plf->dev, &ab8500_registers_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("all-banks", S_IRUGO,
+ ab8500_dir, &plf->dev, &ab8500_all_banks_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("register-bank", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_dir, &plf->dev, &ab8500_bank_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("register-address", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_dir, &plf->dev, &ab8500_address_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("register-value", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_dir, &plf->dev, &ab8500_val_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("irq-subscribe", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_dir, &plf->dev, &ab8500_subscribe_fops);
+ if (!file)
+ goto err;
- ab8500_reg_file = debugfs_create_file("all-bank-registers",
- S_IRUGO, ab8500_dir, &plf->dev, &ab8500_registers_fops);
- if (!ab8500_reg_file)
- goto exit_destroy_dir;
+ if (is_ab8500(ab8500)) {
+ debug_ranges = ab8500_debug_ranges;
+ num_interrupt_lines = AB8500_NR_IRQS;
+ } else if (is_ab8505(ab8500)) {
+ debug_ranges = ab8505_debug_ranges;
+ num_interrupt_lines = AB8505_NR_IRQS;
+ } else if (is_ab9540(ab8500)) {
+ debug_ranges = ab8505_debug_ranges;
+ num_interrupt_lines = AB9540_NR_IRQS;
+ } else if (is_ab8540(ab8500)) {
+ debug_ranges = ab8540_debug_ranges;
+ num_interrupt_lines = AB8540_NR_IRQS;
+ }
+
+ file = debugfs_create_file("interrupts", (S_IRUGO),
+ ab8500_dir, &plf->dev, &ab8500_interrupts_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("irq-unsubscribe", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_dir, &plf->dev, &ab8500_unsubscribe_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("hwreg", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_dir, &plf->dev, &ab8500_hwreg_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("all-modem-registers", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_dir, &plf->dev, &ab8500_modem_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("bat_ctrl", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bat_ctrl_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("btemp_ball", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_btemp_ball_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("main_charger_v", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_v_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("acc_detect1", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect1_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("acc_detect2", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect2_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("adc_aux1", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux1_fops);
+ if (!file)
+ goto err;
- ab8500_bank_file = debugfs_create_file("register-bank",
- (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_bank_fops);
- if (!ab8500_bank_file)
- goto exit_destroy_reg;
+ file = debugfs_create_file("adc_aux2", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux2_fops);
+ if (!file)
+ goto err;
- ab8500_address_file = debugfs_create_file("register-address",
- (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev,
- &ab8500_address_fops);
- if (!ab8500_address_file)
- goto exit_destroy_bank;
+ file = debugfs_create_file("main_bat_v", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_bat_v_fops);
+ if (!file)
+ goto err;
- ab8500_val_file = debugfs_create_file("register-value",
- (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_val_fops);
- if (!ab8500_val_file)
- goto exit_destroy_address;
+ file = debugfs_create_file("vbus_v", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_vbus_v_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("main_charger_c", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_c_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("usb_charger_c", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_usb_charger_c_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("bk_bat_v", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bk_bat_v_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("die_temp", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_die_temp_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("usb_id", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_usb_id_fops);
+ if (!file)
+ goto err;
+
+ if (is_ab8540(ab8500)) {
+ file = debugfs_create_file("xtal_temp", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8540_gpadc_xtal_temp_fops);
+ if (!file)
+ goto err;
+ file = debugfs_create_file("vbattruemeas", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev,
+ &ab8540_gpadc_vbat_true_meas_fops);
+ if (!file)
+ goto err;
+ file = debugfs_create_file("batctrl_and_ibat",
+ (S_IRUGO | S_IWUGO), ab8500_gpadc_dir,
+ &plf->dev, &ab8540_gpadc_bat_ctrl_and_ibat_fops);
+ if (!file)
+ goto err;
+ file = debugfs_create_file("vbatmeas_and_ibat",
+ (S_IRUGO | S_IWUGO), ab8500_gpadc_dir,
+ &plf->dev,
+ &ab8540_gpadc_vbat_meas_and_ibat_fops);
+ if (!file)
+ goto err;
+ file = debugfs_create_file("vbattruemeas_and_ibat",
+ (S_IRUGO | S_IWUGO), ab8500_gpadc_dir,
+ &plf->dev,
+ &ab8540_gpadc_vbat_true_meas_and_ibat_fops);
+ if (!file)
+ goto err;
+ file = debugfs_create_file("battemp_and_ibat",
+ (S_IRUGO | S_IWUGO), ab8500_gpadc_dir,
+ &plf->dev, &ab8540_gpadc_bat_temp_and_ibat_fops);
+ if (!file)
+ goto err;
+ file = debugfs_create_file("otp_calib", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8540_gpadc_otp_calib_fops);
+ if (!file)
+ goto err;
+ }
+ file = debugfs_create_file("avg_sample", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_avg_sample_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("trig_edge", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_trig_edge_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("trig_timer", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_trig_timer_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("conv_type", (S_IRUGO | S_IWUSR | S_IWGRP),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_conv_type_fops);
+ if (!file)
+ goto err;
return 0;
-exit_destroy_address:
- debugfs_remove(ab8500_address_file);
-exit_destroy_bank:
- debugfs_remove(ab8500_bank_file);
-exit_destroy_reg:
- debugfs_remove(ab8500_reg_file);
-exit_destroy_dir:
- debugfs_remove(ab8500_dir);
-exit_no_debugfs:
+err:
+ if (ab8500_dir)
+ debugfs_remove_recursive(ab8500_dir);
dev_err(&plf->dev, "failed to create debugfs entries.\n");
+
return -ENOMEM;
}
-static int __devexit ab8500_debug_remove(struct platform_device *plf)
+static int ab8500_debug_remove(struct platform_device *plf)
{
- debugfs_remove(ab8500_val_file);
- debugfs_remove(ab8500_address_file);
- debugfs_remove(ab8500_bank_file);
- debugfs_remove(ab8500_reg_file);
- debugfs_remove(ab8500_dir);
+ debugfs_remove_recursive(ab8500_dir);
return 0;
}
@@ -614,7 +3212,7 @@ static struct platform_driver ab8500_debug_driver = {
.owner = THIS_MODULE,
},
.probe = ab8500_debug_probe,
- .remove = __devexit_p(ab8500_debug_remove)
+ .remove = ab8500_debug_remove
};
static int __init ab8500_debug_init(void)
diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
index 29d72a259c8..36000f92098 100644
--- a/drivers/mfd/ab8500-gpadc.c
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -12,6 +12,7 @@
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
+#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
#include <linux/completion.h>
#include <linux/regulator/consumer.h>
@@ -36,6 +37,13 @@
#define AB8500_GPADC_AUTODATAL_REG 0x07
#define AB8500_GPADC_AUTODATAH_REG 0x08
#define AB8500_GPADC_MUX_CTRL_REG 0x09
+#define AB8540_GPADC_MANDATA2L_REG 0x09
+#define AB8540_GPADC_MANDATA2H_REG 0x0A
+#define AB8540_GPADC_APEAAX_REG 0x10
+#define AB8540_GPADC_APEAAT_REG 0x11
+#define AB8540_GPADC_APEAAM_REG 0x12
+#define AB8540_GPADC_APEAAH_REG 0x13
+#define AB8540_GPADC_APEAAL_REG 0x14
/*
* OTP register offsets
@@ -48,19 +56,29 @@
#define AB8500_GPADC_CAL_5 0x13
#define AB8500_GPADC_CAL_6 0x14
#define AB8500_GPADC_CAL_7 0x15
+/* New calibration for 8540 */
+#define AB8540_GPADC_OTP4_REG_7 0x38
+#define AB8540_GPADC_OTP4_REG_6 0x39
+#define AB8540_GPADC_OTP4_REG_5 0x3A
/* gpadc constants */
#define EN_VINTCORE12 0x04
#define EN_VTVOUT 0x02
#define EN_GPADC 0x01
#define DIS_GPADC 0x00
-#define SW_AVG_16 0x60
+#define AVG_1 0x00
+#define AVG_4 0x20
+#define AVG_8 0x40
+#define AVG_16 0x60
#define ADC_SW_CONV 0x04
#define EN_ICHAR 0x80
#define BTEMP_PULL_UP 0x08
#define EN_BUF 0x40
#define DIS_ZERO 0x00
#define GPADC_BUSY 0x01
+#define EN_FALLING 0x10
+#define EN_TRIG_EDGE 0x02
+#define EN_VBIAS_XTAL_TEMP 0x02
/* GPADC constants from AB8500 spec, UM0836 */
#define ADC_RESOLUTION 1024
@@ -79,13 +97,32 @@
#define ADC_CH_BKBAT_MIN 0
#define ADC_CH_BKBAT_MAX 3200
+/* GPADC constants from AB8540 spec */
+#define ADC_CH_IBAT_MIN (-6000) /* mA range measured by ADC for ibat*/
+#define ADC_CH_IBAT_MAX 6000
+#define ADC_CH_IBAT_MIN_V (-60) /* mV range measured by ADC for ibat*/
+#define ADC_CH_IBAT_MAX_V 60
+#define IBAT_VDROP_L (-56) /* mV */
+#define IBAT_VDROP_H 56
+
/* This is used to not lose precision when dividing to get gain and offset */
-#define CALIB_SCALE 1000
+#define CALIB_SCALE 1000
+/*
+ * Number of bits shift used to not lose precision
+ * when dividing to get ibat gain.
+ */
+#define CALIB_SHIFT_IBAT 20
+
+/* Time in ms before disabling regulator */
+#define GPADC_AUDOSUSPEND_DELAY 1
+
+#define CONVERSION_TIME 500 /* ms */
enum cal_channels {
ADC_INPUT_VMAIN = 0,
ADC_INPUT_BTEMP,
ADC_INPUT_VBAT,
+ ADC_INPUT_IBAT,
NBR_CAL_INPUTS,
};
@@ -96,31 +133,37 @@ enum cal_channels {
* @offset: Offset of the ADC channel
*/
struct adc_cal_data {
- u64 gain;
- u64 offset;
+ s64 gain;
+ s64 offset;
+ u16 otp_calib_hi;
+ u16 otp_calib_lo;
};
/**
* struct ab8500_gpadc - AB8500 GPADC device information
- * @chip_id ABB chip id
* @dev: pointer to the struct device
* @node: a list of AB8500 GPADCs, hence prepared for
reentrance
+ * @parent: pointer to the struct ab8500
* @ab8500_gpadc_complete: pointer to the struct completion, to indicate
* the completion of gpadc conversion
* @ab8500_gpadc_lock: structure of type mutex
* @regu: pointer to the struct regulator
- * @irq: interrupt number that is used by gpadc
+ * @irq_sw: interrupt number that is used by gpadc for Sw
+ * conversion
+ * @irq_hw: interrupt number that is used by gpadc for Hw
+ * conversion
* @cal_data array of ADC calibration data structs
*/
struct ab8500_gpadc {
- u8 chip_id;
struct device *dev;
struct list_head node;
+ struct ab8500 *parent;
struct completion ab8500_gpadc_complete;
struct mutex ab8500_gpadc_lock;
struct regulator *regu;
- int irq;
+ int irq_sw;
+ int irq_hw;
struct adc_cal_data cal_data[NBR_CAL_INPUTS];
};
@@ -165,6 +208,7 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
gpadc->cal_data[ADC_INPUT_VMAIN].offset) / CALIB_SCALE;
break;
+ case XTAL_TEMP:
case BAT_CTRL:
case BTEMP_BALL:
case ACC_DETECT1:
@@ -183,6 +227,7 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
break;
case MAIN_BAT_V:
+ case VBAT_TRUE_MEAS:
/* For some reason we don't have calibrated data */
if (!gpadc->cal_data[ADC_INPUT_VBAT].gain) {
res = ADC_CH_VBAT_MIN + (ADC_CH_VBAT_MAX -
@@ -226,6 +271,20 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
ADC_RESOLUTION;
break;
+ case IBAT_VIRTUAL_CHANNEL:
+ /* For some reason we don't have calibrated data */
+ if (!gpadc->cal_data[ADC_INPUT_IBAT].gain) {
+ res = ADC_CH_IBAT_MIN + (ADC_CH_IBAT_MAX -
+ ADC_CH_IBAT_MIN) * ad_value /
+ ADC_RESOLUTION;
+ break;
+ }
+ /* Here we can use the calibrated data */
+ res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_IBAT].gain +
+ gpadc->cal_data[ADC_INPUT_IBAT].offset)
+ >> CALIB_SHIFT_IBAT;
+ break;
+
default:
dev_err(gpadc->dev,
"unknown channel, not possible to convert\n");
@@ -238,52 +297,88 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
EXPORT_SYMBOL(ab8500_gpadc_ad_to_voltage);
/**
- * ab8500_gpadc_convert() - gpadc conversion
+ * ab8500_gpadc_sw_hw_convert() - gpadc conversion
* @channel: analog channel to be converted to digital data
+ * @avg_sample: number of ADC sample to average
+ * @trig_egde: selected ADC trig edge
+ * @trig_timer: selected ADC trigger delay timer
+ * @conv_type: selected conversion type (HW or SW conversion)
*
* This function converts the selected analog i/p to digital
* data.
*/
-int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel)
+int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel,
+ u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type)
{
int ad_value;
int voltage;
- ad_value = ab8500_gpadc_read_raw(gpadc, channel);
- if (ad_value < 0) {
- dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n", channel);
+ ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample,
+ trig_edge, trig_timer, conv_type);
+/* On failure retry a second time */
+ if (ad_value < 0)
+ ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample,
+ trig_edge, trig_timer, conv_type);
+if (ad_value < 0) {
+ dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n",
+ channel);
return ad_value;
}
voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value);
-
if (voltage < 0)
dev_err(gpadc->dev, "GPADC to voltage conversion failed ch:"
" %d AD: 0x%x\n", channel, ad_value);
return voltage;
}
-EXPORT_SYMBOL(ab8500_gpadc_convert);
+EXPORT_SYMBOL(ab8500_gpadc_sw_hw_convert);
/**
* ab8500_gpadc_read_raw() - gpadc read
* @channel: analog channel to be read
+ * @avg_sample: number of ADC sample to average
+ * @trig_edge: selected trig edge
+ * @trig_timer: selected ADC trigger delay timer
+ * @conv_type: selected conversion type (HW or SW conversion)
*
- * This function obtains the raw ADC value, this then needs
- * to be converted by calling ab8500_gpadc_ad_to_voltage()
+ * This function obtains the raw ADC value for an hardware conversion,
+ * this then needs to be converted by calling ab8500_gpadc_ad_to_voltage()
*/
-int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
+int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
+ u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type)
+{
+ int raw_data;
+ raw_data = ab8500_gpadc_double_read_raw(gpadc, channel,
+ avg_sample, trig_edge, trig_timer, conv_type, NULL);
+ return raw_data;
+}
+
+int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
+ u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type,
+ int *ibat)
{
int ret;
int looplimit = 0;
- u8 val, low_data, high_data;
+ unsigned long completion_timeout;
+ u8 val, low_data, high_data, low_data2, high_data2;
+ u8 val_reg1 = 0;
+ unsigned int delay_min = 0;
+ unsigned int delay_max = 0;
+ u8 data_low_addr, data_high_addr;
if (!gpadc)
return -ENODEV;
+ /* check if convertion is supported */
+ if ((gpadc->irq_sw < 0) && (conv_type == ADC_SW))
+ return -ENOTSUPP;
+ if ((gpadc->irq_hw < 0) && (conv_type == ADC_HW))
+ return -ENOTSUPP;
+
mutex_lock(&gpadc->ab8500_gpadc_lock);
/* Enable VTVout LDO this is required for GPADC */
- regulator_enable(gpadc->regu);
+ pm_runtime_get_sync(gpadc->dev);
/* Check if ADC is not busy, lock and proceed */
do {
@@ -302,16 +397,34 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
}
/* Enable GPADC */
- ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
- AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_GPADC, EN_GPADC);
- if (ret < 0) {
- dev_err(gpadc->dev, "gpadc_conversion: enable gpadc failed\n");
- goto out;
+ val_reg1 |= EN_GPADC;
+
+ /* Select the channel source and set average samples */
+ switch (avg_sample) {
+ case SAMPLE_1:
+ val = channel | AVG_1;
+ break;
+ case SAMPLE_4:
+ val = channel | AVG_4;
+ break;
+ case SAMPLE_8:
+ val = channel | AVG_8;
+ break;
+ default:
+ val = channel | AVG_16;
+ break;
}
- /* Select the channel source and set average samples to 16 */
- ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
- AB8500_GPADC_CTRL2_REG, (channel | SW_AVG_16));
+ if (conv_type == ADC_HW) {
+ ret = abx500_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL3_REG, val);
+ val_reg1 |= EN_TRIG_EDGE;
+ if (trig_edge)
+ val_reg1 |= EN_FALLING;
+ }
+ else
+ ret = abx500_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL2_REG, val);
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: set avg samples failed\n");
@@ -326,70 +439,129 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
switch (channel) {
case MAIN_CHARGER_C:
case USB_CHARGER_C:
- ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
- AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
- EN_BUF | EN_ICHAR,
- EN_BUF | EN_ICHAR);
+ val_reg1 |= EN_BUF | EN_ICHAR;
break;
case BTEMP_BALL:
- if (gpadc->chip_id >= AB8500_CUT3P0) {
- /* Turn on btemp pull-up on ABB 3.0 */
- ret = abx500_mask_and_set_register_interruptible(
- gpadc->dev,
- AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
- EN_BUF | BTEMP_PULL_UP,
- EN_BUF | BTEMP_PULL_UP);
-
- /*
- * Delay might be needed for ABB8500 cut 3.0, if not, remove
- * when hardware will be available
- */
- msleep(1);
+ if (!is_ab8500_2p0_or_earlier(gpadc->parent)) {
+ val_reg1 |= EN_BUF | BTEMP_PULL_UP;
+ /*
+ * Delay might be needed for ABB8500 cut 3.0, if not,
+ * remove when hardware will be availible
+ */
+ delay_min = 1000; /* Delay in micro seconds */
+ delay_max = 10000; /* large range to optimise sleep mode */
break;
}
/* Intentional fallthrough */
default:
- ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
- AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF);
+ val_reg1 |= EN_BUF;
break;
}
+
+ /* Write configuration to register */
+ ret = abx500_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL1_REG, val_reg1);
if (ret < 0) {
dev_err(gpadc->dev,
- "gpadc_conversion: select falling edge failed\n");
+ "gpadc_conversion: set Control register failed\n");
goto out;
}
- ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
- AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ADC_SW_CONV, ADC_SW_CONV);
- if (ret < 0) {
- dev_err(gpadc->dev,
- "gpadc_conversion: start s/w conversion failed\n");
- goto out;
+ if (delay_min != 0)
+ usleep_range(delay_min, delay_max);
+
+ if (conv_type == ADC_HW) {
+ /* Set trigger delay timer */
+ ret = abx500_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_AUTO_TIMER_REG, trig_timer);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: trig timer failed\n");
+ goto out;
+ }
+ completion_timeout = 2 * HZ;
+ data_low_addr = AB8500_GPADC_AUTODATAL_REG;
+ data_high_addr = AB8500_GPADC_AUTODATAH_REG;
+ } else {
+ /* Start SW conversion */
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
+ ADC_SW_CONV, ADC_SW_CONV);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: start s/w conv failed\n");
+ goto out;
+ }
+ completion_timeout = msecs_to_jiffies(CONVERSION_TIME);
+ data_low_addr = AB8500_GPADC_MANDATAL_REG;
+ data_high_addr = AB8500_GPADC_MANDATAH_REG;
}
+
/* wait for completion of conversion */
- if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, 2*HZ)) {
+ if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete,
+ completion_timeout)) {
dev_err(gpadc->dev,
- "timeout: didn't receive GPADC conversion interrupt\n");
+ "timeout didn't receive GPADC conv interrupt\n");
ret = -EINVAL;
goto out;
}
/* Read the converted RAW data */
- ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC,
- AB8500_GPADC_MANDATAL_REG, &low_data);
+ ret = abx500_get_register_interruptible(gpadc->dev,
+ AB8500_GPADC, data_low_addr, &low_data);
if (ret < 0) {
dev_err(gpadc->dev, "gpadc_conversion: read low data failed\n");
goto out;
}
- ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC,
- AB8500_GPADC_MANDATAH_REG, &high_data);
+ ret = abx500_get_register_interruptible(gpadc->dev,
+ AB8500_GPADC, data_high_addr, &high_data);
if (ret < 0) {
- dev_err(gpadc->dev,
- "gpadc_conversion: read high data failed\n");
+ dev_err(gpadc->dev, "gpadc_conversion: read high data failed\n");
goto out;
}
+ /* Check if double convertion is required */
+ if ((channel == BAT_CTRL_AND_IBAT) ||
+ (channel == VBAT_MEAS_AND_IBAT) ||
+ (channel == VBAT_TRUE_MEAS_AND_IBAT) ||
+ (channel == BAT_TEMP_AND_IBAT)) {
+
+ if (conv_type == ADC_HW) {
+ /* not supported */
+ ret = -ENOTSUPP;
+ dev_err(gpadc->dev,
+ "gpadc_conversion: only SW double conversion supported\n");
+ goto out;
+ } else {
+ /* Read the converted RAW data 2 */
+ ret = abx500_get_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8540_GPADC_MANDATA2L_REG,
+ &low_data2);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: read sw low data 2 failed\n");
+ goto out;
+ }
+
+ ret = abx500_get_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8540_GPADC_MANDATA2H_REG,
+ &high_data2);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: read sw high data 2 failed\n");
+ goto out;
+ }
+ if (ibat != NULL) {
+ *ibat = (high_data2 << 8) | low_data2;
+ } else {
+ dev_warn(gpadc->dev,
+ "gpadc_conversion: ibat not stored\n");
+ }
+
+ }
+ }
+
/* Disable GPADC */
ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
@@ -397,8 +569,11 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n");
goto out;
}
+
/* Disable VTVout LDO this is required for GPADC */
- regulator_disable(gpadc->regu);
+ pm_runtime_mark_last_busy(gpadc->dev);
+ pm_runtime_put_autosuspend(gpadc->dev);
+
mutex_unlock(&gpadc->ab8500_gpadc_lock);
return (high_data << 8) | low_data;
@@ -412,7 +587,7 @@ out:
*/
(void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
- regulator_disable(gpadc->regu);
+ pm_runtime_put(gpadc->dev);
mutex_unlock(&gpadc->ab8500_gpadc_lock);
dev_err(gpadc->dev,
"gpadc_conversion: Failed to AD convert channel %d\n", channel);
@@ -421,16 +596,16 @@ out:
EXPORT_SYMBOL(ab8500_gpadc_read_raw);
/**
- * ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion
+ * ab8500_bm_gpadcconvend_handler() - isr for gpadc conversion completion
* @irq: irq number
* @data: pointer to the data passed during request irq
*
- * This is a interrupt service routine for s/w gpadc conversion completion.
+ * This is a interrupt service routine for gpadc conversion completion.
* Notifies the gpadc completion is completed and the converted raw value
* can be read from the registers.
* Returns IRQ status(IRQ_HANDLED)
*/
-static irqreturn_t ab8500_bm_gpswadcconvend_handler(int irq, void *_gpadc)
+static irqreturn_t ab8500_bm_gpadcconvend_handler(int irq, void *_gpadc)
{
struct ab8500_gpadc *gpadc = _gpadc;
@@ -449,15 +624,27 @@ static int otp_cal_regs[] = {
AB8500_GPADC_CAL_7,
};
+static int otp4_cal_regs[] = {
+ AB8540_GPADC_OTP4_REG_7,
+ AB8540_GPADC_OTP4_REG_6,
+ AB8540_GPADC_OTP4_REG_5,
+};
+
static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
{
int i;
int ret[ARRAY_SIZE(otp_cal_regs)];
u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)];
-
+ int ret_otp4[ARRAY_SIZE(otp4_cal_regs)];
+ u8 gpadc_otp4[ARRAY_SIZE(otp4_cal_regs)];
int vmain_high, vmain_low;
int btemp_high, btemp_low;
int vbat_high, vbat_low;
+ int ibat_high, ibat_low;
+ s64 V_gain, V_offset, V2A_gain, V2A_offset;
+ struct ab8500 *ab8500;
+
+ ab8500 = gpadc->parent;
/* First we read all OTP registers and store the error code */
for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) {
@@ -477,7 +664,7 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
* bt_h/l = btemp_high/low
* vb_h/l = vbat_high/low
*
- * Data bits:
+ * Data bits 8500/9540:
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
* |.......|.......|.......|.......|.......|.......|.......|.......
* | | vm_h9 | vm_h8
@@ -495,6 +682,35 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
* | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 |
* |.......|.......|.......|.......|.......|.......|.......|.......
*
+ * Data bits 8540:
+ * OTP2
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * |
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vm_h9 | vm_h8 | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 |
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ *
+ * Data bits 8540:
+ * OTP4
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | | ib_h9 | ib_h8 | ib_h7
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | ib_h6 | ib_h5 | ib_h4 | ib_h3 | ib_h2 | ib_h1 | ib_h0 | ib_l5
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | ib_l4 | ib_l3 | ib_l2 | ib_l1 | ib_l0 |
+ *
*
* Ideal output ADC codes corresponding to injected input voltages
* during manufacturing is:
@@ -507,38 +723,116 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
* vbat_low: Vin = 2380mV / ADC ideal code = 33
*/
- /* Calculate gain and offset for VMAIN if all reads succeeded */
- if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) {
- vmain_high = (((gpadc_cal[0] & 0x03) << 8) |
- ((gpadc_cal[1] & 0x3F) << 2) |
- ((gpadc_cal[2] & 0xC0) >> 6));
+ if (is_ab8540(ab8500)) {
+ /* Calculate gain and offset for VMAIN if all reads succeeded*/
+ if (!(ret[1] < 0 || ret[2] < 0)) {
+ vmain_high = (((gpadc_cal[1] & 0xFF) << 2) |
+ ((gpadc_cal[2] & 0xC0) >> 6));
+ vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
+
+ gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi =
+ (u16)vmain_high;
+ gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo =
+ (u16)vmain_low;
+
+ gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
+ (19500 - 315) / (vmain_high - vmain_low);
+ gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE *
+ 19500 - (CALIB_SCALE * (19500 - 315) /
+ (vmain_high - vmain_low)) * vmain_high;
+ } else {
+ gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0;
+ }
- vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
+ /* Read IBAT calibration Data */
+ for (i = 0; i < ARRAY_SIZE(otp4_cal_regs); i++) {
+ ret_otp4[i] = abx500_get_register_interruptible(
+ gpadc->dev, AB8500_OTP_EMUL,
+ otp4_cal_regs[i], &gpadc_otp4[i]);
+ if (ret_otp4[i] < 0)
+ dev_err(gpadc->dev,
+ "%s: read otp4 reg 0x%02x failed\n",
+ __func__, otp4_cal_regs[i]);
+ }
- gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
- (19500 - 315) / (vmain_high - vmain_low);
+ /* Calculate gain and offset for IBAT if all reads succeeded */
+ if (!(ret_otp4[0] < 0 || ret_otp4[1] < 0 || ret_otp4[2] < 0)) {
+ ibat_high = (((gpadc_otp4[0] & 0x07) << 7) |
+ ((gpadc_otp4[1] & 0xFE) >> 1));
+ ibat_low = (((gpadc_otp4[1] & 0x01) << 5) |
+ ((gpadc_otp4[2] & 0xF8) >> 3));
+
+ gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi =
+ (u16)ibat_high;
+ gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo =
+ (u16)ibat_low;
+
+ V_gain = ((IBAT_VDROP_H - IBAT_VDROP_L)
+ << CALIB_SHIFT_IBAT) / (ibat_high - ibat_low);
+
+ V_offset = (IBAT_VDROP_H << CALIB_SHIFT_IBAT) -
+ (((IBAT_VDROP_H - IBAT_VDROP_L) <<
+ CALIB_SHIFT_IBAT) / (ibat_high - ibat_low))
+ * ibat_high;
+ /*
+ * Result obtained is in mV (at a scale factor),
+ * we need to calculate gain and offset to get mA
+ */
+ V2A_gain = (ADC_CH_IBAT_MAX - ADC_CH_IBAT_MIN)/
+ (ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V);
+ V2A_offset = ((ADC_CH_IBAT_MAX_V * ADC_CH_IBAT_MIN -
+ ADC_CH_IBAT_MAX * ADC_CH_IBAT_MIN_V)
+ << CALIB_SHIFT_IBAT)
+ / (ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V);
+
+ gpadc->cal_data[ADC_INPUT_IBAT].gain = V_gain * V2A_gain;
+ gpadc->cal_data[ADC_INPUT_IBAT].offset = V_offset *
+ V2A_gain + V2A_offset;
+ } else {
+ gpadc->cal_data[ADC_INPUT_IBAT].gain = 0;
+ }
- gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * 19500 -
- (CALIB_SCALE * (19500 - 315) /
- (vmain_high - vmain_low)) * vmain_high;
+ dev_dbg(gpadc->dev, "IBAT gain %llu offset %llu\n",
+ gpadc->cal_data[ADC_INPUT_IBAT].gain,
+ gpadc->cal_data[ADC_INPUT_IBAT].offset);
} else {
- gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0;
+ /* Calculate gain and offset for VMAIN if all reads succeeded */
+ if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) {
+ vmain_high = (((gpadc_cal[0] & 0x03) << 8) |
+ ((gpadc_cal[1] & 0x3F) << 2) |
+ ((gpadc_cal[2] & 0xC0) >> 6));
+ vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
+
+ gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi =
+ (u16)vmain_high;
+ gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo =
+ (u16)vmain_low;
+
+ gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
+ (19500 - 315) / (vmain_high - vmain_low);
+
+ gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE *
+ 19500 - (CALIB_SCALE * (19500 - 315) /
+ (vmain_high - vmain_low)) * vmain_high;
+ } else {
+ gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0;
+ }
}
/* Calculate gain and offset for BTEMP if all reads succeeded */
if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) {
btemp_high = (((gpadc_cal[2] & 0x01) << 9) |
- (gpadc_cal[3] << 1) |
- ((gpadc_cal[4] & 0x80) >> 7));
-
+ (gpadc_cal[3] << 1) | ((gpadc_cal[4] & 0x80) >> 7));
btemp_low = ((gpadc_cal[4] & 0x7C) >> 2);
+ gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi = (u16)btemp_high;
+ gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo = (u16)btemp_low;
+
gpadc->cal_data[ADC_INPUT_BTEMP].gain =
CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low);
-
gpadc->cal_data[ADC_INPUT_BTEMP].offset = CALIB_SCALE * 1300 -
- (CALIB_SCALE * (1300 - 21) /
- (btemp_high - btemp_low)) * btemp_high;
+ (CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low))
+ * btemp_high;
} else {
gpadc->cal_data[ADC_INPUT_BTEMP].gain = 0;
}
@@ -548,9 +842,11 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]);
vbat_low = ((gpadc_cal[6] & 0xFC) >> 2);
+ gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi = (u16)vbat_high;
+ gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo = (u16)vbat_low;
+
gpadc->cal_data[ADC_INPUT_VBAT].gain = CALIB_SCALE *
(4700 - 2380) / (vbat_high - vbat_low);
-
gpadc->cal_data[ADC_INPUT_VBAT].offset = CALIB_SCALE * 4700 -
(CALIB_SCALE * (4700 - 2380) /
(vbat_high - vbat_low)) * vbat_high;
@@ -571,90 +867,185 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
gpadc->cal_data[ADC_INPUT_VBAT].offset);
}
-static int __devinit ab8500_gpadc_probe(struct platform_device *pdev)
+#ifdef CONFIG_PM_RUNTIME
+static int ab8500_gpadc_runtime_suspend(struct device *dev)
+{
+ struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+
+ regulator_disable(gpadc->regu);
+ return 0;
+}
+
+static int ab8500_gpadc_runtime_resume(struct device *dev)
+{
+ struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_enable(gpadc->regu);
+ if (ret)
+ dev_err(dev, "Failed to enable vtvout LDO: %d\n", ret);
+ return ret;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int ab8500_gpadc_suspend(struct device *dev)
+{
+ struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+
+ mutex_lock(&gpadc->ab8500_gpadc_lock);
+
+ pm_runtime_get_sync(dev);
+
+ regulator_disable(gpadc->regu);
+ return 0;
+}
+
+static int ab8500_gpadc_resume(struct device *dev)
+{
+ struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_enable(gpadc->regu);
+ if (ret)
+ dev_err(dev, "Failed to enable vtvout LDO: %d\n", ret);
+
+ pm_runtime_mark_last_busy(gpadc->dev);
+ pm_runtime_put_autosuspend(gpadc->dev);
+
+ mutex_unlock(&gpadc->ab8500_gpadc_lock);
+ return ret;
+}
+#endif
+
+static int ab8500_gpadc_probe(struct platform_device *pdev)
{
int ret = 0;
struct ab8500_gpadc *gpadc;
- gpadc = kzalloc(sizeof(struct ab8500_gpadc), GFP_KERNEL);
+ gpadc = devm_kzalloc(&pdev->dev, sizeof(struct ab8500_gpadc), GFP_KERNEL);
if (!gpadc) {
dev_err(&pdev->dev, "Error: No memory\n");
return -ENOMEM;
}
- gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END");
- if (gpadc->irq < 0) {
- dev_err(&pdev->dev, "failed to get platform irq-%d\n",
- gpadc->irq);
- ret = gpadc->irq;
- goto fail;
- }
+ gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END");
+ if (gpadc->irq_sw < 0)
+ dev_err(gpadc->dev, "failed to get platform sw_conv_end irq\n");
+
+ gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
+ if (gpadc->irq_hw < 0)
+ dev_err(gpadc->dev, "failed to get platform hw_conv_end irq\n");
gpadc->dev = &pdev->dev;
+ gpadc->parent = dev_get_drvdata(pdev->dev.parent);
mutex_init(&gpadc->ab8500_gpadc_lock);
/* Initialize completion used to notify completion of conversion */
init_completion(&gpadc->ab8500_gpadc_complete);
- /* Register interrupt - SwAdcComplete */
- ret = request_threaded_irq(gpadc->irq, NULL,
- ab8500_bm_gpswadcconvend_handler,
- IRQF_ONESHOT | IRQF_NO_SUSPEND | IRQF_SHARED,
- "ab8500-gpadc", gpadc);
- if (ret < 0) {
- dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n",
- gpadc->irq);
- goto fail;
+ /* Register interrupts */
+ if (gpadc->irq_sw >= 0) {
+ ret = request_threaded_irq(gpadc->irq_sw, NULL,
+ ab8500_bm_gpadcconvend_handler,
+ IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-sw",
+ gpadc);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "Failed to register interrupt irq: %d\n",
+ gpadc->irq_sw);
+ goto fail;
+ }
}
- /* Get Chip ID of the ABB ASIC */
- ret = abx500_get_chip_id(gpadc->dev);
- if (ret < 0) {
- dev_err(gpadc->dev, "failed to get chip ID\n");
- goto fail_irq;
+ if (gpadc->irq_hw >= 0) {
+ ret = request_threaded_irq(gpadc->irq_hw, NULL,
+ ab8500_bm_gpadcconvend_handler,
+ IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-hw",
+ gpadc);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "Failed to register interrupt irq: %d\n",
+ gpadc->irq_hw);
+ goto fail_irq;
+ }
}
- gpadc->chip_id = (u8) ret;
/* VTVout LDO used to power up ab8500-GPADC */
- gpadc->regu = regulator_get(&pdev->dev, "vddadc");
+ gpadc->regu = devm_regulator_get(&pdev->dev, "vddadc");
if (IS_ERR(gpadc->regu)) {
ret = PTR_ERR(gpadc->regu);
dev_err(gpadc->dev, "failed to get vtvout LDO\n");
goto fail_irq;
}
+
+ platform_set_drvdata(pdev, gpadc);
+
+ ret = regulator_enable(gpadc->regu);
+ if (ret) {
+ dev_err(gpadc->dev, "Failed to enable vtvout LDO: %d\n", ret);
+ goto fail_enable;
+ }
+
+ pm_runtime_set_autosuspend_delay(gpadc->dev, GPADC_AUDOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(gpadc->dev);
+ pm_runtime_set_active(gpadc->dev);
+ pm_runtime_enable(gpadc->dev);
+
ab8500_gpadc_read_calibration_data(gpadc);
list_add_tail(&gpadc->node, &ab8500_gpadc_list);
dev_dbg(gpadc->dev, "probe success\n");
+
return 0;
+
+fail_enable:
fail_irq:
- free_irq(gpadc->irq, gpadc);
+ free_irq(gpadc->irq_sw, gpadc);
+ free_irq(gpadc->irq_hw, gpadc);
fail:
- kfree(gpadc);
- gpadc = NULL;
return ret;
}
-static int __devexit ab8500_gpadc_remove(struct platform_device *pdev)
+static int ab8500_gpadc_remove(struct platform_device *pdev)
{
struct ab8500_gpadc *gpadc = platform_get_drvdata(pdev);
/* remove this gpadc entry from the list */
list_del(&gpadc->node);
/* remove interrupt - completion of Sw ADC conversion */
- free_irq(gpadc->irq, gpadc);
- /* disable VTVout LDO that is being used by GPADC */
- regulator_put(gpadc->regu);
- kfree(gpadc);
- gpadc = NULL;
+ if (gpadc->irq_sw >= 0)
+ free_irq(gpadc->irq_sw, gpadc);
+ if (gpadc->irq_hw >= 0)
+ free_irq(gpadc->irq_hw, gpadc);
+
+ pm_runtime_get_sync(gpadc->dev);
+ pm_runtime_disable(gpadc->dev);
+
+ regulator_disable(gpadc->regu);
+
+ pm_runtime_set_suspended(gpadc->dev);
+
+ pm_runtime_put_noidle(gpadc->dev);
+
return 0;
}
+static const struct dev_pm_ops ab8500_gpadc_pm_ops = {
+ SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend,
+ ab8500_gpadc_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(ab8500_gpadc_suspend,
+ ab8500_gpadc_resume)
+
+};
+
static struct platform_driver ab8500_gpadc_driver = {
.probe = ab8500_gpadc_probe,
- .remove = __devexit_p(ab8500_gpadc_remove),
+ .remove = ab8500_gpadc_remove,
.driver = {
.name = "ab8500-gpadc",
.owner = THIS_MODULE,
+ .pm = &ab8500_gpadc_pm_ops,
},
};
@@ -668,10 +1059,30 @@ static void __exit ab8500_gpadc_exit(void)
platform_driver_unregister(&ab8500_gpadc_driver);
}
+/**
+ * ab8540_gpadc_get_otp() - returns OTP values
+ *
+ */
+void ab8540_gpadc_get_otp(struct ab8500_gpadc *gpadc,
+ u16 *vmain_l, u16 *vmain_h, u16 *btemp_l, u16 *btemp_h,
+ u16 *vbat_l, u16 *vbat_h, u16 *ibat_l, u16 *ibat_h)
+{
+ *vmain_l = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo;
+ *vmain_h = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi;
+ *btemp_l = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo;
+ *btemp_h = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi;
+ *vbat_l = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo;
+ *vbat_h = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi;
+ *ibat_l = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo;
+ *ibat_h = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi;
+ return ;
+}
+
subsys_initcall_sync(ab8500_gpadc_init);
module_exit(ab8500_gpadc_exit);
MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson");
+MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson,"
+ "M'boumba Cedric Madianga");
MODULE_ALIAS("platform:ab8500_gpadc");
MODULE_DESCRIPTION("AB8500 GPADC driver");
diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c
index c28d4eb1eff..8e0dae59844 100644
--- a/drivers/mfd/ab8500-sysctrl.c
+++ b/drivers/mfd/ab8500-sysctrl.c
@@ -7,12 +7,141 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/reboot.h>
+#include <linux/signal.h>
+#include <linux/power_supply.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ab8500-sysctrl.h>
+/* RtcCtrl bits */
+#define AB8500_ALARM_MIN_LOW 0x08
+#define AB8500_ALARM_MIN_MID 0x09
+#define RTC_CTRL 0x0B
+#define RTC_ALARM_ENABLE 0x4
+
static struct device *sysctrl_dev;
+static void ab8500_power_off(void)
+{
+ sigset_t old;
+ sigset_t all;
+ static char *pss[] = {"ab8500_ac", "pm2301", "ab8500_usb"};
+ int i;
+ bool charger_present = false;
+ union power_supply_propval val;
+ struct power_supply *psy;
+ int ret;
+
+ if (sysctrl_dev == NULL) {
+ pr_err("%s: sysctrl not initialized\n", __func__);
+ return;
+ }
+
+ /*
+ * If we have a charger connected and we're powering off,
+ * reboot into charge-only mode.
+ */
+
+ for (i = 0; i < ARRAY_SIZE(pss); i++) {
+ psy = power_supply_get_by_name(pss[i]);
+ if (!psy)
+ continue;
+
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val);
+
+ if (!ret && val.intval) {
+ charger_present = true;
+ break;
+ }
+ }
+
+ if (!charger_present)
+ goto shutdown;
+
+ /* Check if battery is known */
+ psy = power_supply_get_by_name("ab8500_btemp");
+ if (psy) {
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_TECHNOLOGY,
+ &val);
+ if (!ret && val.intval != POWER_SUPPLY_TECHNOLOGY_UNKNOWN) {
+ printk(KERN_INFO
+ "Charger \"%s\" is connected with known battery."
+ " Rebooting.\n",
+ pss[i]);
+ machine_restart("charging");
+ }
+ }
+
+shutdown:
+ sigfillset(&all);
+
+ if (!sigprocmask(SIG_BLOCK, &all, &old)) {
+ (void)ab8500_sysctrl_set(AB8500_STW4500CTRL1,
+ AB8500_STW4500CTRL1_SWOFF |
+ AB8500_STW4500CTRL1_SWRESET4500N);
+ (void)sigprocmask(SIG_SETMASK, &old, NULL);
+ }
+}
+
+/*
+ * Use the AB WD to reset the platform. It will perform a hard
+ * reset instead of a soft reset. Write the reset reason to
+ * the AB before reset, which can be read upon restart.
+ */
+void ab8500_restart(char mode, const char *cmd)
+{
+ struct ab8500_platform_data *plat;
+ struct ab8500_sysctrl_platform_data *pdata;
+ u16 reason = 0;
+ u8 val;
+
+ if (sysctrl_dev == NULL) {
+ pr_err("%s: sysctrl not initialized\n", __func__);
+ return;
+ }
+
+ plat = dev_get_platdata(sysctrl_dev->parent);
+ pdata = plat->sysctrl;
+ if (pdata && pdata->reboot_reason_code)
+ reason = pdata->reboot_reason_code(cmd);
+ else
+ pr_warn("[%s] No reboot reason set. Default reason %d\n",
+ __func__, reason);
+
+ /*
+ * Disable RTC alarm, just a precaution so that no alarm
+ * is running when WD reset is executed.
+ */
+ abx500_get_register_interruptible(sysctrl_dev, AB8500_RTC,
+ RTC_CTRL , &val);
+ abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC,
+ RTC_CTRL , (val & ~RTC_ALARM_ENABLE));
+
+ /*
+ * Android is not using the RTC alarm registers during reboot
+ * so we borrow them for writing the reason of reset
+ */
+
+ /* reason[8 LSB] */
+ val = reason & 0xFF;
+ abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC,
+ AB8500_ALARM_MIN_LOW , val);
+
+ /* reason[8 MSB] */
+ val = (reason>>8) & 0xFF;
+ abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC,
+ AB8500_ALARM_MIN_MID , val);
+
+ /* Setting WD timeout to 0 */
+ ab8500_sysctrl_write(AB8500_MAINWDOGTIMER, 0xFF, 0x0);
+
+ /* Setting the parameters to AB8500 WD*/
+ ab8500_sysctrl_write(AB8500_MAINWDOGCTRL, 0xFF, (AB8500_ENABLE_WD |
+ AB8500_WD_RESTART_ON_EXPIRE | AB8500_KICK_WD));
+}
+
static inline bool valid_bank(u8 bank)
{
return ((bank == AB8500_SYS_CTRL1_BLOCK) ||
@@ -24,7 +153,7 @@ int ab8500_sysctrl_read(u16 reg, u8 *value)
u8 bank;
if (sysctrl_dev == NULL)
- return -EAGAIN;
+ return -EINVAL;
bank = (reg >> 8);
if (!valid_bank(bank))
@@ -33,13 +162,14 @@ int ab8500_sysctrl_read(u16 reg, u8 *value)
return abx500_get_register_interruptible(sysctrl_dev, bank,
(u8)(reg & 0xFF), value);
}
+EXPORT_SYMBOL(ab8500_sysctrl_read);
int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
{
u8 bank;
if (sysctrl_dev == NULL)
- return -EAGAIN;
+ return -EINVAL;
bank = (reg >> 8);
if (!valid_bank(bank))
@@ -48,16 +178,59 @@ int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank,
(u8)(reg & 0xFF), mask, value);
}
+EXPORT_SYMBOL(ab8500_sysctrl_write);
-static int __devinit ab8500_sysctrl_probe(struct platform_device *pdev)
+static int ab8500_sysctrl_probe(struct platform_device *pdev)
{
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_platform_data *plat;
+ struct ab8500_sysctrl_platform_data *pdata;
+
+ plat = dev_get_platdata(pdev->dev.parent);
+
+ if (!plat)
+ return -EINVAL;
+
sysctrl_dev = &pdev->dev;
+
+ if (!pm_power_off)
+ pm_power_off = ab8500_power_off;
+
+ pdata = plat->sysctrl;
+ if (pdata) {
+ int last, ret, i, j;
+
+ if (is_ab8505(ab8500))
+ last = AB8500_SYSCLKREQ4RFCLKBUF;
+ else
+ last = AB8500_SYSCLKREQ8RFCLKBUF;
+
+ for (i = AB8500_SYSCLKREQ1RFCLKBUF; i <= last; i++) {
+ j = i - AB8500_SYSCLKREQ1RFCLKBUF;
+ ret = ab8500_sysctrl_write(i, 0xff,
+ pdata->initial_req_buf_config[j]);
+ dev_dbg(&pdev->dev,
+ "Setting SysClkReq%dRfClkBuf 0x%X\n",
+ j + 1,
+ pdata->initial_req_buf_config[j]);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "unable to set sysClkReq%dRfClkBuf: "
+ "%d\n", j + 1, ret);
+ }
+ }
+ }
+
return 0;
}
-static int __devexit ab8500_sysctrl_remove(struct platform_device *pdev)
+static int ab8500_sysctrl_remove(struct platform_device *pdev)
{
sysctrl_dev = NULL;
+
+ if (pm_power_off == ab8500_power_off)
+ pm_power_off = NULL;
+
return 0;
}
@@ -67,14 +240,14 @@ static struct platform_driver ab8500_sysctrl_driver = {
.owner = THIS_MODULE,
},
.probe = ab8500_sysctrl_probe,
- .remove = __devexit_p(ab8500_sysctrl_remove),
+ .remove = ab8500_sysctrl_remove,
};
static int __init ab8500_sysctrl_init(void)
{
return platform_driver_register(&ab8500_sysctrl_driver);
}
-subsys_initcall(ab8500_sysctrl_init);
+arch_initcall(ab8500_sysctrl_init);
MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com");
MODULE_DESCRIPTION("AB8500 system control driver");
diff --git a/drivers/mfd/abx500-core.c b/drivers/mfd/abx500-core.c
index 7ce65f49480..fe418995108 100644
--- a/drivers/mfd/abx500-core.c
+++ b/drivers/mfd/abx500-core.c
@@ -36,7 +36,9 @@ int abx500_register_ops(struct device *dev, struct abx500_ops *ops)
{
struct abx500_device_entry *dev_entry;
- dev_entry = kzalloc(sizeof(struct abx500_device_entry), GFP_KERNEL);
+ dev_entry = devm_kzalloc(dev,
+ sizeof(struct abx500_device_entry),
+ GFP_KERNEL);
if (!dev_entry) {
dev_err(dev, "register_ops kzalloc failed");
return -ENOMEM;
@@ -54,12 +56,8 @@ void abx500_remove_ops(struct device *dev)
struct abx500_device_entry *dev_entry, *tmp;
list_for_each_entry_safe(dev_entry, tmp, &abx500_list, list)
- {
- if (dev_entry->dev == dev) {
+ if (dev_entry->dev == dev)
list_del(&dev_entry->list);
- kfree(dev_entry);
- }
- }
}
EXPORT_SYMBOL(abx500_remove_ops);
diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c
index ea8b9475731..f495b8b57dd 100644
--- a/drivers/mfd/adp5520.c
+++ b/drivers/mfd/adp5520.c
@@ -20,7 +20,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -36,6 +35,7 @@ struct adp5520_chip {
struct blocking_notifier_head notifier_list;
int irq;
unsigned long id;
+ uint8_t mode;
};
static int __adp5520_read(struct i2c_client *client,
@@ -203,10 +203,10 @@ static int adp5520_remove_subdevs(struct adp5520_chip *chip)
return device_for_each_child(chip->dev, NULL, __remove_subdev);
}
-static int __devinit adp5520_probe(struct i2c_client *client,
+static int adp5520_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct adp5520_platform_data *pdata = client->dev.platform_data;
+ struct adp5520_platform_data *pdata = dev_get_platdata(&client->dev);
struct platform_device *pdev;
struct adp5520_chip *chip;
int ret;
@@ -222,7 +222,7 @@ static int __devinit adp5520_probe(struct i2c_client *client,
return -ENODEV;
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
@@ -243,7 +243,7 @@ static int __devinit adp5520_probe(struct i2c_client *client,
if (ret) {
dev_err(&client->dev, "failed to request irq %d\n",
chip->irq);
- goto out_free_chip;
+ return ret;
}
}
@@ -301,13 +301,10 @@ out_free_irq:
if (chip->irq)
free_irq(chip->irq, chip);
-out_free_chip:
- kfree(chip);
-
return ret;
}
-static int __devexit adp5520_remove(struct i2c_client *client)
+static int adp5520_remove(struct i2c_client *client)
{
struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
@@ -316,7 +313,6 @@ static int __devexit adp5520_remove(struct i2c_client *client)
adp5520_remove_subdevs(chip);
adp5520_write(chip->dev, ADP5520_MODE_STATUS, 0);
- kfree(chip);
return 0;
}
@@ -326,7 +322,10 @@ static int adp5520_suspend(struct device *dev)
struct i2c_client *client = to_i2c_client(dev);
struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
- adp5520_clr_bits(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY);
+ adp5520_read(chip->dev, ADP5520_MODE_STATUS, &chip->mode);
+ /* All other bits are W1C */
+ chip->mode &= ADP5520_BL_EN | ADP5520_DIM_EN | ADP5520_nSTNBY;
+ adp5520_write(chip->dev, ADP5520_MODE_STATUS, 0);
return 0;
}
@@ -335,7 +334,7 @@ static int adp5520_resume(struct device *dev)
struct i2c_client *client = to_i2c_client(dev);
struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
- adp5520_set_bits(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY);
+ adp5520_write(chip->dev, ADP5520_MODE_STATUS, chip->mode);
return 0;
}
#endif
@@ -356,21 +355,11 @@ static struct i2c_driver adp5520_driver = {
.pm = &adp5520_pm,
},
.probe = adp5520_probe,
- .remove = __devexit_p(adp5520_remove),
+ .remove = adp5520_remove,
.id_table = adp5520_id,
};
-static int __init adp5520_init(void)
-{
- return i2c_add_driver(&adp5520_driver);
-}
-module_init(adp5520_init);
-
-static void __exit adp5520_exit(void)
-{
- i2c_del_driver(&adp5520_driver);
-}
-module_exit(adp5520_exit);
+module_i2c_driver(adp5520_driver);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("ADP5520(01) PMIC-MFD Driver");
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index 1b48f209480..cfc191abae4 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -16,9 +16,13 @@
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
#include <linux/slab.h>
#include <linux/mfd/arizona/core.h>
@@ -39,11 +43,21 @@ int arizona_clk32k_enable(struct arizona *arizona)
arizona->clk32k_ref++;
- if (arizona->clk32k_ref == 1)
+ if (arizona->clk32k_ref == 1) {
+ switch (arizona->pdata.clk32k_src) {
+ case ARIZONA_32KZ_MCLK1:
+ ret = pm_runtime_get_sync(arizona->dev);
+ if (ret != 0)
+ goto out;
+ break;
+ }
+
ret = regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
ARIZONA_CLK_32K_ENA,
ARIZONA_CLK_32K_ENA);
+ }
+out:
if (ret != 0)
arizona->clk32k_ref--;
@@ -63,10 +77,17 @@ int arizona_clk32k_disable(struct arizona *arizona)
arizona->clk32k_ref--;
- if (arizona->clk32k_ref == 0)
+ if (arizona->clk32k_ref == 0) {
regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
ARIZONA_CLK_32K_ENA, 0);
+ switch (arizona->pdata.clk32k_src) {
+ case ARIZONA_32KZ_MCLK1:
+ pm_runtime_put_sync(arizona->dev);
+ break;
+ }
+ }
+
mutex_unlock(&arizona->clk_lock);
return ret;
@@ -98,9 +119,9 @@ static irqreturn_t arizona_underclocked(int irq, void *data)
if (val & ARIZONA_AIF3_UNDERCLOCKED_STS)
dev_err(arizona->dev, "AIF3 underclocked\n");
- if (val & ARIZONA_AIF3_UNDERCLOCKED_STS)
- dev_err(arizona->dev, "AIF3 underclocked\n");
if (val & ARIZONA_AIF2_UNDERCLOCKED_STS)
+ dev_err(arizona->dev, "AIF2 underclocked\n");
+ if (val & ARIZONA_AIF1_UNDERCLOCKED_STS)
dev_err(arizona->dev, "AIF1 underclocked\n");
if (val & ARIZONA_ISRC2_UNDERCLOCKED_STS)
dev_err(arizona->dev, "ISRC2 underclocked\n");
@@ -115,7 +136,7 @@ static irqreturn_t arizona_underclocked(int irq, void *data)
if (val & ARIZONA_ADC_UNDERCLOCKED_STS)
dev_err(arizona->dev, "ADC underclocked\n");
if (val & ARIZONA_MIXER_UNDERCLOCKED_STS)
- dev_err(arizona->dev, "Mixer underclocked\n");
+ dev_err(arizona->dev, "Mixer dropped sample\n");
return IRQ_HANDLED;
}
@@ -179,42 +200,130 @@ static irqreturn_t arizona_overclocked(int irq, void *data)
return IRQ_HANDLED;
}
-static int arizona_wait_for_boot(struct arizona *arizona)
+static int arizona_poll_reg(struct arizona *arizona,
+ int timeout, unsigned int reg,
+ unsigned int mask, unsigned int target)
{
- unsigned int reg;
+ unsigned int val = 0;
int ret, i;
+ for (i = 0; i < timeout; i++) {
+ ret = regmap_read(arizona->regmap, reg, &val);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to read reg %u: %d\n",
+ reg, ret);
+ continue;
+ }
+
+ if ((val & mask) == target)
+ return 0;
+
+ msleep(1);
+ }
+
+ dev_err(arizona->dev, "Polling reg %u timed out: %x\n", reg, val);
+ return -ETIMEDOUT;
+}
+
+static int arizona_wait_for_boot(struct arizona *arizona)
+{
+ int ret;
+
/*
* We can't use an interrupt as we need to runtime resume to do so,
* we won't race with the interrupt handler as it'll be blocked on
* runtime resume.
*/
- for (i = 0; i < 5; i++) {
- msleep(1);
+ ret = arizona_poll_reg(arizona, 5, ARIZONA_INTERRUPT_RAW_STATUS_5,
+ ARIZONA_BOOT_DONE_STS, ARIZONA_BOOT_DONE_STS);
- ret = regmap_read(arizona->regmap,
- ARIZONA_INTERRUPT_RAW_STATUS_5, &reg);
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to read boot state: %d\n",
- ret);
- continue;
- }
+ if (!ret)
+ regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5,
+ ARIZONA_BOOT_DONE_STS);
- if (reg & ARIZONA_BOOT_DONE_STS)
- break;
+ pm_runtime_mark_last_busy(arizona->dev);
+
+ return ret;
+}
+
+static int arizona_apply_hardware_patch(struct arizona* arizona)
+{
+ unsigned int fll, sysclk;
+ int ret, err;
+
+ /* Cache existing FLL and SYSCLK settings */
+ ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &fll);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to cache FLL settings: %d\n",
+ ret);
+ return ret;
+ }
+ ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &sysclk);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to cache SYSCLK settings: %d\n",
+ ret);
+ return ret;
}
- if (reg & ARIZONA_BOOT_DONE_STS) {
- regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5,
- ARIZONA_BOOT_DONE_STS);
- } else {
- dev_err(arizona->dev, "Device boot timed out: %x\n", reg);
- return -ETIMEDOUT;
+ /* Start up SYSCLK using the FLL in free running mode */
+ ret = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1,
+ ARIZONA_FLL1_ENA | ARIZONA_FLL1_FREERUN);
+ if (ret != 0) {
+ dev_err(arizona->dev,
+ "Failed to start FLL in freerunning mode: %d\n",
+ ret);
+ return ret;
+ }
+ ret = arizona_poll_reg(arizona, 25, ARIZONA_INTERRUPT_RAW_STATUS_5,
+ ARIZONA_FLL1_CLOCK_OK_STS,
+ ARIZONA_FLL1_CLOCK_OK_STS);
+ if (ret != 0) {
+ ret = -ETIMEDOUT;
+ goto err_fll;
}
- pm_runtime_mark_last_busy(arizona->dev);
+ ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, 0x0144);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to start SYSCLK: %d\n", ret);
+ goto err_fll;
+ }
- return 0;
+ /* Start the write sequencer and wait for it to finish */
+ ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
+ ARIZONA_WSEQ_ENA | ARIZONA_WSEQ_START | 160);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to start write sequencer: %d\n",
+ ret);
+ goto err_sysclk;
+ }
+ ret = arizona_poll_reg(arizona, 5, ARIZONA_WRITE_SEQUENCER_CTRL_1,
+ ARIZONA_WSEQ_BUSY, 0);
+ if (ret != 0) {
+ regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
+ ARIZONA_WSEQ_ABORT);
+ ret = -ETIMEDOUT;
+ }
+
+err_sysclk:
+ err = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, sysclk);
+ if (err != 0) {
+ dev_err(arizona->dev,
+ "Failed to re-apply old SYSCLK settings: %d\n",
+ err);
+ }
+
+err_fll:
+ err = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, fll);
+ if (err != 0) {
+ dev_err(arizona->dev,
+ "Failed to re-apply old FLL settings: %d\n",
+ err);
+ }
+
+ if (ret != 0)
+ return ret;
+ else
+ return err;
}
#ifdef CONFIG_PM_RUNTIME
@@ -233,26 +342,143 @@ static int arizona_runtime_resume(struct device *dev)
regcache_cache_only(arizona->regmap, false);
- ret = arizona_wait_for_boot(arizona);
- if (ret != 0) {
- regulator_disable(arizona->dcvdd);
- return ret;
+ switch (arizona->type) {
+ case WM5102:
+ if (arizona->external_dcvdd) {
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_ISOLATION_CONTROL,
+ ARIZONA_ISOLATE_DCVDD1, 0);
+ if (ret != 0) {
+ dev_err(arizona->dev,
+ "Failed to connect DCVDD: %d\n", ret);
+ goto err;
+ }
+ }
+
+ ret = wm5102_patch(arizona);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to apply patch: %d\n",
+ ret);
+ goto err;
+ }
+
+ ret = arizona_apply_hardware_patch(arizona);
+ if (ret != 0) {
+ dev_err(arizona->dev,
+ "Failed to apply hardware patch: %d\n",
+ ret);
+ goto err;
+ }
+ break;
+ default:
+ ret = arizona_wait_for_boot(arizona);
+ if (ret != 0) {
+ goto err;
+ }
+
+ if (arizona->external_dcvdd) {
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_ISOLATION_CONTROL,
+ ARIZONA_ISOLATE_DCVDD1, 0);
+ if (ret != 0) {
+ dev_err(arizona->dev,
+ "Failed to connect DCVDD: %d\n", ret);
+ goto err;
+ }
+ }
+ break;
}
- regcache_sync(arizona->regmap);
+ switch (arizona->type) {
+ case WM5102:
+ ret = wm5102_patch(arizona);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to apply patch: %d\n",
+ ret);
+ goto err;
+ }
+ default:
+ break;
+ }
+
+ ret = regcache_sync(arizona->regmap);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to restore register cache\n");
+ goto err;
+ }
return 0;
+
+err:
+ regcache_cache_only(arizona->regmap, true);
+ regulator_disable(arizona->dcvdd);
+ return ret;
}
static int arizona_runtime_suspend(struct device *dev)
{
struct arizona *arizona = dev_get_drvdata(dev);
+ int ret;
dev_dbg(arizona->dev, "Entering AoD mode\n");
- regulator_disable(arizona->dcvdd);
+ if (arizona->external_dcvdd) {
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_ISOLATION_CONTROL,
+ ARIZONA_ISOLATE_DCVDD1,
+ ARIZONA_ISOLATE_DCVDD1);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to isolate DCVDD: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
regcache_cache_only(arizona->regmap, true);
regcache_mark_dirty(arizona->regmap);
+ regulator_disable(arizona->dcvdd);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int arizona_suspend(struct device *dev)
+{
+ struct arizona *arizona = dev_get_drvdata(dev);
+
+ dev_dbg(arizona->dev, "Suspend, disabling IRQ\n");
+ disable_irq(arizona->irq);
+
+ return 0;
+}
+
+static int arizona_suspend_late(struct device *dev)
+{
+ struct arizona *arizona = dev_get_drvdata(dev);
+
+ dev_dbg(arizona->dev, "Late suspend, reenabling IRQ\n");
+ enable_irq(arizona->irq);
+
+ return 0;
+}
+
+static int arizona_resume_noirq(struct device *dev)
+{
+ struct arizona *arizona = dev_get_drvdata(dev);
+
+ dev_dbg(arizona->dev, "Early resume, disabling IRQ\n");
+ disable_irq(arizona->irq);
+
+ return 0;
+}
+
+static int arizona_resume(struct device *dev)
+{
+ struct arizona *arizona = dev_get_drvdata(dev);
+
+ dev_dbg(arizona->dev, "Late resume, reenabling IRQ\n");
+ enable_irq(arizona->irq);
return 0;
}
@@ -262,34 +488,155 @@ const struct dev_pm_ops arizona_pm_ops = {
SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
arizona_runtime_resume,
NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(arizona_suspend, arizona_resume)
+#ifdef CONFIG_PM_SLEEP
+ .suspend_late = arizona_suspend_late,
+ .resume_noirq = arizona_resume_noirq,
+#endif
};
EXPORT_SYMBOL_GPL(arizona_pm_ops);
-static struct mfd_cell early_devs[] = {
+#ifdef CONFIG_OF
+int arizona_of_get_type(struct device *dev)
+{
+ const struct of_device_id *id = of_match_device(arizona_of_match, dev);
+
+ if (id)
+ return (int)id->data;
+ else
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_of_get_type);
+
+int arizona_of_get_named_gpio(struct arizona *arizona, const char *prop,
+ bool mandatory)
+{
+ int gpio;
+
+ gpio = of_get_named_gpio(arizona->dev->of_node, prop, 0);
+ if (gpio < 0) {
+ if (mandatory)
+ dev_err(arizona->dev,
+ "Mandatory DT gpio %s missing/malformed: %d\n",
+ prop, gpio);
+
+ gpio = 0;
+ }
+
+ return gpio;
+}
+EXPORT_SYMBOL_GPL(arizona_of_get_named_gpio);
+
+static int arizona_of_get_core_pdata(struct arizona *arizona)
+{
+ struct arizona_pdata *pdata = &arizona->pdata;
+ int ret, i;
+
+ pdata->reset = arizona_of_get_named_gpio(arizona, "wlf,reset", true);
+
+ ret = of_property_read_u32_array(arizona->dev->of_node,
+ "wlf,gpio-defaults",
+ arizona->pdata.gpio_defaults,
+ ARRAY_SIZE(arizona->pdata.gpio_defaults));
+ if (ret >= 0) {
+ /*
+ * All values are literal except out of range values
+ * which are chip default, translate into platform
+ * data which uses 0 as chip default and out of range
+ * as zero.
+ */
+ for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
+ if (arizona->pdata.gpio_defaults[i] > 0xffff)
+ arizona->pdata.gpio_defaults[i] = 0;
+ else if (arizona->pdata.gpio_defaults[i] == 0)
+ arizona->pdata.gpio_defaults[i] = 0x10000;
+ }
+ } else {
+ dev_err(arizona->dev, "Failed to parse GPIO defaults: %d\n",
+ ret);
+ }
+
+ return 0;
+}
+
+const struct of_device_id arizona_of_match[] = {
+ { .compatible = "wlf,wm5102", .data = (void *)WM5102 },
+ { .compatible = "wlf,wm5110", .data = (void *)WM5110 },
+ { .compatible = "wlf,wm8997", .data = (void *)WM8997 },
+ {},
+};
+EXPORT_SYMBOL_GPL(arizona_of_match);
+#else
+static inline int arizona_of_get_core_pdata(struct arizona *arizona)
+{
+ return 0;
+}
+#endif
+
+static const struct mfd_cell early_devs[] = {
{ .name = "arizona-ldo1" },
};
-static struct mfd_cell wm5102_devs[] = {
+static const char *wm5102_supplies[] = {
+ "DBVDD2",
+ "DBVDD3",
+ "CPVDD",
+ "SPKVDDL",
+ "SPKVDDR",
+ "MICVDD",
+};
+
+static const struct mfd_cell wm5102_devs[] = {
+ { .name = "arizona-micsupp" },
{ .name = "arizona-extcon" },
{ .name = "arizona-gpio" },
- { .name = "arizona-micsupp" },
+ { .name = "arizona-haptics" },
{ .name = "arizona-pwm" },
- { .name = "wm5102-codec" },
+ {
+ .name = "wm5102-codec",
+ .parent_supplies = wm5102_supplies,
+ .num_parent_supplies = ARRAY_SIZE(wm5102_supplies),
+ },
};
-static struct mfd_cell wm5110_devs[] = {
+static const struct mfd_cell wm5110_devs[] = {
+ { .name = "arizona-micsupp" },
{ .name = "arizona-extcon" },
{ .name = "arizona-gpio" },
+ { .name = "arizona-haptics" },
+ { .name = "arizona-pwm" },
+ {
+ .name = "wm5110-codec",
+ .parent_supplies = wm5102_supplies,
+ .num_parent_supplies = ARRAY_SIZE(wm5102_supplies),
+ },
+};
+
+static const char *wm8997_supplies[] = {
+ "DBVDD2",
+ "CPVDD",
+ "SPKVDD",
+};
+
+static const struct mfd_cell wm8997_devs[] = {
{ .name = "arizona-micsupp" },
+ { .name = "arizona-extcon" },
+ { .name = "arizona-gpio" },
+ { .name = "arizona-haptics" },
{ .name = "arizona-pwm" },
- { .name = "wm5110-codec" },
+ {
+ .name = "wm8997-codec",
+ .parent_supplies = wm8997_supplies,
+ .num_parent_supplies = ARRAY_SIZE(wm8997_supplies),
+ },
};
-int __devinit arizona_dev_init(struct arizona *arizona)
+int arizona_dev_init(struct arizona *arizona)
{
struct device *dev = arizona->dev;
const char *type_name;
unsigned int reg, val;
+ int (*apply_patch)(struct arizona *) = NULL;
int ret, i;
dev_set_drvdata(arizona->dev, arizona);
@@ -298,12 +645,15 @@ int __devinit arizona_dev_init(struct arizona *arizona)
if (dev_get_platdata(arizona->dev))
memcpy(&arizona->pdata, dev_get_platdata(arizona->dev),
sizeof(arizona->pdata));
+ else
+ arizona_of_get_core_pdata(arizona);
regcache_cache_only(arizona->regmap, true);
switch (arizona->type) {
case WM5102:
case WM5110:
+ case WM8997:
for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++)
arizona->core_supplies[i].supply
= wm5102_core_supplies[i];
@@ -315,6 +665,9 @@ int __devinit arizona_dev_init(struct arizona *arizona)
return -EINVAL;
}
+ /* Mark DCVDD as external, LDO1 driver will clear if internal */
+ arizona->external_dcvdd = true;
+
ret = mfd_add_devices(arizona->dev, -1, early_devs,
ARRAY_SIZE(early_devs), NULL, 0, NULL);
if (ret != 0) {
@@ -337,6 +690,17 @@ int __devinit arizona_dev_init(struct arizona *arizona)
goto err_early;
}
+ if (arizona->pdata.reset) {
+ /* Start out with /RESET low to put the chip into reset */
+ ret = gpio_request_one(arizona->pdata.reset,
+ GPIOF_DIR_OUT | GPIOF_INIT_LOW,
+ "arizona /RESET");
+ if (ret != 0) {
+ dev_err(dev, "Failed to request /RESET: %d\n", ret);
+ goto err_early;
+ }
+ }
+
ret = regulator_bulk_enable(arizona->num_core_supplies,
arizona->core_supplies);
if (ret != 0) {
@@ -352,20 +716,70 @@ int __devinit arizona_dev_init(struct arizona *arizona)
}
if (arizona->pdata.reset) {
- /* Start out with /RESET low to put the chip into reset */
- ret = gpio_request_one(arizona->pdata.reset,
- GPIOF_DIR_OUT | GPIOF_INIT_LOW,
- "arizona /RESET");
+ gpio_set_value_cansleep(arizona->pdata.reset, 1);
+ msleep(1);
+ }
+
+ regcache_cache_only(arizona->regmap, false);
+
+ /* Verify that this is a chip we know about */
+ ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
+ if (ret != 0) {
+ dev_err(dev, "Failed to read ID register: %d\n", ret);
+ goto err_reset;
+ }
+
+ switch (reg) {
+ case 0x5102:
+ case 0x5110:
+ case 0x8997:
+ break;
+ default:
+ dev_err(arizona->dev, "Unknown device ID: %x\n", reg);
+ goto err_reset;
+ }
+
+ /* If we have a /RESET GPIO we'll already be reset */
+ if (!arizona->pdata.reset) {
+ regcache_mark_dirty(arizona->regmap);
+
+ ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
if (ret != 0) {
- dev_err(dev, "Failed to request /RESET: %d\n", ret);
- goto err_dcvdd;
+ dev_err(dev, "Failed to reset device: %d\n", ret);
+ goto err_reset;
}
- gpio_set_value_cansleep(arizona->pdata.reset, 1);
+ msleep(1);
+
+ ret = regcache_sync(arizona->regmap);
+ if (ret != 0) {
+ dev_err(dev, "Failed to sync device: %d\n", ret);
+ goto err_reset;
+ }
}
- regcache_cache_only(arizona->regmap, false);
+ /* Ensure device startup is complete */
+ switch (arizona->type) {
+ case WM5102:
+ ret = regmap_read(arizona->regmap, 0x19, &val);
+ if (ret != 0)
+ dev_err(dev,
+ "Failed to check write sequencer state: %d\n",
+ ret);
+ else if (val & 0x01)
+ break;
+ /* Fall through */
+ default:
+ ret = arizona_wait_for_boot(arizona);
+ if (ret != 0) {
+ dev_err(arizona->dev,
+ "Device failed initial boot: %d\n", ret);
+ goto err_reset;
+ }
+ break;
+ }
+ /* Read the device ID information & do device specific stuff */
ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
if (ret != 0) {
dev_err(dev, "Failed to read ID register: %d\n", ret);
@@ -389,7 +803,8 @@ int __devinit arizona_dev_init(struct arizona *arizona)
arizona->type);
arizona->type = WM5102;
}
- ret = wm5102_patch(arizona);
+ apply_patch = wm5102_patch;
+ arizona->rev &= 0x7;
break;
#endif
#ifdef CONFIG_MFD_WM5110
@@ -400,7 +815,18 @@ int __devinit arizona_dev_init(struct arizona *arizona)
arizona->type);
arizona->type = WM5110;
}
- ret = wm5110_patch(arizona);
+ apply_patch = wm5110_patch;
+ break;
+#endif
+#ifdef CONFIG_MFD_WM8997
+ case 0x8997:
+ type_name = "WM8997";
+ if (arizona->type != WM8997) {
+ dev_err(arizona->dev, "WM8997 registered as %d\n",
+ arizona->type);
+ arizona->type = WM8997;
+ }
+ apply_patch = wm8997_patch;
break;
#endif
default:
@@ -410,22 +836,27 @@ int __devinit arizona_dev_init(struct arizona *arizona)
dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A');
- if (ret != 0)
- dev_err(arizona->dev, "Failed to apply patch: %d\n", ret);
-
- /* If we have a /RESET GPIO we'll already be reset */
- if (!arizona->pdata.reset) {
- ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
+ if (apply_patch) {
+ ret = apply_patch(arizona);
if (ret != 0) {
- dev_err(dev, "Failed to reset device: %d\n", ret);
+ dev_err(arizona->dev, "Failed to apply patch: %d\n",
+ ret);
goto err_reset;
}
- }
- ret = arizona_wait_for_boot(arizona);
- if (ret != 0) {
- dev_err(arizona->dev, "Device failed initial boot: %d\n", ret);
- goto err_reset;
+ switch (arizona->type) {
+ case WM5102:
+ ret = arizona_apply_hardware_patch(arizona);
+ if (ret != 0) {
+ dev_err(arizona->dev,
+ "Failed to apply hardware patch: %d\n",
+ ret);
+ goto err_reset;
+ }
+ break;
+ default:
+ break;
+ }
}
for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
@@ -450,6 +881,7 @@ int __devinit arizona_dev_init(struct arizona *arizona)
regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
ARIZONA_CLK_32K_SRC_MASK,
arizona->pdata.clk32k_src - 1);
+ arizona_clk32k_enable(arizona);
break;
case ARIZONA_32KZ_NONE:
regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
@@ -462,6 +894,39 @@ int __devinit arizona_dev_init(struct arizona *arizona)
goto err_reset;
}
+ for (i = 0; i < ARIZONA_MAX_MICBIAS; i++) {
+ if (!arizona->pdata.micbias[i].mV &&
+ !arizona->pdata.micbias[i].bypass)
+ continue;
+
+ /* Apply default for bypass mode */
+ if (!arizona->pdata.micbias[i].mV)
+ arizona->pdata.micbias[i].mV = 2800;
+
+ val = (arizona->pdata.micbias[i].mV - 1500) / 100;
+
+ val <<= ARIZONA_MICB1_LVL_SHIFT;
+
+ if (arizona->pdata.micbias[i].ext_cap)
+ val |= ARIZONA_MICB1_EXT_CAP;
+
+ if (arizona->pdata.micbias[i].discharge)
+ val |= ARIZONA_MICB1_DISCH;
+
+ if (arizona->pdata.micbias[i].soft_start)
+ val |= ARIZONA_MICB1_RATE;
+
+ if (arizona->pdata.micbias[i].bypass)
+ val |= ARIZONA_MICB1_BYPASS;
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_MIC_BIAS_CTRL_1 + i,
+ ARIZONA_MICB1_LVL_MASK |
+ ARIZONA_MICB1_DISCH |
+ ARIZONA_MICB1_BYPASS |
+ ARIZONA_MICB1_RATE, val);
+ }
+
for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
/* Default for both is 0 so noop with defaults */
val = arizona->pdata.dmic_ref[i]
@@ -520,7 +985,11 @@ int __devinit arizona_dev_init(struct arizona *arizona)
break;
case WM5110:
ret = mfd_add_devices(arizona->dev, -1, wm5110_devs,
- ARRAY_SIZE(wm5102_devs), NULL, 0, NULL);
+ ARRAY_SIZE(wm5110_devs), NULL, 0, NULL);
+ break;
+ case WM8997:
+ ret = mfd_add_devices(arizona->dev, -1, wm8997_devs,
+ ARRAY_SIZE(wm8997_devs), NULL, 0, NULL);
break;
}
@@ -539,10 +1008,9 @@ err_irq:
arizona_irq_exit(arizona);
err_reset:
if (arizona->pdata.reset) {
- gpio_set_value_cansleep(arizona->pdata.reset, 1);
+ gpio_set_value_cansleep(arizona->pdata.reset, 0);
gpio_free(arizona->pdata.reset);
}
-err_dcvdd:
regulator_disable(arizona->dcvdd);
err_enable:
regulator_bulk_disable(arizona->num_core_supplies,
@@ -553,7 +1021,7 @@ err_early:
}
EXPORT_SYMBOL_GPL(arizona_dev_init);
-int __devexit arizona_dev_exit(struct arizona *arizona)
+int arizona_dev_exit(struct arizona *arizona)
{
mfd_remove_devices(arizona->dev);
arizona_free_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, arizona);
@@ -561,6 +1029,11 @@ int __devexit arizona_dev_exit(struct arizona *arizona)
arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona);
pm_runtime_disable(arizona->dev);
arizona_irq_exit(arizona);
+ if (arizona->pdata.reset)
+ gpio_set_value_cansleep(arizona->pdata.reset, 0);
+ regulator_disable(arizona->dcvdd);
+ regulator_bulk_disable(ARRAY_SIZE(arizona->core_supplies),
+ arizona->core_supplies);
return 0;
}
EXPORT_SYMBOL_GPL(arizona_dev_exit);
diff --git a/drivers/mfd/arizona-i2c.c b/drivers/mfd/arizona-i2c.c
index 570c4b43808..beccb790c9b 100644
--- a/drivers/mfd/arizona-i2c.c
+++ b/drivers/mfd/arizona-i2c.c
@@ -17,19 +17,25 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/of.h>
#include <linux/mfd/arizona/core.h>
#include "arizona.h"
-static __devinit int arizona_i2c_probe(struct i2c_client *i2c,
+static int arizona_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct arizona *arizona;
const struct regmap_config *regmap_config;
- int ret;
+ int ret, type;
- switch (id->driver_data) {
+ if (i2c->dev.of_node)
+ type = arizona_of_get_type(&i2c->dev);
+ else
+ type = id->driver_data;
+
+ switch (type) {
#ifdef CONFIG_MFD_WM5102
case WM5102:
regmap_config = &wm5102_i2c_regmap;
@@ -40,6 +46,11 @@ static __devinit int arizona_i2c_probe(struct i2c_client *i2c,
regmap_config = &wm5110_i2c_regmap;
break;
#endif
+#ifdef CONFIG_MFD_WM8997
+ case WM8997:
+ regmap_config = &wm8997_i2c_regmap;
+ break;
+#endif
default:
dev_err(&i2c->dev, "Unknown device type %ld\n",
id->driver_data);
@@ -65,7 +76,7 @@ static __devinit int arizona_i2c_probe(struct i2c_client *i2c,
return arizona_dev_init(arizona);
}
-static int __devexit arizona_i2c_remove(struct i2c_client *i2c)
+static int arizona_i2c_remove(struct i2c_client *i2c)
{
struct arizona *arizona = dev_get_drvdata(&i2c->dev);
arizona_dev_exit(arizona);
@@ -75,6 +86,7 @@ static int __devexit arizona_i2c_remove(struct i2c_client *i2c)
static const struct i2c_device_id arizona_i2c_id[] = {
{ "wm5102", WM5102 },
{ "wm5110", WM5110 },
+ { "wm8997", WM8997 },
{ }
};
MODULE_DEVICE_TABLE(i2c, arizona_i2c_id);
@@ -84,9 +96,10 @@ static struct i2c_driver arizona_i2c_driver = {
.name = "arizona",
.owner = THIS_MODULE,
.pm = &arizona_pm_ops,
+ .of_match_table = of_match_ptr(arizona_of_match),
},
.probe = arizona_i2c_probe,
- .remove = __devexit_p(arizona_i2c_remove),
+ .remove = arizona_i2c_remove,
.id_table = arizona_i2c_id,
};
diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c
index ef0f2d001df..17102f58910 100644
--- a/drivers/mfd/arizona-irq.c
+++ b/drivers/mfd/arizona-irq.c
@@ -94,6 +94,7 @@ static irqreturn_t arizona_ctrlif_err(int irq, void *data)
static irqreturn_t arizona_irq_thread(int irq, void *data)
{
struct arizona *arizona = data;
+ bool poll;
unsigned int val;
int ret;
@@ -103,20 +104,39 @@ static irqreturn_t arizona_irq_thread(int irq, void *data)
return IRQ_NONE;
}
- /* Always handle the AoD domain */
- handle_nested_irq(irq_find_mapping(arizona->virq, 0));
+ do {
+ poll = false;
+
+ /* Always handle the AoD domain */
+ handle_nested_irq(irq_find_mapping(arizona->virq, 0));
+
+ /*
+ * Check if one of the main interrupts is asserted and only
+ * check that domain if it is.
+ */
+ ret = regmap_read(arizona->regmap, ARIZONA_IRQ_PIN_STATUS,
+ &val);
+ if (ret == 0 && val & ARIZONA_IRQ1_STS) {
+ handle_nested_irq(irq_find_mapping(arizona->virq, 1));
+ } else if (ret != 0) {
+ dev_err(arizona->dev,
+ "Failed to read main IRQ status: %d\n", ret);
+ }
- /*
- * Check if one of the main interrupts is asserted and only
- * check that domain if it is.
- */
- ret = regmap_read(arizona->regmap, ARIZONA_IRQ_PIN_STATUS, &val);
- if (ret == 0 && val & ARIZONA_IRQ1_STS) {
- handle_nested_irq(irq_find_mapping(arizona->virq, 1));
- } else if (ret != 0) {
- dev_err(arizona->dev, "Failed to read main IRQ status: %d\n",
- ret);
- }
+ /*
+ * Poll the IRQ pin status to see if we're really done
+ * if the interrupt controller can't do it for us.
+ */
+ if (!arizona->pdata.irq_gpio) {
+ break;
+ } else if (arizona->pdata.irq_flags & IRQF_TRIGGER_RISING &&
+ gpio_get_value_cansleep(arizona->pdata.irq_gpio)) {
+ poll = true;
+ } else if (arizona->pdata.irq_flags & IRQF_TRIGGER_FALLING &&
+ !gpio_get_value_cansleep(arizona->pdata.irq_gpio)) {
+ poll = true;
+ }
+ } while (poll);
pm_runtime_mark_last_busy(arizona->dev);
pm_runtime_put_autosuspend(arizona->dev);
@@ -169,6 +189,7 @@ int arizona_irq_init(struct arizona *arizona)
int ret, i;
const struct regmap_irq_chip *aod, *irq;
bool ctrlif_error = true;
+ struct irq_data *irq_data;
switch (arizona->type) {
#ifdef CONFIG_MFD_WM5102
@@ -176,13 +197,7 @@ int arizona_irq_init(struct arizona *arizona)
aod = &wm5102_aod;
irq = &wm5102_irq;
- switch (arizona->rev) {
- case 0:
- ctrlif_error = false;
- break;
- default:
- break;
- }
+ ctrlif_error = false;
break;
#endif
#ifdef CONFIG_MFD_WM5110
@@ -190,14 +205,15 @@ int arizona_irq_init(struct arizona *arizona)
aod = &wm5110_aod;
irq = &wm5110_irq;
- switch (arizona->rev) {
- case 0:
- case 1:
- ctrlif_error = false;
- break;
- default:
- break;
- }
+ ctrlif_error = false;
+ break;
+#endif
+#ifdef CONFIG_MFD_WM8997
+ case WM8997:
+ aod = &wm8997_aod;
+ irq = &wm8997_irq;
+
+ ctrlif_error = false;
break;
#endif
default:
@@ -205,7 +221,36 @@ int arizona_irq_init(struct arizona *arizona)
return -EINVAL;
}
- if (arizona->pdata.irq_active_high) {
+ /* Disable all wake sources by default */
+ regmap_write(arizona->regmap, ARIZONA_WAKE_CONTROL, 0);
+
+ /* Read the flags from the interrupt controller if not specified */
+ if (!arizona->pdata.irq_flags) {
+ irq_data = irq_get_irq_data(arizona->irq);
+ if (!irq_data) {
+ dev_err(arizona->dev, "Invalid IRQ: %d\n",
+ arizona->irq);
+ return -EINVAL;
+ }
+
+ arizona->pdata.irq_flags = irqd_get_trigger_type(irq_data);
+ switch (arizona->pdata.irq_flags) {
+ case IRQF_TRIGGER_LOW:
+ case IRQF_TRIGGER_HIGH:
+ case IRQF_TRIGGER_RISING:
+ case IRQF_TRIGGER_FALLING:
+ break;
+
+ case IRQ_TYPE_NONE:
+ default:
+ /* Device default */
+ arizona->pdata.irq_flags = IRQF_TRIGGER_LOW;
+ break;
+ }
+ }
+
+ if (arizona->pdata.irq_flags & (IRQF_TRIGGER_HIGH |
+ IRQF_TRIGGER_RISING)) {
ret = regmap_update_bits(arizona->regmap, ARIZONA_IRQ_CTRL_1,
ARIZONA_IRQ_POL, 0);
if (ret != 0) {
@@ -213,16 +258,15 @@ int arizona_irq_init(struct arizona *arizona)
ret);
goto err;
}
-
- flags |= IRQF_TRIGGER_HIGH;
- } else {
- flags |= IRQF_TRIGGER_LOW;
}
+ flags |= arizona->pdata.irq_flags;
+
/* Allocate a virtual IRQ domain to distribute to the regmap domains */
arizona->virq = irq_domain_add_linear(NULL, 2, &arizona_domain_ops,
arizona);
if (!arizona->virq) {
+ dev_err(arizona->dev, "Failed to add core IRQ domain\n");
ret = -EINVAL;
goto err;
}
@@ -241,7 +285,7 @@ int arizona_irq_init(struct arizona *arizona)
IRQF_ONESHOT, -1, irq,
&arizona->irq_chip);
if (ret != 0) {
- dev_err(arizona->dev, "Failed to add AOD IRQs: %d\n", ret);
+ dev_err(arizona->dev, "Failed to add main IRQs: %d\n", ret);
goto err_aod;
}
@@ -269,11 +313,31 @@ int arizona_irq_init(struct arizona *arizona)
}
}
+ /* Used to emulate edge trigger and to work around broken pinmux */
+ if (arizona->pdata.irq_gpio) {
+ if (gpio_to_irq(arizona->pdata.irq_gpio) != arizona->irq) {
+ dev_warn(arizona->dev, "IRQ %d is not GPIO %d (%d)\n",
+ arizona->irq, arizona->pdata.irq_gpio,
+ gpio_to_irq(arizona->pdata.irq_gpio));
+ arizona->irq = gpio_to_irq(arizona->pdata.irq_gpio);
+ }
+
+ ret = devm_gpio_request_one(arizona->dev,
+ arizona->pdata.irq_gpio,
+ GPIOF_IN, "arizona IRQ");
+ if (ret != 0) {
+ dev_err(arizona->dev,
+ "Failed to request IRQ GPIO %d:: %d\n",
+ arizona->pdata.irq_gpio, ret);
+ arizona->pdata.irq_gpio = 0;
+ }
+ }
+
ret = request_threaded_irq(arizona->irq, NULL, arizona_irq_thread,
flags, "arizona", arizona);
if (ret != 0) {
- dev_err(arizona->dev, "Failed to request IRQ %d: %d\n",
+ dev_err(arizona->dev, "Failed to request primary IRQ %d: %d\n",
arizona->irq, ret);
goto err_main_irq;
}
diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c
index df2e5a8bee2..1ca554b18be 100644
--- a/drivers/mfd/arizona-spi.c
+++ b/drivers/mfd/arizona-spi.c
@@ -17,19 +17,25 @@
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
+#include <linux/of.h>
#include <linux/mfd/arizona/core.h>
#include "arizona.h"
-static int __devinit arizona_spi_probe(struct spi_device *spi)
+static int arizona_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct arizona *arizona;
const struct regmap_config *regmap_config;
- int ret;
+ int ret, type;
- switch (id->driver_data) {
+ if (spi->dev.of_node)
+ type = arizona_of_get_type(&spi->dev);
+ else
+ type = id->driver_data;
+
+ switch (type) {
#ifdef CONFIG_MFD_WM5102
case WM5102:
regmap_config = &wm5102_spi_regmap;
@@ -65,9 +71,9 @@ static int __devinit arizona_spi_probe(struct spi_device *spi)
return arizona_dev_init(arizona);
}
-static int __devexit arizona_spi_remove(struct spi_device *spi)
+static int arizona_spi_remove(struct spi_device *spi)
{
- struct arizona *arizona = dev_get_drvdata(&spi->dev);
+ struct arizona *arizona = spi_get_drvdata(spi);
arizona_dev_exit(arizona);
return 0;
}
@@ -84,9 +90,10 @@ static struct spi_driver arizona_spi_driver = {
.name = "arizona",
.owner = THIS_MODULE,
.pm = &arizona_pm_ops,
+ .of_match_table = of_match_ptr(arizona_of_match),
},
.probe = arizona_spi_probe,
- .remove = __devexit_p(arizona_spi_remove),
+ .remove = arizona_spi_remove,
.id_table = arizona_spi_ids,
};
diff --git a/drivers/mfd/arizona.h b/drivers/mfd/arizona.h
index 9798ae5da67..b4cef777df7 100644
--- a/drivers/mfd/arizona.h
+++ b/drivers/mfd/arizona.h
@@ -13,6 +13,7 @@
#ifndef _WM5102_H
#define _WM5102_H
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/pm.h>
@@ -24,17 +25,33 @@ extern const struct regmap_config wm5102_spi_regmap;
extern const struct regmap_config wm5110_i2c_regmap;
extern const struct regmap_config wm5110_spi_regmap;
+extern const struct regmap_config wm8997_i2c_regmap;
+
extern const struct dev_pm_ops arizona_pm_ops;
+extern const struct of_device_id arizona_of_match[];
+
extern const struct regmap_irq_chip wm5102_aod;
extern const struct regmap_irq_chip wm5102_irq;
extern const struct regmap_irq_chip wm5110_aod;
extern const struct regmap_irq_chip wm5110_irq;
+extern const struct regmap_irq_chip wm8997_aod;
+extern const struct regmap_irq_chip wm8997_irq;
+
int arizona_dev_init(struct arizona *arizona);
int arizona_dev_exit(struct arizona *arizona);
int arizona_irq_init(struct arizona *arizona);
int arizona_irq_exit(struct arizona *arizona);
+#ifdef CONFIG_OF
+int arizona_of_get_type(struct device *dev);
+#else
+static inline int arizona_of_get_type(struct device *dev)
+{
+ return 0;
+}
+#endif
+
#endif
diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c
new file mode 100644
index 00000000000..d9706ede8d3
--- /dev/null
+++ b/drivers/mfd/as3711.c
@@ -0,0 +1,237 @@
+/*
+ * AS3711 PMIC MFC driver
+ *
+ * Copyright (C) 2012 Renesas Electronics Corporation
+ * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License as
+ * published by the Free Software Foundation
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mfd/as3711.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+enum {
+ AS3711_REGULATOR,
+ AS3711_BACKLIGHT,
+};
+
+/*
+ * Ok to have it static: it is only used during probing and multiple I2C devices
+ * cannot be probed simultaneously. Just make sure to avoid stale data.
+ */
+static struct mfd_cell as3711_subdevs[] = {
+ [AS3711_REGULATOR] = {.name = "as3711-regulator",},
+ [AS3711_BACKLIGHT] = {.name = "as3711-backlight",},
+};
+
+static bool as3711_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AS3711_GPIO_SIGNAL_IN:
+ case AS3711_INTERRUPT_STATUS_1:
+ case AS3711_INTERRUPT_STATUS_2:
+ case AS3711_INTERRUPT_STATUS_3:
+ case AS3711_CHARGER_STATUS_1:
+ case AS3711_CHARGER_STATUS_2:
+ case AS3711_REG_STATUS:
+ return true;
+ }
+ return false;
+}
+
+static bool as3711_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AS3711_INTERRUPT_STATUS_1:
+ case AS3711_INTERRUPT_STATUS_2:
+ case AS3711_INTERRUPT_STATUS_3:
+ return true;
+ }
+ return false;
+}
+
+static bool as3711_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AS3711_SD_1_VOLTAGE:
+ case AS3711_SD_2_VOLTAGE:
+ case AS3711_SD_3_VOLTAGE:
+ case AS3711_SD_4_VOLTAGE:
+ case AS3711_LDO_1_VOLTAGE:
+ case AS3711_LDO_2_VOLTAGE:
+ case AS3711_LDO_3_VOLTAGE:
+ case AS3711_LDO_4_VOLTAGE:
+ case AS3711_LDO_5_VOLTAGE:
+ case AS3711_LDO_6_VOLTAGE:
+ case AS3711_LDO_7_VOLTAGE:
+ case AS3711_LDO_8_VOLTAGE:
+ case AS3711_SD_CONTROL:
+ case AS3711_GPIO_SIGNAL_OUT:
+ case AS3711_GPIO_SIGNAL_IN:
+ case AS3711_SD_CONTROL_1:
+ case AS3711_SD_CONTROL_2:
+ case AS3711_CURR_CONTROL:
+ case AS3711_CURR1_VALUE:
+ case AS3711_CURR2_VALUE:
+ case AS3711_CURR3_VALUE:
+ case AS3711_STEPUP_CONTROL_1:
+ case AS3711_STEPUP_CONTROL_2:
+ case AS3711_STEPUP_CONTROL_4:
+ case AS3711_STEPUP_CONTROL_5:
+ case AS3711_REG_STATUS:
+ case AS3711_INTERRUPT_STATUS_1:
+ case AS3711_INTERRUPT_STATUS_2:
+ case AS3711_INTERRUPT_STATUS_3:
+ case AS3711_CHARGER_STATUS_1:
+ case AS3711_CHARGER_STATUS_2:
+ case AS3711_ASIC_ID_1:
+ case AS3711_ASIC_ID_2:
+ return true;
+ }
+ return false;
+}
+
+static const struct regmap_config as3711_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = as3711_volatile_reg,
+ .readable_reg = as3711_readable_reg,
+ .precious_reg = as3711_precious_reg,
+ .max_register = AS3711_MAX_REGS,
+ .num_reg_defaults_raw = AS3711_MAX_REGS,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id as3711_of_match[] = {
+ {.compatible = "ams,as3711",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, as3711_of_match);
+#endif
+
+static int as3711_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct as3711 *as3711;
+ struct as3711_platform_data *pdata;
+ unsigned int id1, id2;
+ int ret;
+
+ if (!client->dev.of_node) {
+ pdata = dev_get_platdata(&client->dev);
+ if (!pdata)
+ dev_dbg(&client->dev, "Platform data not found\n");
+ } else {
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&client->dev, "Failed to allocate pdata\n");
+ return -ENOMEM;
+ }
+ }
+
+ as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
+ if (!as3711) {
+ dev_err(&client->dev, "Memory allocation failed\n");
+ return -ENOMEM;
+ }
+
+ as3711->dev = &client->dev;
+ i2c_set_clientdata(client, as3711);
+
+ if (client->irq)
+ dev_notice(&client->dev, "IRQ not supported yet\n");
+
+ as3711->regmap = devm_regmap_init_i2c(client, &as3711_regmap_config);
+ if (IS_ERR(as3711->regmap)) {
+ ret = PTR_ERR(as3711->regmap);
+ dev_err(&client->dev, "regmap initialization failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_1, &id1);
+ if (!ret)
+ ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_2, &id2);
+ if (ret < 0) {
+ dev_err(&client->dev, "regmap_read() failed: %d\n", ret);
+ return ret;
+ }
+ if (id1 != 0x8b)
+ return -ENODEV;
+ dev_info(as3711->dev, "AS3711 detected: %x:%x\n", id1, id2);
+
+ /* We can reuse as3711_subdevs[], it will be copied in mfd_add_devices() */
+ if (pdata) {
+ as3711_subdevs[AS3711_REGULATOR].platform_data = &pdata->regulator;
+ as3711_subdevs[AS3711_REGULATOR].pdata_size = sizeof(pdata->regulator);
+ as3711_subdevs[AS3711_BACKLIGHT].platform_data = &pdata->backlight;
+ as3711_subdevs[AS3711_BACKLIGHT].pdata_size = sizeof(pdata->backlight);
+ } else {
+ as3711_subdevs[AS3711_REGULATOR].platform_data = NULL;
+ as3711_subdevs[AS3711_REGULATOR].pdata_size = 0;
+ as3711_subdevs[AS3711_BACKLIGHT].platform_data = NULL;
+ as3711_subdevs[AS3711_BACKLIGHT].pdata_size = 0;
+ }
+
+ ret = mfd_add_devices(as3711->dev, -1, as3711_subdevs,
+ ARRAY_SIZE(as3711_subdevs), NULL, 0, NULL);
+ if (ret < 0)
+ dev_err(&client->dev, "add mfd devices failed: %d\n", ret);
+
+ return ret;
+}
+
+static int as3711_i2c_remove(struct i2c_client *client)
+{
+ struct as3711 *as3711 = i2c_get_clientdata(client);
+
+ mfd_remove_devices(as3711->dev);
+ return 0;
+}
+
+static const struct i2c_device_id as3711_i2c_id[] = {
+ {.name = "as3711", .driver_data = 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, as3711_i2c_id);
+
+static struct i2c_driver as3711_i2c_driver = {
+ .driver = {
+ .name = "as3711",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(as3711_of_match),
+ },
+ .probe = as3711_i2c_probe,
+ .remove = as3711_i2c_remove,
+ .id_table = as3711_i2c_id,
+};
+
+static int __init as3711_i2c_init(void)
+{
+ return i2c_add_driver(&as3711_i2c_driver);
+}
+/* Initialise early */
+subsys_initcall(as3711_i2c_init);
+
+static void __exit as3711_i2c_exit(void)
+{
+ i2c_del_driver(&as3711_i2c_driver);
+}
+module_exit(as3711_i2c_exit);
+
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_DESCRIPTION("AS3711 PMIC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/as3722.c b/drivers/mfd/as3722.c
new file mode 100644
index 00000000000..39fa554f13b
--- /dev/null
+++ b/drivers/mfd/as3722.c
@@ -0,0 +1,453 @@
+/*
+ * Core driver for ams AS3722 PMICs
+ *
+ * Copyright (C) 2013 AMS AG
+ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
+ *
+ * Author: Florian Lobmaier <florian.lobmaier@ams.com>
+ * Author: Laxman Dewangan <ldewangan@nvidia.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/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/as3722.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define AS3722_DEVICE_ID 0x0C
+
+static const struct resource as3722_rtc_resource[] = {
+ {
+ .name = "as3722-rtc-alarm",
+ .start = AS3722_IRQ_RTC_ALARM,
+ .end = AS3722_IRQ_RTC_ALARM,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static const struct resource as3722_adc_resource[] = {
+ {
+ .name = "as3722-adc",
+ .start = AS3722_IRQ_ADC,
+ .end = AS3722_IRQ_ADC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static const struct mfd_cell as3722_devs[] = {
+ {
+ .name = "as3722-pinctrl",
+ },
+ {
+ .name = "as3722-regulator",
+ },
+ {
+ .name = "as3722-rtc",
+ .num_resources = ARRAY_SIZE(as3722_rtc_resource),
+ .resources = as3722_rtc_resource,
+ },
+ {
+ .name = "as3722-adc",
+ .num_resources = ARRAY_SIZE(as3722_adc_resource),
+ .resources = as3722_adc_resource,
+ },
+ {
+ .name = "as3722-power-off",
+ },
+ {
+ .name = "as3722-wdt",
+ },
+};
+
+static const struct regmap_irq as3722_irqs[] = {
+ /* INT1 IRQs */
+ [AS3722_IRQ_LID] = {
+ .mask = AS3722_INTERRUPT_MASK1_LID,
+ },
+ [AS3722_IRQ_ACOK] = {
+ .mask = AS3722_INTERRUPT_MASK1_ACOK,
+ },
+ [AS3722_IRQ_ENABLE1] = {
+ .mask = AS3722_INTERRUPT_MASK1_ENABLE1,
+ },
+ [AS3722_IRQ_OCCUR_ALARM_SD0] = {
+ .mask = AS3722_INTERRUPT_MASK1_OCURR_ALARM_SD0,
+ },
+ [AS3722_IRQ_ONKEY_LONG_PRESS] = {
+ .mask = AS3722_INTERRUPT_MASK1_ONKEY_LONG,
+ },
+ [AS3722_IRQ_ONKEY] = {
+ .mask = AS3722_INTERRUPT_MASK1_ONKEY,
+ },
+ [AS3722_IRQ_OVTMP] = {
+ .mask = AS3722_INTERRUPT_MASK1_OVTMP,
+ },
+ [AS3722_IRQ_LOWBAT] = {
+ .mask = AS3722_INTERRUPT_MASK1_LOWBAT,
+ },
+
+ /* INT2 IRQs */
+ [AS3722_IRQ_SD0_LV] = {
+ .mask = AS3722_INTERRUPT_MASK2_SD0_LV,
+ .reg_offset = 1,
+ },
+ [AS3722_IRQ_SD1_LV] = {
+ .mask = AS3722_INTERRUPT_MASK2_SD1_LV,
+ .reg_offset = 1,
+ },
+ [AS3722_IRQ_SD2_LV] = {
+ .mask = AS3722_INTERRUPT_MASK2_SD2345_LV,
+ .reg_offset = 1,
+ },
+ [AS3722_IRQ_PWM1_OV_PROT] = {
+ .mask = AS3722_INTERRUPT_MASK2_PWM1_OV_PROT,
+ .reg_offset = 1,
+ },
+ [AS3722_IRQ_PWM2_OV_PROT] = {
+ .mask = AS3722_INTERRUPT_MASK2_PWM2_OV_PROT,
+ .reg_offset = 1,
+ },
+ [AS3722_IRQ_ENABLE2] = {
+ .mask = AS3722_INTERRUPT_MASK2_ENABLE2,
+ .reg_offset = 1,
+ },
+ [AS3722_IRQ_SD6_LV] = {
+ .mask = AS3722_INTERRUPT_MASK2_SD6_LV,
+ .reg_offset = 1,
+ },
+ [AS3722_IRQ_RTC_REP] = {
+ .mask = AS3722_INTERRUPT_MASK2_RTC_REP,
+ .reg_offset = 1,
+ },
+
+ /* INT3 IRQs */
+ [AS3722_IRQ_RTC_ALARM] = {
+ .mask = AS3722_INTERRUPT_MASK3_RTC_ALARM,
+ .reg_offset = 2,
+ },
+ [AS3722_IRQ_GPIO1] = {
+ .mask = AS3722_INTERRUPT_MASK3_GPIO1,
+ .reg_offset = 2,
+ },
+ [AS3722_IRQ_GPIO2] = {
+ .mask = AS3722_INTERRUPT_MASK3_GPIO2,
+ .reg_offset = 2,
+ },
+ [AS3722_IRQ_GPIO3] = {
+ .mask = AS3722_INTERRUPT_MASK3_GPIO3,
+ .reg_offset = 2,
+ },
+ [AS3722_IRQ_GPIO4] = {
+ .mask = AS3722_INTERRUPT_MASK3_GPIO4,
+ .reg_offset = 2,
+ },
+ [AS3722_IRQ_GPIO5] = {
+ .mask = AS3722_INTERRUPT_MASK3_GPIO5,
+ .reg_offset = 2,
+ },
+ [AS3722_IRQ_WATCHDOG] = {
+ .mask = AS3722_INTERRUPT_MASK3_WATCHDOG,
+ .reg_offset = 2,
+ },
+ [AS3722_IRQ_ENABLE3] = {
+ .mask = AS3722_INTERRUPT_MASK3_ENABLE3,
+ .reg_offset = 2,
+ },
+
+ /* INT4 IRQs */
+ [AS3722_IRQ_TEMP_SD0_SHUTDOWN] = {
+ .mask = AS3722_INTERRUPT_MASK4_TEMP_SD0_SHUTDOWN,
+ .reg_offset = 3,
+ },
+ [AS3722_IRQ_TEMP_SD1_SHUTDOWN] = {
+ .mask = AS3722_INTERRUPT_MASK4_TEMP_SD1_SHUTDOWN,
+ .reg_offset = 3,
+ },
+ [AS3722_IRQ_TEMP_SD2_SHUTDOWN] = {
+ .mask = AS3722_INTERRUPT_MASK4_TEMP_SD6_SHUTDOWN,
+ .reg_offset = 3,
+ },
+ [AS3722_IRQ_TEMP_SD0_ALARM] = {
+ .mask = AS3722_INTERRUPT_MASK4_TEMP_SD0_ALARM,
+ .reg_offset = 3,
+ },
+ [AS3722_IRQ_TEMP_SD1_ALARM] = {
+ .mask = AS3722_INTERRUPT_MASK4_TEMP_SD1_ALARM,
+ .reg_offset = 3,
+ },
+ [AS3722_IRQ_TEMP_SD6_ALARM] = {
+ .mask = AS3722_INTERRUPT_MASK4_TEMP_SD6_ALARM,
+ .reg_offset = 3,
+ },
+ [AS3722_IRQ_OCCUR_ALARM_SD6] = {
+ .mask = AS3722_INTERRUPT_MASK4_OCCUR_ALARM_SD6,
+ .reg_offset = 3,
+ },
+ [AS3722_IRQ_ADC] = {
+ .mask = AS3722_INTERRUPT_MASK4_ADC,
+ .reg_offset = 3,
+ },
+};
+
+static const struct regmap_irq_chip as3722_irq_chip = {
+ .name = "as3722",
+ .irqs = as3722_irqs,
+ .num_irqs = ARRAY_SIZE(as3722_irqs),
+ .num_regs = 4,
+ .status_base = AS3722_INTERRUPT_STATUS1_REG,
+ .mask_base = AS3722_INTERRUPT_MASK1_REG,
+};
+
+static int as3722_check_device_id(struct as3722 *as3722)
+{
+ u32 val;
+ int ret;
+
+ /* Check that this is actually a AS3722 */
+ ret = as3722_read(as3722, AS3722_ASIC_ID1_REG, &val);
+ if (ret < 0) {
+ dev_err(as3722->dev, "ASIC_ID1 read failed: %d\n", ret);
+ return ret;
+ }
+
+ if (val != AS3722_DEVICE_ID) {
+ dev_err(as3722->dev, "Device is not AS3722, ID is 0x%x\n", val);
+ return -ENODEV;
+ }
+
+ ret = as3722_read(as3722, AS3722_ASIC_ID2_REG, &val);
+ if (ret < 0) {
+ dev_err(as3722->dev, "ASIC_ID2 read failed: %d\n", ret);
+ return ret;
+ }
+
+ dev_info(as3722->dev, "AS3722 with revision 0x%x found\n", val);
+ return 0;
+}
+
+static int as3722_configure_pullups(struct as3722 *as3722)
+{
+ int ret;
+ u32 val = 0;
+
+ if (as3722->en_intern_int_pullup)
+ val |= AS3722_INT_PULL_UP;
+ if (as3722->en_intern_i2c_pullup)
+ val |= AS3722_I2C_PULL_UP;
+
+ ret = as3722_update_bits(as3722, AS3722_IOVOLTAGE_REG,
+ AS3722_INT_PULL_UP | AS3722_I2C_PULL_UP, val);
+ if (ret < 0)
+ dev_err(as3722->dev, "IOVOLTAGE_REG update failed: %d\n", ret);
+ return ret;
+}
+
+static const struct regmap_range as3722_readable_ranges[] = {
+ regmap_reg_range(AS3722_SD0_VOLTAGE_REG, AS3722_SD6_VOLTAGE_REG),
+ regmap_reg_range(AS3722_GPIO0_CONTROL_REG, AS3722_LDO7_VOLTAGE_REG),
+ regmap_reg_range(AS3722_LDO9_VOLTAGE_REG, AS3722_REG_SEQU_MOD3_REG),
+ regmap_reg_range(AS3722_SD_PHSW_CTRL_REG, AS3722_PWM_CONTROL_H_REG),
+ regmap_reg_range(AS3722_WATCHDOG_TIMER_REG, AS3722_WATCHDOG_TIMER_REG),
+ regmap_reg_range(AS3722_WATCHDOG_SOFTWARE_SIGNAL_REG,
+ AS3722_BATTERY_VOLTAGE_MONITOR2_REG),
+ regmap_reg_range(AS3722_SD_CONTROL_REG, AS3722_PWM_VCONTROL4_REG),
+ regmap_reg_range(AS3722_BB_CHARGER_REG, AS3722_SRAM_REG),
+ regmap_reg_range(AS3722_RTC_ACCESS_REG, AS3722_RTC_ACCESS_REG),
+ regmap_reg_range(AS3722_RTC_STATUS_REG, AS3722_TEMP_STATUS_REG),
+ regmap_reg_range(AS3722_ADC0_CONTROL_REG, AS3722_ADC_CONFIGURATION_REG),
+ regmap_reg_range(AS3722_ASIC_ID1_REG, AS3722_ASIC_ID2_REG),
+ regmap_reg_range(AS3722_LOCK_REG, AS3722_LOCK_REG),
+ regmap_reg_range(AS3722_FUSE7_REG, AS3722_FUSE7_REG),
+};
+
+static const struct regmap_access_table as3722_readable_table = {
+ .yes_ranges = as3722_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(as3722_readable_ranges),
+};
+
+static const struct regmap_range as3722_writable_ranges[] = {
+ regmap_reg_range(AS3722_SD0_VOLTAGE_REG, AS3722_SD6_VOLTAGE_REG),
+ regmap_reg_range(AS3722_GPIO0_CONTROL_REG, AS3722_LDO7_VOLTAGE_REG),
+ regmap_reg_range(AS3722_LDO9_VOLTAGE_REG, AS3722_GPIO_SIGNAL_OUT_REG),
+ regmap_reg_range(AS3722_REG_SEQU_MOD1_REG, AS3722_REG_SEQU_MOD3_REG),
+ regmap_reg_range(AS3722_SD_PHSW_CTRL_REG, AS3722_PWM_CONTROL_H_REG),
+ regmap_reg_range(AS3722_WATCHDOG_TIMER_REG, AS3722_WATCHDOG_TIMER_REG),
+ regmap_reg_range(AS3722_WATCHDOG_SOFTWARE_SIGNAL_REG,
+ AS3722_BATTERY_VOLTAGE_MONITOR2_REG),
+ regmap_reg_range(AS3722_SD_CONTROL_REG, AS3722_PWM_VCONTROL4_REG),
+ regmap_reg_range(AS3722_BB_CHARGER_REG, AS3722_SRAM_REG),
+ regmap_reg_range(AS3722_INTERRUPT_MASK1_REG, AS3722_TEMP_STATUS_REG),
+ regmap_reg_range(AS3722_ADC0_CONTROL_REG, AS3722_ADC1_CONTROL_REG),
+ regmap_reg_range(AS3722_ADC1_THRESHOLD_HI_MSB_REG,
+ AS3722_ADC_CONFIGURATION_REG),
+ regmap_reg_range(AS3722_LOCK_REG, AS3722_LOCK_REG),
+};
+
+static const struct regmap_access_table as3722_writable_table = {
+ .yes_ranges = as3722_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(as3722_writable_ranges),
+};
+
+static const struct regmap_range as3722_cacheable_ranges[] = {
+ regmap_reg_range(AS3722_SD0_VOLTAGE_REG, AS3722_LDO11_VOLTAGE_REG),
+ regmap_reg_range(AS3722_SD_CONTROL_REG, AS3722_LDOCONTROL1_REG),
+};
+
+static const struct regmap_access_table as3722_volatile_table = {
+ .no_ranges = as3722_cacheable_ranges,
+ .n_no_ranges = ARRAY_SIZE(as3722_cacheable_ranges),
+};
+
+static const struct regmap_config as3722_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AS3722_MAX_REGISTER,
+ .cache_type = REGCACHE_RBTREE,
+ .rd_table = &as3722_readable_table,
+ .wr_table = &as3722_writable_table,
+ .volatile_table = &as3722_volatile_table,
+};
+
+static int as3722_i2c_of_probe(struct i2c_client *i2c,
+ struct as3722 *as3722)
+{
+ struct device_node *np = i2c->dev.of_node;
+ struct irq_data *irq_data;
+
+ if (!np) {
+ dev_err(&i2c->dev, "Device Tree not found\n");
+ return -EINVAL;
+ }
+
+ irq_data = irq_get_irq_data(i2c->irq);
+ if (!irq_data) {
+ dev_err(&i2c->dev, "Invalid IRQ: %d\n", i2c->irq);
+ return -EINVAL;
+ }
+
+ as3722->en_intern_int_pullup = of_property_read_bool(np,
+ "ams,enable-internal-int-pullup");
+ as3722->en_intern_i2c_pullup = of_property_read_bool(np,
+ "ams,enable-internal-i2c-pullup");
+ as3722->irq_flags = irqd_get_trigger_type(irq_data);
+ dev_dbg(&i2c->dev, "IRQ flags are 0x%08lx\n", as3722->irq_flags);
+ return 0;
+}
+
+static int as3722_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct as3722 *as3722;
+ unsigned long irq_flags;
+ int ret;
+
+ as3722 = devm_kzalloc(&i2c->dev, sizeof(struct as3722), GFP_KERNEL);
+ if (!as3722)
+ return -ENOMEM;
+
+ as3722->dev = &i2c->dev;
+ as3722->chip_irq = i2c->irq;
+ i2c_set_clientdata(i2c, as3722);
+
+ ret = as3722_i2c_of_probe(i2c, as3722);
+ if (ret < 0)
+ return ret;
+
+ as3722->regmap = devm_regmap_init_i2c(i2c, &as3722_regmap_config);
+ if (IS_ERR(as3722->regmap)) {
+ ret = PTR_ERR(as3722->regmap);
+ dev_err(&i2c->dev, "regmap init failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = as3722_check_device_id(as3722);
+ if (ret < 0)
+ return ret;
+
+ irq_flags = as3722->irq_flags | IRQF_ONESHOT;
+ ret = regmap_add_irq_chip(as3722->regmap, as3722->chip_irq,
+ irq_flags, -1, &as3722_irq_chip,
+ &as3722->irq_data);
+ if (ret < 0) {
+ dev_err(as3722->dev, "Failed to add regmap irq: %d\n", ret);
+ return ret;
+ }
+
+ ret = as3722_configure_pullups(as3722);
+ if (ret < 0)
+ goto scrub;
+
+ ret = mfd_add_devices(&i2c->dev, -1, as3722_devs,
+ ARRAY_SIZE(as3722_devs), NULL, 0,
+ regmap_irq_get_domain(as3722->irq_data));
+ if (ret) {
+ dev_err(as3722->dev, "Failed to add MFD devices: %d\n", ret);
+ goto scrub;
+ }
+
+ dev_dbg(as3722->dev, "AS3722 core driver initialized successfully\n");
+ return 0;
+
+scrub:
+ regmap_del_irq_chip(as3722->chip_irq, as3722->irq_data);
+ return ret;
+}
+
+static int as3722_i2c_remove(struct i2c_client *i2c)
+{
+ struct as3722 *as3722 = i2c_get_clientdata(i2c);
+
+ mfd_remove_devices(as3722->dev);
+ regmap_del_irq_chip(as3722->chip_irq, as3722->irq_data);
+ return 0;
+}
+
+static const struct of_device_id as3722_of_match[] = {
+ { .compatible = "ams,as3722", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, as3722_of_match);
+
+static const struct i2c_device_id as3722_i2c_id[] = {
+ { "as3722", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, as3722_i2c_id);
+
+static struct i2c_driver as3722_i2c_driver = {
+ .driver = {
+ .name = "as3722",
+ .owner = THIS_MODULE,
+ .of_match_table = as3722_of_match,
+ },
+ .probe = as3722_i2c_probe,
+ .remove = as3722_i2c_remove,
+ .id_table = as3722_i2c_id,
+};
+
+module_i2c_driver(as3722_i2c_driver);
+
+MODULE_DESCRIPTION("I2C support for AS3722 PMICs");
+MODULE_AUTHOR("Florian Lobmaier <florian.lobmaier@ams.com>");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index 62f0883a763..9f6294f2a07 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -695,7 +695,7 @@ static int ds1wm_disable(struct platform_device *pdev)
return 0;
}
-static struct mfd_cell asic3_cell_ds1wm = {
+static const struct mfd_cell asic3_cell_ds1wm = {
.name = "ds1wm",
.enable = ds1wm_enable,
.disable = ds1wm_disable,
@@ -797,7 +797,7 @@ static int asic3_mmc_disable(struct platform_device *pdev)
return 0;
}
-static struct mfd_cell asic3_cell_mmc = {
+static const struct mfd_cell asic3_cell_mmc = {
.name = "tmio-mmc",
.enable = asic3_mmc_enable,
.disable = asic3_mmc_disable,
@@ -952,13 +952,14 @@ static void asic3_mfd_remove(struct platform_device *pdev)
/* Core */
static int __init asic3_probe(struct platform_device *pdev)
{
- struct asic3_platform_data *pdata = pdev->dev.platform_data;
+ struct asic3_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct asic3 *asic;
struct resource *mem;
unsigned long clksel;
int ret = 0;
- asic = kzalloc(sizeof(struct asic3), GFP_KERNEL);
+ asic = devm_kzalloc(&pdev->dev,
+ sizeof(struct asic3), GFP_KERNEL);
if (asic == NULL) {
printk(KERN_ERR "kzalloc failed\n");
return -ENOMEM;
@@ -970,16 +971,14 @@ static int __init asic3_probe(struct platform_device *pdev)
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
- ret = -ENOMEM;
dev_err(asic->dev, "no MEM resource\n");
- goto out_free;
+ return -ENOMEM;
}
asic->mapping = ioremap(mem->start, resource_size(mem));
if (!asic->mapping) {
- ret = -ENOMEM;
dev_err(asic->dev, "Couldn't ioremap\n");
- goto out_free;
+ return -ENOMEM;
}
asic->irq_base = pdata->irq_base;
@@ -1033,13 +1032,10 @@ static int __init asic3_probe(struct platform_device *pdev)
out_unmap:
iounmap(asic->mapping);
- out_free:
- kfree(asic);
-
return ret;
}
-static int __devexit asic3_remove(struct platform_device *pdev)
+static int asic3_remove(struct platform_device *pdev)
{
int ret;
struct asic3 *asic = platform_get_drvdata(pdev);
@@ -1058,8 +1054,6 @@ static int __devexit asic3_remove(struct platform_device *pdev)
iounmap(asic->mapping);
- kfree(asic);
-
return 0;
}
@@ -1071,7 +1065,7 @@ static struct platform_driver asic3_device_driver = {
.driver = {
.name = "asic3",
},
- .remove = __devexit_p(asic3_remove),
+ .remove = asic3_remove,
.shutdown = asic3_shutdown,
};
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
new file mode 100644
index 00000000000..dee653989e3
--- /dev/null
+++ b/drivers/mfd/axp20x.c
@@ -0,0 +1,258 @@
+/*
+ * axp20x.c - MFD core driver for the X-Powers AXP202 and AXP209
+ *
+ * AXP20x comprises an adaptive USB-Compatible PWM charger, 2 BUCK DC-DC
+ * converters, 5 LDOs, multiple 12-bit ADCs of voltage, current and temperature
+ * as well as 4 configurable GPIOs.
+ *
+ * Author: Carlo Caione <carlo@caione.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/mfd/core.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#define AXP20X_OFF 0x80
+
+static const struct regmap_range axp20x_writeable_ranges[] = {
+ regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE),
+ regmap_reg_range(AXP20X_DCDC_MODE, AXP20X_FG_RES),
+};
+
+static const struct regmap_range axp20x_volatile_ranges[] = {
+ regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE),
+};
+
+static const struct regmap_access_table axp20x_writeable_table = {
+ .yes_ranges = axp20x_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp20x_writeable_ranges),
+};
+
+static const struct regmap_access_table axp20x_volatile_table = {
+ .yes_ranges = axp20x_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges),
+};
+
+static struct resource axp20x_pek_resources[] = {
+ {
+ .name = "PEK_DBR",
+ .start = AXP20X_IRQ_PEK_RIS_EDGE,
+ .end = AXP20X_IRQ_PEK_RIS_EDGE,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .name = "PEK_DBF",
+ .start = AXP20X_IRQ_PEK_FAL_EDGE,
+ .end = AXP20X_IRQ_PEK_FAL_EDGE,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static const struct regmap_config axp20x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &axp20x_writeable_table,
+ .volatile_table = &axp20x_volatile_table,
+ .max_register = AXP20X_FG_RES,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+#define AXP20X_IRQ(_irq, _off, _mask) \
+ [AXP20X_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) }
+
+static const struct regmap_irq axp20x_regmap_irqs[] = {
+ AXP20X_IRQ(ACIN_OVER_V, 0, 7),
+ AXP20X_IRQ(ACIN_PLUGIN, 0, 6),
+ AXP20X_IRQ(ACIN_REMOVAL, 0, 5),
+ AXP20X_IRQ(VBUS_OVER_V, 0, 4),
+ AXP20X_IRQ(VBUS_PLUGIN, 0, 3),
+ AXP20X_IRQ(VBUS_REMOVAL, 0, 2),
+ AXP20X_IRQ(VBUS_V_LOW, 0, 1),
+ AXP20X_IRQ(BATT_PLUGIN, 1, 7),
+ AXP20X_IRQ(BATT_REMOVAL, 1, 6),
+ AXP20X_IRQ(BATT_ENT_ACT_MODE, 1, 5),
+ AXP20X_IRQ(BATT_EXIT_ACT_MODE, 1, 4),
+ AXP20X_IRQ(CHARG, 1, 3),
+ AXP20X_IRQ(CHARG_DONE, 1, 2),
+ AXP20X_IRQ(BATT_TEMP_HIGH, 1, 1),
+ AXP20X_IRQ(BATT_TEMP_LOW, 1, 0),
+ AXP20X_IRQ(DIE_TEMP_HIGH, 2, 7),
+ AXP20X_IRQ(CHARG_I_LOW, 2, 6),
+ AXP20X_IRQ(DCDC1_V_LONG, 2, 5),
+ AXP20X_IRQ(DCDC2_V_LONG, 2, 4),
+ AXP20X_IRQ(DCDC3_V_LONG, 2, 3),
+ AXP20X_IRQ(PEK_SHORT, 2, 1),
+ AXP20X_IRQ(PEK_LONG, 2, 0),
+ AXP20X_IRQ(N_OE_PWR_ON, 3, 7),
+ AXP20X_IRQ(N_OE_PWR_OFF, 3, 6),
+ AXP20X_IRQ(VBUS_VALID, 3, 5),
+ AXP20X_IRQ(VBUS_NOT_VALID, 3, 4),
+ AXP20X_IRQ(VBUS_SESS_VALID, 3, 3),
+ AXP20X_IRQ(VBUS_SESS_END, 3, 2),
+ AXP20X_IRQ(LOW_PWR_LVL1, 3, 1),
+ AXP20X_IRQ(LOW_PWR_LVL2, 3, 0),
+ AXP20X_IRQ(TIMER, 4, 7),
+ AXP20X_IRQ(PEK_RIS_EDGE, 4, 6),
+ AXP20X_IRQ(PEK_FAL_EDGE, 4, 5),
+ AXP20X_IRQ(GPIO3_INPUT, 4, 3),
+ AXP20X_IRQ(GPIO2_INPUT, 4, 2),
+ AXP20X_IRQ(GPIO1_INPUT, 4, 1),
+ AXP20X_IRQ(GPIO0_INPUT, 4, 0),
+};
+
+static const struct of_device_id axp20x_of_match[] = {
+ { .compatible = "x-powers,axp202", .data = (void *) AXP202_ID },
+ { .compatible = "x-powers,axp209", .data = (void *) AXP209_ID },
+ { },
+};
+MODULE_DEVICE_TABLE(of, axp20x_of_match);
+
+/*
+ * This is useless for OF-enabled devices, but it is needed by I2C subsystem
+ */
+static const struct i2c_device_id axp20x_i2c_id[] = {
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id);
+
+static const struct regmap_irq_chip axp20x_regmap_irq_chip = {
+ .name = "axp20x_irq_chip",
+ .status_base = AXP20X_IRQ1_STATE,
+ .ack_base = AXP20X_IRQ1_STATE,
+ .mask_base = AXP20X_IRQ1_EN,
+ .num_regs = 5,
+ .irqs = axp20x_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(axp20x_regmap_irqs),
+ .mask_invert = true,
+ .init_ack_masked = true,
+};
+
+static const char * const axp20x_supplies[] = {
+ "acin",
+ "vin2",
+ "vin3",
+ "ldo24in",
+ "ldo3in",
+ "ldo5in",
+};
+
+static struct mfd_cell axp20x_cells[] = {
+ {
+ .name = "axp20x-pek",
+ .num_resources = ARRAY_SIZE(axp20x_pek_resources),
+ .resources = axp20x_pek_resources,
+ }, {
+ .name = "axp20x-regulator",
+ .parent_supplies = axp20x_supplies,
+ .num_parent_supplies = ARRAY_SIZE(axp20x_supplies),
+ },
+};
+
+static struct axp20x_dev *axp20x_pm_power_off;
+static void axp20x_power_off(void)
+{
+ regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL,
+ AXP20X_OFF);
+}
+
+static int axp20x_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct axp20x_dev *axp20x;
+ const struct of_device_id *of_id;
+ int ret;
+
+ axp20x = devm_kzalloc(&i2c->dev, sizeof(*axp20x), GFP_KERNEL);
+ if (!axp20x)
+ return -ENOMEM;
+
+ of_id = of_match_device(axp20x_of_match, &i2c->dev);
+ if (!of_id) {
+ dev_err(&i2c->dev, "Unable to setup AXP20X data\n");
+ return -ENODEV;
+ }
+ axp20x->variant = (long) of_id->data;
+
+ axp20x->i2c_client = i2c;
+ axp20x->dev = &i2c->dev;
+ dev_set_drvdata(axp20x->dev, axp20x);
+
+ axp20x->regmap = devm_regmap_init_i2c(i2c, &axp20x_regmap_config);
+ if (IS_ERR(axp20x->regmap)) {
+ ret = PTR_ERR(axp20x->regmap);
+ dev_err(&i2c->dev, "regmap init failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_add_irq_chip(axp20x->regmap, i2c->irq,
+ IRQF_ONESHOT | IRQF_SHARED, -1,
+ &axp20x_regmap_irq_chip,
+ &axp20x->regmap_irqc);
+ if (ret) {
+ dev_err(&i2c->dev, "failed to add irq chip: %d\n", ret);
+ return ret;
+ }
+
+ ret = mfd_add_devices(axp20x->dev, -1, axp20x_cells,
+ ARRAY_SIZE(axp20x_cells), NULL, 0, NULL);
+
+ if (ret) {
+ dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret);
+ regmap_del_irq_chip(i2c->irq, axp20x->regmap_irqc);
+ return ret;
+ }
+
+ if (!pm_power_off) {
+ axp20x_pm_power_off = axp20x;
+ pm_power_off = axp20x_power_off;
+ }
+
+ dev_info(&i2c->dev, "AXP20X driver loaded\n");
+
+ return 0;
+}
+
+static int axp20x_i2c_remove(struct i2c_client *i2c)
+{
+ struct axp20x_dev *axp20x = i2c_get_clientdata(i2c);
+
+ if (axp20x == axp20x_pm_power_off) {
+ axp20x_pm_power_off = NULL;
+ pm_power_off = NULL;
+ }
+
+ mfd_remove_devices(axp20x->dev);
+ regmap_del_irq_chip(axp20x->i2c_client->irq, axp20x->regmap_irqc);
+
+ return 0;
+}
+
+static struct i2c_driver axp20x_i2c_driver = {
+ .driver = {
+ .name = "axp20x",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(axp20x_of_match),
+ },
+ .probe = axp20x_i2c_probe,
+ .remove = axp20x_i2c_remove,
+ .id_table = axp20x_i2c_id,
+};
+
+module_i2c_driver(axp20x_i2c_driver);
+
+MODULE_DESCRIPTION("PMIC MFD core driver for AXP20X");
+MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/bcm590xx.c b/drivers/mfd/bcm590xx.c
new file mode 100644
index 00000000000..e334de000e8
--- /dev/null
+++ b/drivers/mfd/bcm590xx.c
@@ -0,0 +1,132 @@
+/*
+ * Broadcom BCM590xx PMU
+ *
+ * Copyright 2014 Linaro Limited
+ * Author: Matt Porter <mporter@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/mfd/bcm590xx.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+static const struct mfd_cell bcm590xx_devs[] = {
+ {
+ .name = "bcm590xx-vregs",
+ },
+};
+
+static const struct regmap_config bcm590xx_regmap_config_pri = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = BCM590XX_MAX_REGISTER_PRI,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct regmap_config bcm590xx_regmap_config_sec = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = BCM590XX_MAX_REGISTER_SEC,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int bcm590xx_i2c_probe(struct i2c_client *i2c_pri,
+ const struct i2c_device_id *id)
+{
+ struct bcm590xx *bcm590xx;
+ int ret;
+
+ bcm590xx = devm_kzalloc(&i2c_pri->dev, sizeof(*bcm590xx), GFP_KERNEL);
+ if (!bcm590xx)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_pri, bcm590xx);
+ bcm590xx->dev = &i2c_pri->dev;
+ bcm590xx->i2c_pri = i2c_pri;
+
+ bcm590xx->regmap_pri = devm_regmap_init_i2c(i2c_pri,
+ &bcm590xx_regmap_config_pri);
+ if (IS_ERR(bcm590xx->regmap_pri)) {
+ ret = PTR_ERR(bcm590xx->regmap_pri);
+ dev_err(&i2c_pri->dev, "primary regmap init failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Secondary I2C slave address is the base address with A(2) asserted */
+ bcm590xx->i2c_sec = i2c_new_dummy(i2c_pri->adapter,
+ i2c_pri->addr | BIT(2));
+ if (IS_ERR_OR_NULL(bcm590xx->i2c_sec)) {
+ dev_err(&i2c_pri->dev, "failed to add secondary I2C device\n");
+ return -ENODEV;
+ }
+ i2c_set_clientdata(bcm590xx->i2c_sec, bcm590xx);
+
+ bcm590xx->regmap_sec = devm_regmap_init_i2c(bcm590xx->i2c_sec,
+ &bcm590xx_regmap_config_sec);
+ if (IS_ERR(bcm590xx->regmap_sec)) {
+ ret = PTR_ERR(bcm590xx->regmap_sec);
+ dev_err(&bcm590xx->i2c_sec->dev,
+ "secondary regmap init failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = mfd_add_devices(&i2c_pri->dev, -1, bcm590xx_devs,
+ ARRAY_SIZE(bcm590xx_devs), NULL, 0, NULL);
+ if (ret < 0) {
+ dev_err(&i2c_pri->dev, "failed to add sub-devices: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ i2c_unregister_device(bcm590xx->i2c_sec);
+ return ret;
+}
+
+static int bcm590xx_i2c_remove(struct i2c_client *i2c)
+{
+ mfd_remove_devices(&i2c->dev);
+ return 0;
+}
+
+static const struct of_device_id bcm590xx_of_match[] = {
+ { .compatible = "brcm,bcm59056" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bcm590xx_of_match);
+
+static const struct i2c_device_id bcm590xx_i2c_id[] = {
+ { "bcm59056" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bcm590xx_i2c_id);
+
+static struct i2c_driver bcm590xx_i2c_driver = {
+ .driver = {
+ .name = "bcm590xx",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(bcm590xx_of_match),
+ },
+ .probe = bcm590xx_i2c_probe,
+ .remove = bcm590xx_i2c_remove,
+ .id_table = bcm590xx_i2c_id,
+};
+module_i2c_driver(bcm590xx_i2c_driver);
+
+MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
+MODULE_DESCRIPTION("BCM590xx multi-function driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:bcm590xx");
diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c
new file mode 100644
index 00000000000..38fe9bf0d16
--- /dev/null
+++ b/drivers/mfd/cros_ec.c
@@ -0,0 +1,194 @@
+/*
+ * ChromeOS EC multi-function device
+ *
+ * Copyright (C) 2012 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ * The ChromeOS EC multi function device is used to mux all the requests
+ * to the EC device for its multiple features: keyboard controller,
+ * battery charging and regulator control, firmware update.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+
+int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
+ struct cros_ec_msg *msg)
+{
+ uint8_t *out;
+ int csum, i;
+
+ BUG_ON(msg->out_len > EC_PROTO2_MAX_PARAM_SIZE);
+ out = ec_dev->dout;
+ out[0] = EC_CMD_VERSION0 + msg->version;
+ out[1] = msg->cmd;
+ out[2] = msg->out_len;
+ csum = out[0] + out[1] + out[2];
+ for (i = 0; i < msg->out_len; i++)
+ csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->out_buf[i];
+ out[EC_MSG_TX_HEADER_BYTES + msg->out_len] = (uint8_t)(csum & 0xff);
+
+ return EC_MSG_TX_PROTO_BYTES + msg->out_len;
+}
+EXPORT_SYMBOL(cros_ec_prepare_tx);
+
+static int cros_ec_command_sendrecv(struct cros_ec_device *ec_dev,
+ uint16_t cmd, void *out_buf, int out_len,
+ void *in_buf, int in_len)
+{
+ struct cros_ec_msg msg;
+
+ msg.version = cmd >> 8;
+ msg.cmd = cmd & 0xff;
+ msg.out_buf = out_buf;
+ msg.out_len = out_len;
+ msg.in_buf = in_buf;
+ msg.in_len = in_len;
+
+ return ec_dev->command_xfer(ec_dev, &msg);
+}
+
+static int cros_ec_command_recv(struct cros_ec_device *ec_dev,
+ uint16_t cmd, void *buf, int buf_len)
+{
+ return cros_ec_command_sendrecv(ec_dev, cmd, NULL, 0, buf, buf_len);
+}
+
+static int cros_ec_command_send(struct cros_ec_device *ec_dev,
+ uint16_t cmd, void *buf, int buf_len)
+{
+ return cros_ec_command_sendrecv(ec_dev, cmd, buf, buf_len, NULL, 0);
+}
+
+static irqreturn_t ec_irq_thread(int irq, void *data)
+{
+ struct cros_ec_device *ec_dev = data;
+
+ if (device_may_wakeup(ec_dev->dev))
+ pm_wakeup_event(ec_dev->dev, 0);
+
+ blocking_notifier_call_chain(&ec_dev->event_notifier, 1, ec_dev);
+
+ return IRQ_HANDLED;
+}
+
+static const struct mfd_cell cros_devs[] = {
+ {
+ .name = "cros-ec-keyb",
+ .id = 1,
+ .of_compatible = "google,cros-ec-keyb",
+ },
+ {
+ .name = "cros-ec-i2c-tunnel",
+ .id = 2,
+ .of_compatible = "google,cros-ec-i2c-tunnel",
+ },
+};
+
+int cros_ec_register(struct cros_ec_device *ec_dev)
+{
+ struct device *dev = ec_dev->dev;
+ int err = 0;
+
+ BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier);
+
+ ec_dev->command_send = cros_ec_command_send;
+ ec_dev->command_recv = cros_ec_command_recv;
+ ec_dev->command_sendrecv = cros_ec_command_sendrecv;
+
+ if (ec_dev->din_size) {
+ ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
+ if (!ec_dev->din)
+ return -ENOMEM;
+ }
+ if (ec_dev->dout_size) {
+ ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL);
+ if (!ec_dev->dout)
+ return -ENOMEM;
+ }
+
+ if (!ec_dev->irq) {
+ dev_dbg(dev, "no valid IRQ: %d\n", ec_dev->irq);
+ return err;
+ }
+
+ err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "chromeos-ec", ec_dev);
+ if (err) {
+ dev_err(dev, "request irq %d: error %d\n", ec_dev->irq, err);
+ return err;
+ }
+
+ err = mfd_add_devices(dev, 0, cros_devs,
+ ARRAY_SIZE(cros_devs),
+ NULL, ec_dev->irq, NULL);
+ if (err) {
+ dev_err(dev, "failed to add mfd devices\n");
+ goto fail_mfd;
+ }
+
+ dev_info(dev, "Chrome EC (%s)\n", ec_dev->name);
+
+ return 0;
+
+fail_mfd:
+ free_irq(ec_dev->irq, ec_dev);
+
+ return err;
+}
+EXPORT_SYMBOL(cros_ec_register);
+
+int cros_ec_remove(struct cros_ec_device *ec_dev)
+{
+ mfd_remove_devices(ec_dev->dev);
+ free_irq(ec_dev->irq, ec_dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(cros_ec_remove);
+
+#ifdef CONFIG_PM_SLEEP
+int cros_ec_suspend(struct cros_ec_device *ec_dev)
+{
+ struct device *dev = ec_dev->dev;
+
+ if (device_may_wakeup(dev))
+ ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq);
+
+ disable_irq(ec_dev->irq);
+ ec_dev->was_wake_device = ec_dev->wake_enabled;
+
+ return 0;
+}
+EXPORT_SYMBOL(cros_ec_suspend);
+
+int cros_ec_resume(struct cros_ec_device *ec_dev)
+{
+ enable_irq(ec_dev->irq);
+
+ if (ec_dev->wake_enabled) {
+ disable_irq_wake(ec_dev->irq);
+ ec_dev->wake_enabled = 0;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(cros_ec_resume);
+
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ChromeOS EC core driver");
diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c
new file mode 100644
index 00000000000..4f71be99a18
--- /dev/null
+++ b/drivers/mfd/cros_ec_i2c.c
@@ -0,0 +1,201 @@
+/*
+ * ChromeOS EC multi-function device (I2C)
+ *
+ * Copyright (C) 2012 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+static inline struct cros_ec_device *to_ec_dev(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ return i2c_get_clientdata(client);
+}
+
+static int cros_ec_command_xfer(struct cros_ec_device *ec_dev,
+ struct cros_ec_msg *msg)
+{
+ struct i2c_client *client = ec_dev->priv;
+ int ret = -ENOMEM;
+ int i;
+ int packet_len;
+ u8 *out_buf = NULL;
+ u8 *in_buf = NULL;
+ u8 sum;
+ struct i2c_msg i2c_msg[2];
+
+ i2c_msg[0].addr = client->addr;
+ i2c_msg[0].flags = 0;
+ i2c_msg[1].addr = client->addr;
+ i2c_msg[1].flags = I2C_M_RD;
+
+ /*
+ * allocate larger packet (one byte for checksum, one byte for
+ * length, and one for result code)
+ */
+ packet_len = msg->in_len + 3;
+ in_buf = kzalloc(packet_len, GFP_KERNEL);
+ if (!in_buf)
+ goto done;
+ i2c_msg[1].len = packet_len;
+ i2c_msg[1].buf = (char *)in_buf;
+
+ /*
+ * allocate larger packet (one byte for checksum, one for
+ * command code, one for length, and one for command version)
+ */
+ packet_len = msg->out_len + 4;
+ out_buf = kzalloc(packet_len, GFP_KERNEL);
+ if (!out_buf)
+ goto done;
+ i2c_msg[0].len = packet_len;
+ i2c_msg[0].buf = (char *)out_buf;
+
+ out_buf[0] = EC_CMD_VERSION0 + msg->version;
+ out_buf[1] = msg->cmd;
+ out_buf[2] = msg->out_len;
+
+ /* copy message payload and compute checksum */
+ sum = out_buf[0] + out_buf[1] + out_buf[2];
+ for (i = 0; i < msg->out_len; i++) {
+ out_buf[3 + i] = msg->out_buf[i];
+ sum += out_buf[3 + i];
+ }
+ out_buf[3 + msg->out_len] = sum;
+
+ /* send command to EC and read answer */
+ ret = i2c_transfer(client->adapter, i2c_msg, 2);
+ if (ret < 0) {
+ dev_err(ec_dev->dev, "i2c transfer failed: %d\n", ret);
+ goto done;
+ } else if (ret != 2) {
+ dev_err(ec_dev->dev, "failed to get response: %d\n", ret);
+ ret = -EIO;
+ goto done;
+ }
+
+ /* check response error code */
+ if (i2c_msg[1].buf[0]) {
+ dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n",
+ msg->cmd, i2c_msg[1].buf[0]);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* copy response packet payload and compute checksum */
+ sum = in_buf[0] + in_buf[1];
+ for (i = 0; i < msg->in_len; i++) {
+ msg->in_buf[i] = in_buf[2 + i];
+ sum += in_buf[2 + i];
+ }
+ dev_dbg(ec_dev->dev, "packet: %*ph, sum = %02x\n",
+ i2c_msg[1].len, in_buf, sum);
+ if (sum != in_buf[2 + msg->in_len]) {
+ dev_err(ec_dev->dev, "bad packet checksum\n");
+ ret = -EBADMSG;
+ goto done;
+ }
+
+ ret = 0;
+ done:
+ kfree(in_buf);
+ kfree(out_buf);
+ return ret;
+}
+
+static int cros_ec_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ struct device *dev = &client->dev;
+ struct cros_ec_device *ec_dev = NULL;
+ int err;
+
+ ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
+ if (!ec_dev)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, ec_dev);
+ ec_dev->name = "I2C";
+ ec_dev->dev = dev;
+ ec_dev->priv = client;
+ ec_dev->irq = client->irq;
+ ec_dev->command_xfer = cros_ec_command_xfer;
+ ec_dev->ec_name = client->name;
+ ec_dev->phys_name = client->adapter->name;
+ ec_dev->parent = &client->dev;
+
+ err = cros_ec_register(ec_dev);
+ if (err) {
+ dev_err(dev, "cannot register EC\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int cros_ec_i2c_remove(struct i2c_client *client)
+{
+ struct cros_ec_device *ec_dev = i2c_get_clientdata(client);
+
+ cros_ec_remove(ec_dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cros_ec_i2c_suspend(struct device *dev)
+{
+ struct cros_ec_device *ec_dev = to_ec_dev(dev);
+
+ return cros_ec_suspend(ec_dev);
+}
+
+static int cros_ec_i2c_resume(struct device *dev)
+{
+ struct cros_ec_device *ec_dev = to_ec_dev(dev);
+
+ return cros_ec_resume(ec_dev);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(cros_ec_i2c_pm_ops, cros_ec_i2c_suspend,
+ cros_ec_i2c_resume);
+
+static const struct i2c_device_id cros_ec_i2c_id[] = {
+ { "cros-ec-i2c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cros_ec_i2c_id);
+
+static struct i2c_driver cros_ec_driver = {
+ .driver = {
+ .name = "cros-ec-i2c",
+ .owner = THIS_MODULE,
+ .pm = &cros_ec_i2c_pm_ops,
+ },
+ .probe = cros_ec_i2c_probe,
+ .remove = cros_ec_i2c_remove,
+ .id_table = cros_ec_i2c_id,
+};
+
+module_i2c_driver(cros_ec_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ChromeOS EC multi function device");
diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c
new file mode 100644
index 00000000000..0b8d3282916
--- /dev/null
+++ b/drivers/mfd/cros_ec_spi.c
@@ -0,0 +1,440 @@
+/*
+ * ChromeOS EC multi-function device (SPI)
+ *
+ * Copyright (C) 2012 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+
+/* The header byte, which follows the preamble */
+#define EC_MSG_HEADER 0xec
+
+/*
+ * Number of EC preamble bytes we read at a time. Since it takes
+ * about 400-500us for the EC to respond there is not a lot of
+ * point in tuning this. If the EC could respond faster then
+ * we could increase this so that might expect the preamble and
+ * message to occur in a single transaction. However, the maximum
+ * SPI transfer size is 256 bytes, so at 5MHz we need a response
+ * time of perhaps <320us (200 bytes / 1600 bits).
+ */
+#define EC_MSG_PREAMBLE_COUNT 32
+
+/*
+ * Allow for a long time for the EC to respond. We support i2c
+ * tunneling and support fairly long messages for the tunnel (249
+ * bytes long at the moment). If we're talking to a 100 kHz device
+ * on the other end and need to transfer ~256 bytes, then we need:
+ * 10 us/bit * ~10 bits/byte * ~256 bytes = ~25ms
+ *
+ * We'll wait 4 times that to handle clock stretching and other
+ * paranoia.
+ *
+ * It's pretty unlikely that we'll really see a 249 byte tunnel in
+ * anything other than testing. If this was more common we might
+ * consider having slow commands like this require a GET_STATUS
+ * wait loop. The 'flash write' command would be another candidate
+ * for this, clocking in at 2-3ms.
+ */
+#define EC_MSG_DEADLINE_MS 100
+
+/*
+ * Time between raising the SPI chip select (for the end of a
+ * transaction) and dropping it again (for the next transaction).
+ * If we go too fast, the EC will miss the transaction. We know that we
+ * need at least 70 us with the 16 MHz STM32 EC, so go with 200 us to be
+ * safe.
+ */
+#define EC_SPI_RECOVERY_TIME_NS (200 * 1000)
+
+/**
+ * struct cros_ec_spi - information about a SPI-connected EC
+ *
+ * @spi: SPI device we are connected to
+ * @last_transfer_ns: time that we last finished a transfer, or 0 if there
+ * if no record
+ * @end_of_msg_delay: used to set the delay_usecs on the spi_transfer that
+ * is sent when we want to turn off CS at the end of a transaction.
+ * @lock: mutex to ensure only one user of cros_ec_command_spi_xfer at a time
+ */
+struct cros_ec_spi {
+ struct spi_device *spi;
+ s64 last_transfer_ns;
+ unsigned int end_of_msg_delay;
+ struct mutex lock;
+};
+
+static void debug_packet(struct device *dev, const char *name, u8 *ptr,
+ int len)
+{
+#ifdef DEBUG
+ int i;
+
+ dev_dbg(dev, "%s: ", name);
+ for (i = 0; i < len; i++)
+ pr_cont(" %02x", ptr[i]);
+
+ pr_cont("\n");
+#endif
+}
+
+/**
+ * cros_ec_spi_receive_response - Receive a response from the EC.
+ *
+ * This function has two phases: reading the preamble bytes (since if we read
+ * data from the EC before it is ready to send, we just get preamble) and
+ * reading the actual message.
+ *
+ * The received data is placed into ec_dev->din.
+ *
+ * @ec_dev: ChromeOS EC device
+ * @need_len: Number of message bytes we need to read
+ */
+static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
+ int need_len)
+{
+ struct cros_ec_spi *ec_spi = ec_dev->priv;
+ struct spi_transfer trans;
+ struct spi_message msg;
+ u8 *ptr, *end;
+ int ret;
+ unsigned long deadline;
+ int todo;
+
+ /* Receive data until we see the header byte */
+ deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS);
+ while (true) {
+ unsigned long start_jiffies = jiffies;
+
+ memset(&trans, 0, sizeof(trans));
+ trans.cs_change = 1;
+ trans.rx_buf = ptr = ec_dev->din;
+ trans.len = EC_MSG_PREAMBLE_COUNT;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&trans, &msg);
+ ret = spi_sync(ec_spi->spi, &msg);
+ if (ret < 0) {
+ dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+ return ret;
+ }
+
+ for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) {
+ if (*ptr == EC_MSG_HEADER) {
+ dev_dbg(ec_dev->dev, "msg found at %zd\n",
+ ptr - ec_dev->din);
+ break;
+ }
+ }
+ if (ptr != end)
+ break;
+
+ /*
+ * Use the time at the start of the loop as a timeout. This
+ * gives us one last shot at getting the transfer and is useful
+ * in case we got context switched out for a while.
+ */
+ if (time_after(start_jiffies, deadline)) {
+ dev_warn(ec_dev->dev, "EC failed to respond in time\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ /*
+ * ptr now points to the header byte. Copy any valid data to the
+ * start of our buffer
+ */
+ todo = end - ++ptr;
+ BUG_ON(todo < 0 || todo > ec_dev->din_size);
+ todo = min(todo, need_len);
+ memmove(ec_dev->din, ptr, todo);
+ ptr = ec_dev->din + todo;
+ dev_dbg(ec_dev->dev, "need %d, got %d bytes from preamble\n",
+ need_len, todo);
+ need_len -= todo;
+
+ /* Receive data until we have it all */
+ while (need_len > 0) {
+ /*
+ * We can't support transfers larger than the SPI FIFO size
+ * unless we have DMA. We don't have DMA on the ISP SPI ports
+ * for Exynos. We need a way of asking SPI driver for
+ * maximum-supported transfer size.
+ */
+ todo = min(need_len, 256);
+ dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%zd\n",
+ todo, need_len, ptr - ec_dev->din);
+
+ memset(&trans, 0, sizeof(trans));
+ trans.cs_change = 1;
+ trans.rx_buf = ptr;
+ trans.len = todo;
+ spi_message_init(&msg);
+ spi_message_add_tail(&trans, &msg);
+
+ /* send command to EC and read answer */
+ BUG_ON((u8 *)trans.rx_buf - ec_dev->din + todo >
+ ec_dev->din_size);
+ ret = spi_sync(ec_spi->spi, &msg);
+ if (ret < 0) {
+ dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+ return ret;
+ }
+
+ debug_packet(ec_dev->dev, "interim", ptr, todo);
+ ptr += todo;
+ need_len -= todo;
+ }
+
+ dev_dbg(ec_dev->dev, "loop done, ptr=%zd\n", ptr - ec_dev->din);
+
+ return 0;
+}
+
+/**
+ * cros_ec_command_spi_xfer - Transfer a message over SPI and receive the reply
+ *
+ * @ec_dev: ChromeOS EC device
+ * @ec_msg: Message to transfer
+ */
+static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev,
+ struct cros_ec_msg *ec_msg)
+{
+ struct cros_ec_spi *ec_spi = ec_dev->priv;
+ struct spi_transfer trans;
+ struct spi_message msg;
+ int i, len;
+ u8 *ptr;
+ int sum;
+ int ret = 0, final_ret;
+ struct timespec ts;
+
+ /*
+ * We have the shared ec_dev buffer plus we do lots of separate spi_sync
+ * calls, so we need to make sure only one person is using this at a
+ * time.
+ */
+ mutex_lock(&ec_spi->lock);
+
+ len = cros_ec_prepare_tx(ec_dev, ec_msg);
+ dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);
+
+ /* If it's too soon to do another transaction, wait */
+ if (ec_spi->last_transfer_ns) {
+ struct timespec ts;
+ unsigned long delay; /* The delay completed so far */
+
+ ktime_get_ts(&ts);
+ delay = timespec_to_ns(&ts) - ec_spi->last_transfer_ns;
+ if (delay < EC_SPI_RECOVERY_TIME_NS)
+ ndelay(EC_SPI_RECOVERY_TIME_NS - delay);
+ }
+
+ /* Transmit phase - send our message */
+ debug_packet(ec_dev->dev, "out", ec_dev->dout, len);
+ memset(&trans, 0, sizeof(trans));
+ trans.tx_buf = ec_dev->dout;
+ trans.len = len;
+ trans.cs_change = 1;
+ spi_message_init(&msg);
+ spi_message_add_tail(&trans, &msg);
+ ret = spi_sync(ec_spi->spi, &msg);
+
+ /* Get the response */
+ if (!ret) {
+ ret = cros_ec_spi_receive_response(ec_dev,
+ ec_msg->in_len + EC_MSG_TX_PROTO_BYTES);
+ } else {
+ dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+ }
+
+ /* turn off CS */
+ spi_message_init(&msg);
+
+ if (ec_spi->end_of_msg_delay) {
+ /*
+ * Add delay for last transaction, to ensure the rising edge
+ * doesn't come too soon after the end of the data.
+ */
+ memset(&trans, 0, sizeof(trans));
+ trans.delay_usecs = ec_spi->end_of_msg_delay;
+ spi_message_add_tail(&trans, &msg);
+ }
+
+ final_ret = spi_sync(ec_spi->spi, &msg);
+ ktime_get_ts(&ts);
+ ec_spi->last_transfer_ns = timespec_to_ns(&ts);
+ if (!ret)
+ ret = final_ret;
+ if (ret < 0) {
+ dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+ goto exit;
+ }
+
+ /* check response error code */
+ ptr = ec_dev->din;
+ if (ptr[0]) {
+ dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n",
+ ec_msg->cmd, ptr[0]);
+ debug_packet(ec_dev->dev, "in_err", ptr, len);
+ ret = -EINVAL;
+ goto exit;
+ }
+ len = ptr[1];
+ sum = ptr[0] + ptr[1];
+ if (len > ec_msg->in_len) {
+ dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
+ len, ec_msg->in_len);
+ ret = -ENOSPC;
+ goto exit;
+ }
+
+ /* copy response packet payload and compute checksum */
+ for (i = 0; i < len; i++) {
+ sum += ptr[i + 2];
+ if (ec_msg->in_len)
+ ec_msg->in_buf[i] = ptr[i + 2];
+ }
+ sum &= 0xff;
+
+ debug_packet(ec_dev->dev, "in", ptr, len + 3);
+
+ if (sum != ptr[len + 2]) {
+ dev_err(ec_dev->dev,
+ "bad packet checksum, expected %02x, got %02x\n",
+ sum, ptr[len + 2]);
+ ret = -EBADMSG;
+ goto exit;
+ }
+
+ ret = 0;
+exit:
+ mutex_unlock(&ec_spi->lock);
+ return ret;
+}
+
+static void cros_ec_spi_dt_probe(struct cros_ec_spi *ec_spi, struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ u32 val;
+ int ret;
+
+ ret = of_property_read_u32(np, "google,cros-ec-spi-msg-delay", &val);
+ if (!ret)
+ ec_spi->end_of_msg_delay = val;
+}
+
+static int cros_ec_spi_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct cros_ec_device *ec_dev;
+ struct cros_ec_spi *ec_spi;
+ int err;
+
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_0;
+ err = spi_setup(spi);
+ if (err < 0)
+ return err;
+
+ ec_spi = devm_kzalloc(dev, sizeof(*ec_spi), GFP_KERNEL);
+ if (ec_spi == NULL)
+ return -ENOMEM;
+ ec_spi->spi = spi;
+ mutex_init(&ec_spi->lock);
+ ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
+ if (!ec_dev)
+ return -ENOMEM;
+
+ /* Check for any DT properties */
+ cros_ec_spi_dt_probe(ec_spi, dev);
+
+ spi_set_drvdata(spi, ec_dev);
+ ec_dev->name = "SPI";
+ ec_dev->dev = dev;
+ ec_dev->priv = ec_spi;
+ ec_dev->irq = spi->irq;
+ ec_dev->command_xfer = cros_ec_command_spi_xfer;
+ ec_dev->ec_name = ec_spi->spi->modalias;
+ ec_dev->phys_name = dev_name(&ec_spi->spi->dev);
+ ec_dev->parent = &ec_spi->spi->dev;
+ ec_dev->din_size = EC_MSG_BYTES + EC_MSG_PREAMBLE_COUNT;
+ ec_dev->dout_size = EC_MSG_BYTES;
+
+ err = cros_ec_register(ec_dev);
+ if (err) {
+ dev_err(dev, "cannot register EC\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int cros_ec_spi_remove(struct spi_device *spi)
+{
+ struct cros_ec_device *ec_dev;
+
+ ec_dev = spi_get_drvdata(spi);
+ cros_ec_remove(ec_dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cros_ec_spi_suspend(struct device *dev)
+{
+ struct cros_ec_device *ec_dev = dev_get_drvdata(dev);
+
+ return cros_ec_suspend(ec_dev);
+}
+
+static int cros_ec_spi_resume(struct device *dev)
+{
+ struct cros_ec_device *ec_dev = dev_get_drvdata(dev);
+
+ return cros_ec_resume(ec_dev);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(cros_ec_spi_pm_ops, cros_ec_spi_suspend,
+ cros_ec_spi_resume);
+
+static const struct spi_device_id cros_ec_spi_id[] = {
+ { "cros-ec-spi", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, cros_ec_spi_id);
+
+static struct spi_driver cros_ec_driver_spi = {
+ .driver = {
+ .name = "cros-ec-spi",
+ .owner = THIS_MODULE,
+ .pm = &cros_ec_spi_pm_ops,
+ },
+ .probe = cros_ec_spi_probe,
+ .remove = cros_ec_spi_remove,
+ .id_table = cros_ec_spi_id,
+};
+
+module_spi_driver(cros_ec_driver_spi);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ChromeOS EC multi function device (SPI)");
diff --git a/drivers/mfd/cs5535-mfd.c b/drivers/mfd/cs5535-mfd.c
index 2b282133c72..be91cb5d6e7 100644
--- a/drivers/mfd/cs5535-mfd.c
+++ b/drivers/mfd/cs5535-mfd.c
@@ -23,7 +23,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -71,9 +70,9 @@ static int cs5535_mfd_res_disable(struct platform_device *pdev)
return 0;
}
-static __devinitdata struct resource cs5535_mfd_resources[NR_BARS];
+static struct resource cs5535_mfd_resources[NR_BARS];
-static __devinitdata struct mfd_cell cs5535_mfd_cells[] = {
+static struct mfd_cell cs5535_mfd_cells[] = {
{
.id = SMB_BAR,
.name = "cs5535-smb",
@@ -113,7 +112,7 @@ static __devinitdata struct mfd_cell cs5535_mfd_cells[] = {
};
#ifdef CONFIG_OLPC
-static void __devinit cs5535_clone_olpc_cells(void)
+static void cs5535_clone_olpc_cells(void)
{
const char *acpi_clones[] = { "olpc-xo1-pm-acpi", "olpc-xo1-sci-acpi" };
@@ -126,7 +125,7 @@ static void __devinit cs5535_clone_olpc_cells(void)
static void cs5535_clone_olpc_cells(void) { }
#endif
-static int __devinit cs5535_mfd_probe(struct pci_dev *pdev,
+static int cs5535_mfd_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int err, i;
@@ -166,13 +165,13 @@ err_disable:
return err;
}
-static void __devexit cs5535_mfd_remove(struct pci_dev *pdev)
+static void cs5535_mfd_remove(struct pci_dev *pdev)
{
mfd_remove_devices(&pdev->dev);
pci_disable_device(pdev);
}
-static DEFINE_PCI_DEVICE_TABLE(cs5535_mfd_pci_tbl) = {
+static const struct pci_device_id cs5535_mfd_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
{ 0, }
@@ -183,7 +182,7 @@ static struct pci_driver cs5535_mfd_driver = {
.name = DRV_NAME,
.id_table = cs5535_mfd_pci_tbl,
.probe = cs5535_mfd_probe,
- .remove = __devexit_p(cs5535_mfd_remove),
+ .remove = cs5535_mfd_remove,
};
module_pci_driver(cs5535_mfd_driver);
diff --git a/drivers/mfd/da903x.c b/drivers/mfd/da903x.c
index 1924b857a0f..e0a2e0ee603 100644
--- a/drivers/mfd/da903x.c
+++ b/drivers/mfd/da903x.c
@@ -246,7 +246,7 @@ int da903x_query_status(struct device *dev, unsigned int sbits)
}
EXPORT_SYMBOL(da903x_query_status);
-static int __devinit da9030_init_chip(struct da903x_chip *chip)
+static int da9030_init_chip(struct da903x_chip *chip)
{
uint8_t chip_id;
int err;
@@ -459,7 +459,7 @@ static int 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,
+static int da903x_add_subdevs(struct da903x_chip *chip,
struct da903x_platform_data *pdata)
{
struct da903x_subdev_info *subdev;
@@ -491,15 +491,16 @@ failed:
return ret;
}
-static int __devinit da903x_probe(struct i2c_client *client,
+static int da903x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct da903x_platform_data *pdata = client->dev.platform_data;
+ struct da903x_platform_data *pdata = dev_get_platdata(&client->dev);
struct da903x_chip *chip;
unsigned int tmp;
int ret;
- chip = kzalloc(sizeof(struct da903x_chip), GFP_KERNEL);
+ chip = devm_kzalloc(&client->dev, sizeof(struct da903x_chip),
+ GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
@@ -515,42 +516,34 @@ static int __devinit da903x_probe(struct i2c_client *client,
ret = chip->ops->init_chip(chip);
if (ret)
- goto out_free_chip;
+ return ret;
/* 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,
+ ret = devm_request_irq(&client->dev, client->irq, da903x_irq_handler,
IRQF_TRIGGER_FALLING,
"da903x", chip);
if (ret) {
dev_err(&client->dev, "failed to request irq %d\n",
client->irq);
- goto out_free_chip;
+ return ret;
}
ret = da903x_add_subdevs(chip, pdata);
if (ret)
- goto out_free_irq;
+ return ret;
return 0;
-
-out_free_irq:
- free_irq(client->irq, chip);
-out_free_chip:
- kfree(chip);
- return ret;
}
-static int __devexit da903x_remove(struct i2c_client *client)
+static int da903x_remove(struct i2c_client *client)
{
struct da903x_chip *chip = i2c_get_clientdata(client);
da903x_remove_subdevs(chip);
- free_irq(client->irq, chip);
- kfree(chip);
return 0;
}
@@ -560,7 +553,7 @@ static struct i2c_driver da903x_driver = {
.owner = THIS_MODULE,
},
.probe = da903x_probe,
- .remove = __devexit_p(da903x_remove),
+ .remove = da903x_remove,
.id_table = da903x_id_table,
};
diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c
index a0a62b24621..e8af816d73a 100644
--- a/drivers/mfd/da9052-core.c
+++ b/drivers/mfd/da9052-core.c
@@ -15,7 +15,6 @@
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/interrupt.h>
-#include <linux/irq.h>
#include <linux/mfd/core.h>
#include <linux/slab.h>
#include <linux/module.h>
@@ -24,16 +23,6 @@
#include <linux/mfd/da9052/pdata.h>
#include <linux/mfd/da9052/reg.h>
-#define DA9052_NUM_IRQ_REGS 4
-#define DA9052_IRQ_MASK_POS_1 0x01
-#define DA9052_IRQ_MASK_POS_2 0x02
-#define DA9052_IRQ_MASK_POS_3 0x04
-#define DA9052_IRQ_MASK_POS_4 0x08
-#define DA9052_IRQ_MASK_POS_5 0x10
-#define DA9052_IRQ_MASK_POS_6 0x20
-#define DA9052_IRQ_MASK_POS_7 0x40
-#define DA9052_IRQ_MASK_POS_8 0x80
-
static bool da9052_reg_readable(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -290,6 +279,9 @@ static bool da9052_reg_volatile(struct device *dev, unsigned int reg)
case DA9052_EVENT_B_REG:
case DA9052_EVENT_C_REG:
case DA9052_EVENT_D_REG:
+ case DA9052_CONTROL_B_REG:
+ case DA9052_CONTROL_D_REG:
+ case DA9052_SUPPLY_REG:
case DA9052_FAULTLOG_REG:
case DA9052_CHG_TIME_REG:
case DA9052_ADC_RES_L_REG:
@@ -425,15 +417,6 @@ err:
}
EXPORT_SYMBOL_GPL(da9052_adc_manual_read);
-static irqreturn_t da9052_auxadc_irq(int irq, void *irq_data)
-{
- struct da9052 *da9052 = irq_data;
-
- complete(&da9052->done);
-
- return IRQ_HANDLED;
-}
-
int da9052_adc_read_temp(struct da9052 *da9052)
{
int tbat;
@@ -447,75 +430,7 @@ int da9052_adc_read_temp(struct da9052 *da9052)
}
EXPORT_SYMBOL_GPL(da9052_adc_read_temp);
-static struct resource da9052_rtc_resource = {
- .name = "ALM",
- .start = DA9052_IRQ_ALARM,
- .end = DA9052_IRQ_ALARM,
- .flags = IORESOURCE_IRQ,
-};
-
-static struct resource da9052_onkey_resource = {
- .name = "ONKEY",
- .start = DA9052_IRQ_NONKEY,
- .end = DA9052_IRQ_NONKEY,
- .flags = IORESOURCE_IRQ,
-};
-
-static struct resource da9052_bat_resources[] = {
- {
- .name = "BATT TEMP",
- .start = DA9052_IRQ_TBAT,
- .end = DA9052_IRQ_TBAT,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "DCIN DET",
- .start = DA9052_IRQ_DCIN,
- .end = DA9052_IRQ_DCIN,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "DCIN REM",
- .start = DA9052_IRQ_DCINREM,
- .end = DA9052_IRQ_DCINREM,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "VBUS DET",
- .start = DA9052_IRQ_VBUS,
- .end = DA9052_IRQ_VBUS,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "VBUS REM",
- .start = DA9052_IRQ_VBUSREM,
- .end = DA9052_IRQ_VBUSREM,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "CHG END",
- .start = DA9052_IRQ_CHGEND,
- .end = DA9052_IRQ_CHGEND,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource da9052_tsi_resources[] = {
- {
- .name = "PENDWN",
- .start = DA9052_IRQ_PENDOWN,
- .end = DA9052_IRQ_PENDOWN,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "TSIRDY",
- .start = DA9052_IRQ_TSIREADY,
- .end = DA9052_IRQ_TSIREADY,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct mfd_cell __devinitdata da9052_subdev_info[] = {
+static const struct mfd_cell da9052_subdev_info[] = {
{
.name = "da9052-regulator",
.id = 1,
@@ -574,13 +489,9 @@ static struct mfd_cell __devinitdata da9052_subdev_info[] = {
},
{
.name = "da9052-onkey",
- .resources = &da9052_onkey_resource,
- .num_resources = 1,
},
{
.name = "da9052-rtc",
- .resources = &da9052_rtc_resource,
- .num_resources = 1,
},
{
.name = "da9052-gpio",
@@ -602,160 +513,15 @@ static struct mfd_cell __devinitdata da9052_subdev_info[] = {
},
{
.name = "da9052-tsi",
- .resources = da9052_tsi_resources,
- .num_resources = ARRAY_SIZE(da9052_tsi_resources),
},
{
.name = "da9052-bat",
- .resources = da9052_bat_resources,
- .num_resources = ARRAY_SIZE(da9052_bat_resources),
},
{
.name = "da9052-watchdog",
},
};
-static struct regmap_irq da9052_irqs[] = {
- [DA9052_IRQ_DCIN] = {
- .reg_offset = 0,
- .mask = DA9052_IRQ_MASK_POS_1,
- },
- [DA9052_IRQ_VBUS] = {
- .reg_offset = 0,
- .mask = DA9052_IRQ_MASK_POS_2,
- },
- [DA9052_IRQ_DCINREM] = {
- .reg_offset = 0,
- .mask = DA9052_IRQ_MASK_POS_3,
- },
- [DA9052_IRQ_VBUSREM] = {
- .reg_offset = 0,
- .mask = DA9052_IRQ_MASK_POS_4,
- },
- [DA9052_IRQ_VDDLOW] = {
- .reg_offset = 0,
- .mask = DA9052_IRQ_MASK_POS_5,
- },
- [DA9052_IRQ_ALARM] = {
- .reg_offset = 0,
- .mask = DA9052_IRQ_MASK_POS_6,
- },
- [DA9052_IRQ_SEQRDY] = {
- .reg_offset = 0,
- .mask = DA9052_IRQ_MASK_POS_7,
- },
- [DA9052_IRQ_COMP1V2] = {
- .reg_offset = 0,
- .mask = DA9052_IRQ_MASK_POS_8,
- },
- [DA9052_IRQ_NONKEY] = {
- .reg_offset = 1,
- .mask = DA9052_IRQ_MASK_POS_1,
- },
- [DA9052_IRQ_IDFLOAT] = {
- .reg_offset = 1,
- .mask = DA9052_IRQ_MASK_POS_2,
- },
- [DA9052_IRQ_IDGND] = {
- .reg_offset = 1,
- .mask = DA9052_IRQ_MASK_POS_3,
- },
- [DA9052_IRQ_CHGEND] = {
- .reg_offset = 1,
- .mask = DA9052_IRQ_MASK_POS_4,
- },
- [DA9052_IRQ_TBAT] = {
- .reg_offset = 1,
- .mask = DA9052_IRQ_MASK_POS_5,
- },
- [DA9052_IRQ_ADC_EOM] = {
- .reg_offset = 1,
- .mask = DA9052_IRQ_MASK_POS_6,
- },
- [DA9052_IRQ_PENDOWN] = {
- .reg_offset = 1,
- .mask = DA9052_IRQ_MASK_POS_7,
- },
- [DA9052_IRQ_TSIREADY] = {
- .reg_offset = 1,
- .mask = DA9052_IRQ_MASK_POS_8,
- },
- [DA9052_IRQ_GPI0] = {
- .reg_offset = 2,
- .mask = DA9052_IRQ_MASK_POS_1,
- },
- [DA9052_IRQ_GPI1] = {
- .reg_offset = 2,
- .mask = DA9052_IRQ_MASK_POS_2,
- },
- [DA9052_IRQ_GPI2] = {
- .reg_offset = 2,
- .mask = DA9052_IRQ_MASK_POS_3,
- },
- [DA9052_IRQ_GPI3] = {
- .reg_offset = 2,
- .mask = DA9052_IRQ_MASK_POS_4,
- },
- [DA9052_IRQ_GPI4] = {
- .reg_offset = 2,
- .mask = DA9052_IRQ_MASK_POS_5,
- },
- [DA9052_IRQ_GPI5] = {
- .reg_offset = 2,
- .mask = DA9052_IRQ_MASK_POS_6,
- },
- [DA9052_IRQ_GPI6] = {
- .reg_offset = 2,
- .mask = DA9052_IRQ_MASK_POS_7,
- },
- [DA9052_IRQ_GPI7] = {
- .reg_offset = 2,
- .mask = DA9052_IRQ_MASK_POS_8,
- },
- [DA9052_IRQ_GPI8] = {
- .reg_offset = 3,
- .mask = DA9052_IRQ_MASK_POS_1,
- },
- [DA9052_IRQ_GPI9] = {
- .reg_offset = 3,
- .mask = DA9052_IRQ_MASK_POS_2,
- },
- [DA9052_IRQ_GPI10] = {
- .reg_offset = 3,
- .mask = DA9052_IRQ_MASK_POS_3,
- },
- [DA9052_IRQ_GPI11] = {
- .reg_offset = 3,
- .mask = DA9052_IRQ_MASK_POS_4,
- },
- [DA9052_IRQ_GPI12] = {
- .reg_offset = 3,
- .mask = DA9052_IRQ_MASK_POS_5,
- },
- [DA9052_IRQ_GPI13] = {
- .reg_offset = 3,
- .mask = DA9052_IRQ_MASK_POS_6,
- },
- [DA9052_IRQ_GPI14] = {
- .reg_offset = 3,
- .mask = DA9052_IRQ_MASK_POS_7,
- },
- [DA9052_IRQ_GPI15] = {
- .reg_offset = 3,
- .mask = DA9052_IRQ_MASK_POS_8,
- },
-};
-
-static struct regmap_irq_chip da9052_regmap_irq_chip = {
- .name = "da9052_irq",
- .status_base = DA9052_EVENT_A_REG,
- .mask_base = DA9052_IRQ_MASK_A_REG,
- .ack_base = DA9052_EVENT_A_REG,
- .num_regs = DA9052_NUM_IRQ_REGS,
- .irqs = da9052_irqs,
- .num_irqs = ARRAY_SIZE(da9052_irqs),
-};
-
struct regmap_config da9052_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -769,9 +535,9 @@ struct regmap_config da9052_regmap_config = {
};
EXPORT_SYMBOL_GPL(da9052_regmap_config);
-int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)
+int da9052_device_init(struct da9052 *da9052, u8 chip_id)
{
- struct da9052_pdata *pdata = da9052->dev->platform_data;
+ struct da9052_pdata *pdata = dev_get_platdata(da9052->dev);
int ret;
mutex_init(&da9052->auxadc_lock);
@@ -782,45 +548,31 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)
da9052->chip_id = chip_id;
- if (!pdata || !pdata->irq_base)
- da9052->irq_base = -1;
- else
- da9052->irq_base = pdata->irq_base;
-
- ret = regmap_add_irq_chip(da9052->regmap, da9052->chip_irq,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- da9052->irq_base, &da9052_regmap_irq_chip,
- &da9052->irq_data);
- if (ret < 0)
- goto regmap_err;
-
- da9052->irq_base = regmap_irq_chip_get_base(da9052->irq_data);
-
- ret = request_threaded_irq(DA9052_IRQ_ADC_EOM, NULL, da9052_auxadc_irq,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- "adc irq", da9052);
- if (ret != 0)
- dev_err(da9052->dev, "DA9052 ADC IRQ failed ret=%d\n", ret);
+ ret = da9052_irq_init(da9052);
+ if (ret != 0) {
+ dev_err(da9052->dev, "da9052_irq_init failed: %d\n", ret);
+ return ret;
+ }
ret = mfd_add_devices(da9052->dev, -1, da9052_subdev_info,
ARRAY_SIZE(da9052_subdev_info), NULL, 0, NULL);
- if (ret)
+ if (ret) {
+ dev_err(da9052->dev, "mfd_add_devices failed: %d\n", ret);
goto err;
+ }
return 0;
err:
- free_irq(DA9052_IRQ_ADC_EOM, da9052);
- mfd_remove_devices(da9052->dev);
-regmap_err:
+ da9052_irq_exit(da9052);
+
return ret;
}
void da9052_device_exit(struct da9052 *da9052)
{
- free_irq(DA9052_IRQ_ADC_EOM, da9052);
- regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data);
mfd_remove_devices(da9052->dev);
+ da9052_irq_exit(da9052);
}
MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c
index 352c58b5a90..6da8ec8ff80 100644
--- a/drivers/mfd/da9052-i2c.c
+++ b/drivers/mfd/da9052-i2c.c
@@ -27,7 +27,71 @@
#include <linux/of_device.h>
#endif
-static int da9052_i2c_enable_multiwrite(struct da9052 *da9052)
+/* I2C safe register check */
+static inline bool i2c_safe_reg(unsigned char reg)
+{
+ switch (reg) {
+ case DA9052_STATUS_A_REG:
+ case DA9052_STATUS_B_REG:
+ case DA9052_STATUS_C_REG:
+ case DA9052_STATUS_D_REG:
+ case DA9052_ADC_RES_L_REG:
+ case DA9052_ADC_RES_H_REG:
+ case DA9052_VDD_RES_REG:
+ case DA9052_ICHG_AV_REG:
+ case DA9052_TBAT_RES_REG:
+ case DA9052_ADCIN4_RES_REG:
+ case DA9052_ADCIN5_RES_REG:
+ case DA9052_ADCIN6_RES_REG:
+ case DA9052_TJUNC_RES_REG:
+ case DA9052_TSI_X_MSB_REG:
+ case DA9052_TSI_Y_MSB_REG:
+ case DA9052_TSI_LSB_REG:
+ case DA9052_TSI_Z_MSB_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * There is an issue with DA9052 and DA9053_AA/BA/BB PMIC where the PMIC
+ * gets lockup up or fails to respond following a system reset.
+ * This fix is to follow any read or write with a dummy read to a safe
+ * register.
+ */
+static int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg)
+{
+ int val;
+
+ switch (da9052->chip_id) {
+ case DA9052:
+ case DA9053_AA:
+ case DA9053_BA:
+ case DA9053_BB:
+ /* A dummy read to a safe register address. */
+ if (!i2c_safe_reg(reg))
+ return regmap_read(da9052->regmap,
+ DA9052_PARK_REGISTER,
+ &val);
+ break;
+ case DA9053_BC:
+ default:
+ /*
+ * For other chips parking of I2C register
+ * to a safe place is not required.
+ */
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * According to errata item 24, multiwrite mode should be avoided
+ * in order to prevent register data corruption after power-down.
+ */
+static int da9052_i2c_disable_multiwrite(struct da9052 *da9052)
{
int reg_val, ret;
@@ -35,8 +99,8 @@ static int da9052_i2c_enable_multiwrite(struct da9052 *da9052)
if (ret < 0)
return ret;
- if (reg_val & DA9052_CONTROL_B_WRITEMODE) {
- reg_val &= ~DA9052_CONTROL_B_WRITEMODE;
+ if (!(reg_val & DA9052_CONTROL_B_WRITEMODE)) {
+ reg_val |= DA9052_CONTROL_B_WRITEMODE;
ret = regmap_write(da9052->regmap, DA9052_CONTROL_B_REG,
reg_val);
if (ret < 0)
@@ -51,6 +115,7 @@ static const struct i2c_device_id da9052_i2c_id[] = {
{"da9053-aa", DA9053_AA},
{"da9053-ba", DA9053_BA},
{"da9053-bb", DA9053_BB},
+ {"da9053-bc", DA9053_BC},
{}
};
@@ -58,13 +123,14 @@ static const struct i2c_device_id da9052_i2c_id[] = {
static const struct of_device_id dialog_dt_ids[] = {
{ .compatible = "dlg,da9052", .data = &da9052_i2c_id[0] },
{ .compatible = "dlg,da9053-aa", .data = &da9052_i2c_id[1] },
- { .compatible = "dlg,da9053-ab", .data = &da9052_i2c_id[2] },
+ { .compatible = "dlg,da9053-ba", .data = &da9052_i2c_id[2] },
{ .compatible = "dlg,da9053-bb", .data = &da9052_i2c_id[3] },
+ { .compatible = "dlg,da9053-bc", .data = &da9052_i2c_id[4] },
{ /* sentinel */ }
};
#endif
-static int __devinit da9052_i2c_probe(struct i2c_client *client,
+static int da9052_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct da9052 *da9052;
@@ -83,6 +149,7 @@ static int __devinit da9052_i2c_probe(struct i2c_client *client,
da9052->dev = &client->dev;
da9052->chip_irq = client->irq;
+ da9052->fix_io = da9052_i2c_fix;
i2c_set_clientdata(client, da9052);
@@ -94,7 +161,7 @@ static int __devinit da9052_i2c_probe(struct i2c_client *client,
return ret;
}
- ret = da9052_i2c_enable_multiwrite(da9052);
+ ret = da9052_i2c_disable_multiwrite(da9052);
if (ret < 0)
return ret;
@@ -121,7 +188,7 @@ static int __devinit da9052_i2c_probe(struct i2c_client *client,
return 0;
}
-static int __devexit da9052_i2c_remove(struct i2c_client *client)
+static int da9052_i2c_remove(struct i2c_client *client)
{
struct da9052 *da9052 = i2c_get_clientdata(client);
@@ -131,7 +198,7 @@ static int __devexit da9052_i2c_remove(struct i2c_client *client)
static struct i2c_driver da9052_i2c_driver = {
.probe = da9052_i2c_probe,
- .remove = __devexit_p(da9052_i2c_remove),
+ .remove = da9052_i2c_remove,
.id_table = da9052_i2c_id,
.driver = {
.name = "da9052",
diff --git a/drivers/mfd/da9052-irq.c b/drivers/mfd/da9052-irq.c
new file mode 100644
index 00000000000..57ae7841f53
--- /dev/null
+++ b/drivers/mfd/da9052-irq.c
@@ -0,0 +1,288 @@
+/*
+ * DA9052 interrupt support
+ *
+ * Author: Fabio Estevam <fabio.estevam@freescale.com>
+ * Based on arizona-irq.c, which is:
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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/device.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+
+#define DA9052_NUM_IRQ_REGS 4
+#define DA9052_IRQ_MASK_POS_1 0x01
+#define DA9052_IRQ_MASK_POS_2 0x02
+#define DA9052_IRQ_MASK_POS_3 0x04
+#define DA9052_IRQ_MASK_POS_4 0x08
+#define DA9052_IRQ_MASK_POS_5 0x10
+#define DA9052_IRQ_MASK_POS_6 0x20
+#define DA9052_IRQ_MASK_POS_7 0x40
+#define DA9052_IRQ_MASK_POS_8 0x80
+
+static struct regmap_irq da9052_irqs[] = {
+ [DA9052_IRQ_DCIN] = {
+ .reg_offset = 0,
+ .mask = DA9052_IRQ_MASK_POS_1,
+ },
+ [DA9052_IRQ_VBUS] = {
+ .reg_offset = 0,
+ .mask = DA9052_IRQ_MASK_POS_2,
+ },
+ [DA9052_IRQ_DCINREM] = {
+ .reg_offset = 0,
+ .mask = DA9052_IRQ_MASK_POS_3,
+ },
+ [DA9052_IRQ_VBUSREM] = {
+ .reg_offset = 0,
+ .mask = DA9052_IRQ_MASK_POS_4,
+ },
+ [DA9052_IRQ_VDDLOW] = {
+ .reg_offset = 0,
+ .mask = DA9052_IRQ_MASK_POS_5,
+ },
+ [DA9052_IRQ_ALARM] = {
+ .reg_offset = 0,
+ .mask = DA9052_IRQ_MASK_POS_6,
+ },
+ [DA9052_IRQ_SEQRDY] = {
+ .reg_offset = 0,
+ .mask = DA9052_IRQ_MASK_POS_7,
+ },
+ [DA9052_IRQ_COMP1V2] = {
+ .reg_offset = 0,
+ .mask = DA9052_IRQ_MASK_POS_8,
+ },
+ [DA9052_IRQ_NONKEY] = {
+ .reg_offset = 1,
+ .mask = DA9052_IRQ_MASK_POS_1,
+ },
+ [DA9052_IRQ_IDFLOAT] = {
+ .reg_offset = 1,
+ .mask = DA9052_IRQ_MASK_POS_2,
+ },
+ [DA9052_IRQ_IDGND] = {
+ .reg_offset = 1,
+ .mask = DA9052_IRQ_MASK_POS_3,
+ },
+ [DA9052_IRQ_CHGEND] = {
+ .reg_offset = 1,
+ .mask = DA9052_IRQ_MASK_POS_4,
+ },
+ [DA9052_IRQ_TBAT] = {
+ .reg_offset = 1,
+ .mask = DA9052_IRQ_MASK_POS_5,
+ },
+ [DA9052_IRQ_ADC_EOM] = {
+ .reg_offset = 1,
+ .mask = DA9052_IRQ_MASK_POS_6,
+ },
+ [DA9052_IRQ_PENDOWN] = {
+ .reg_offset = 1,
+ .mask = DA9052_IRQ_MASK_POS_7,
+ },
+ [DA9052_IRQ_TSIREADY] = {
+ .reg_offset = 1,
+ .mask = DA9052_IRQ_MASK_POS_8,
+ },
+ [DA9052_IRQ_GPI0] = {
+ .reg_offset = 2,
+ .mask = DA9052_IRQ_MASK_POS_1,
+ },
+ [DA9052_IRQ_GPI1] = {
+ .reg_offset = 2,
+ .mask = DA9052_IRQ_MASK_POS_2,
+ },
+ [DA9052_IRQ_GPI2] = {
+ .reg_offset = 2,
+ .mask = DA9052_IRQ_MASK_POS_3,
+ },
+ [DA9052_IRQ_GPI3] = {
+ .reg_offset = 2,
+ .mask = DA9052_IRQ_MASK_POS_4,
+ },
+ [DA9052_IRQ_GPI4] = {
+ .reg_offset = 2,
+ .mask = DA9052_IRQ_MASK_POS_5,
+ },
+ [DA9052_IRQ_GPI5] = {
+ .reg_offset = 2,
+ .mask = DA9052_IRQ_MASK_POS_6,
+ },
+ [DA9052_IRQ_GPI6] = {
+ .reg_offset = 2,
+ .mask = DA9052_IRQ_MASK_POS_7,
+ },
+ [DA9052_IRQ_GPI7] = {
+ .reg_offset = 2,
+ .mask = DA9052_IRQ_MASK_POS_8,
+ },
+ [DA9052_IRQ_GPI8] = {
+ .reg_offset = 3,
+ .mask = DA9052_IRQ_MASK_POS_1,
+ },
+ [DA9052_IRQ_GPI9] = {
+ .reg_offset = 3,
+ .mask = DA9052_IRQ_MASK_POS_2,
+ },
+ [DA9052_IRQ_GPI10] = {
+ .reg_offset = 3,
+ .mask = DA9052_IRQ_MASK_POS_3,
+ },
+ [DA9052_IRQ_GPI11] = {
+ .reg_offset = 3,
+ .mask = DA9052_IRQ_MASK_POS_4,
+ },
+ [DA9052_IRQ_GPI12] = {
+ .reg_offset = 3,
+ .mask = DA9052_IRQ_MASK_POS_5,
+ },
+ [DA9052_IRQ_GPI13] = {
+ .reg_offset = 3,
+ .mask = DA9052_IRQ_MASK_POS_6,
+ },
+ [DA9052_IRQ_GPI14] = {
+ .reg_offset = 3,
+ .mask = DA9052_IRQ_MASK_POS_7,
+ },
+ [DA9052_IRQ_GPI15] = {
+ .reg_offset = 3,
+ .mask = DA9052_IRQ_MASK_POS_8,
+ },
+};
+
+static struct regmap_irq_chip da9052_regmap_irq_chip = {
+ .name = "da9052_irq",
+ .status_base = DA9052_EVENT_A_REG,
+ .mask_base = DA9052_IRQ_MASK_A_REG,
+ .ack_base = DA9052_EVENT_A_REG,
+ .num_regs = DA9052_NUM_IRQ_REGS,
+ .irqs = da9052_irqs,
+ .num_irqs = ARRAY_SIZE(da9052_irqs),
+};
+
+static int da9052_map_irq(struct da9052 *da9052, int irq)
+{
+ return regmap_irq_get_virq(da9052->irq_data, irq);
+}
+
+int da9052_enable_irq(struct da9052 *da9052, int irq)
+{
+ irq = da9052_map_irq(da9052, irq);
+ if (irq < 0)
+ return irq;
+
+ enable_irq(irq);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(da9052_enable_irq);
+
+int da9052_disable_irq(struct da9052 *da9052, int irq)
+{
+ irq = da9052_map_irq(da9052, irq);
+ if (irq < 0)
+ return irq;
+
+ disable_irq(irq);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(da9052_disable_irq);
+
+int da9052_disable_irq_nosync(struct da9052 *da9052, int irq)
+{
+ irq = da9052_map_irq(da9052, irq);
+ if (irq < 0)
+ return irq;
+
+ disable_irq_nosync(irq);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(da9052_disable_irq_nosync);
+
+int da9052_request_irq(struct da9052 *da9052, int irq, char *name,
+ irq_handler_t handler, void *data)
+{
+ irq = da9052_map_irq(da9052, irq);
+ if (irq < 0)
+ return irq;
+
+ return request_threaded_irq(irq, NULL, handler,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ name, data);
+}
+EXPORT_SYMBOL_GPL(da9052_request_irq);
+
+void da9052_free_irq(struct da9052 *da9052, int irq, void *data)
+{
+ irq = da9052_map_irq(da9052, irq);
+ if (irq < 0)
+ return;
+
+ free_irq(irq, data);
+}
+EXPORT_SYMBOL_GPL(da9052_free_irq);
+
+static irqreturn_t da9052_auxadc_irq(int irq, void *irq_data)
+{
+ struct da9052 *da9052 = irq_data;
+
+ complete(&da9052->done);
+
+ return IRQ_HANDLED;
+}
+
+int da9052_irq_init(struct da9052 *da9052)
+{
+ int ret;
+
+ ret = regmap_add_irq_chip(da9052->regmap, da9052->chip_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ -1, &da9052_regmap_irq_chip,
+ &da9052->irq_data);
+ if (ret < 0) {
+ dev_err(da9052->dev, "regmap_add_irq_chip failed: %d\n", ret);
+ goto regmap_err;
+ }
+
+ ret = da9052_request_irq(da9052, DA9052_IRQ_ADC_EOM, "adc-irq",
+ da9052_auxadc_irq, da9052);
+
+ if (ret != 0) {
+ dev_err(da9052->dev, "DA9052_IRQ_ADC_EOM failed: %d\n", ret);
+ goto request_irq_err;
+ }
+
+ return 0;
+
+request_irq_err:
+ regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data);
+regmap_err:
+ return ret;
+
+}
+
+int da9052_irq_exit(struct da9052 *da9052)
+{
+ da9052_free_irq(da9052, DA9052_IRQ_ADC_EOM , da9052);
+ regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data);
+
+ return 0;
+}
diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c
index dbeadc5a643..17666b40b70 100644
--- a/drivers/mfd/da9052-spi.c
+++ b/drivers/mfd/da9052-spi.c
@@ -21,7 +21,7 @@
#include <linux/mfd/da9052/da9052.h>
-static int __devinit da9052_spi_probe(struct spi_device *spi)
+static int da9052_spi_probe(struct spi_device *spi)
{
int ret;
const struct spi_device_id *id = spi_get_device_id(spi);
@@ -38,7 +38,7 @@ static int __devinit da9052_spi_probe(struct spi_device *spi)
da9052->dev = &spi->dev;
da9052->chip_irq = spi->irq;
- dev_set_drvdata(&spi->dev, da9052);
+ spi_set_drvdata(spi, da9052);
da9052_regmap_config.read_flag_mask = 1;
da9052_regmap_config.write_flag_mask = 0;
@@ -58,9 +58,9 @@ static int __devinit da9052_spi_probe(struct spi_device *spi)
return 0;
}
-static int __devexit da9052_spi_remove(struct spi_device *spi)
+static int da9052_spi_remove(struct spi_device *spi)
{
- struct da9052 *da9052 = dev_get_drvdata(&spi->dev);
+ struct da9052 *da9052 = spi_get_drvdata(spi);
da9052_device_exit(da9052);
return 0;
@@ -71,12 +71,13 @@ static struct spi_device_id da9052_spi_id[] = {
{"da9053-aa", DA9053_AA},
{"da9053-ba", DA9053_BA},
{"da9053-bb", DA9053_BB},
+ {"da9053-bc", DA9053_BC},
{}
};
static struct spi_driver da9052_spi_driver = {
.probe = da9052_spi_probe,
- .remove = __devexit_p(da9052_spi_remove),
+ .remove = da9052_spi_remove,
.id_table = da9052_spi_id,
.driver = {
.name = "da9052",
diff --git a/drivers/mfd/da9055-core.c b/drivers/mfd/da9055-core.c
index ff6c77f392b..caf8dcffd0a 100644
--- a/drivers/mfd/da9055-core.c
+++ b/drivers/mfd/da9055-core.c
@@ -294,7 +294,7 @@ static struct resource da9055_ld05_6_resource = {
.flags = IORESOURCE_IRQ,
};
-static struct mfd_cell da9055_devs[] = {
+static const struct mfd_cell da9055_devs[] = {
{
.of_compatible = "dialog,da9055-gpio",
.name = "da9055-gpio",
@@ -377,10 +377,11 @@ static struct regmap_irq_chip da9055_regmap_irq_chip = {
.num_irqs = ARRAY_SIZE(da9055_irqs),
};
-int __devinit da9055_device_init(struct da9055 *da9055)
+int da9055_device_init(struct da9055 *da9055)
{
- struct da9055_pdata *pdata = da9055->dev->platform_data;
+ struct da9055_pdata *pdata = dev_get_platdata(da9055->dev);
int ret;
+ uint8_t clear_events[3] = {0xFF, 0xFF, 0xFF};
if (pdata && pdata->init != NULL)
pdata->init(da9055);
@@ -390,8 +391,12 @@ int __devinit da9055_device_init(struct da9055 *da9055)
else
da9055->irq_base = pdata->irq_base;
+ ret = da9055_group_write(da9055, DA9055_REG_EVENT_A, 3, clear_events);
+ if (ret < 0)
+ return ret;
+
ret = regmap_add_irq_chip(da9055->regmap, da9055->chip_irq,
- IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
da9055->irq_base, &da9055_regmap_irq_chip,
&da9055->irq_data);
if (ret < 0)
@@ -412,7 +417,7 @@ err:
return ret;
}
-void __devexit da9055_device_exit(struct da9055 *da9055)
+void da9055_device_exit(struct da9055 *da9055)
{
regmap_del_irq_chip(da9055->chip_irq, da9055->irq_data);
mfd_remove_devices(da9055->dev);
diff --git a/drivers/mfd/da9055-i2c.c b/drivers/mfd/da9055-i2c.c
index 88f6dca53ba..d4d4c165eb9 100644
--- a/drivers/mfd/da9055-i2c.c
+++ b/drivers/mfd/da9055-i2c.c
@@ -15,10 +15,12 @@
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/mfd/da9055/core.h>
-static int __devinit da9055_i2c_probe(struct i2c_client *i2c,
+static int da9055_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct da9055 *da9055;
@@ -44,7 +46,7 @@ static int __devinit da9055_i2c_probe(struct i2c_client *i2c,
return da9055_device_init(da9055);
}
-static int __devexit da9055_i2c_remove(struct i2c_client *i2c)
+static int da9055_i2c_remove(struct i2c_client *i2c)
{
struct da9055 *da9055 = i2c_get_clientdata(i2c);
@@ -53,18 +55,32 @@ static int __devexit da9055_i2c_remove(struct i2c_client *i2c)
return 0;
}
+/*
+ * DO NOT change the device Ids. The naming is intentionally specific as both
+ * the PMIC and CODEC parts of this chip are instantiated separately as I2C
+ * devices (both have configurable I2C addresses, and are to all intents and
+ * purposes separate). As a result there are specific DA9055 ids for PMIC
+ * and CODEC, which must be different to operate together.
+ */
static struct i2c_device_id da9055_i2c_id[] = {
{"da9055-pmic", 0},
{ }
};
+MODULE_DEVICE_TABLE(i2c, da9055_i2c_id);
+
+static const struct of_device_id da9055_of_match[] = {
+ { .compatible = "dlg,da9055-pmic", },
+ { }
+};
static struct i2c_driver da9055_i2c_driver = {
.probe = da9055_i2c_probe,
- .remove = __devexit_p(da9055_i2c_remove),
+ .remove = da9055_i2c_remove,
.id_table = da9055_i2c_id,
.driver = {
- .name = "da9055",
+ .name = "da9055-pmic",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(da9055_of_match),
},
};
diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c
new file mode 100644
index 00000000000..e70ae315abc
--- /dev/null
+++ b/drivers/mfd/da9063-core.c
@@ -0,0 +1,188 @@
+/*
+ * da9063-core.c: Device access for Dialog DA9063 modules
+ *
+ * Copyright 2012 Dialog Semiconductors Ltd.
+ * Copyright 2013 Philipp Zabel, Pengutronix
+ *
+ * Author: Krystian Garbaciak <krystian.garbaciak@diasemi.com>,
+ * Michal Hajduk <michal.hajduk@diasemi.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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/da9063/core.h>
+#include <linux/mfd/da9063/pdata.h>
+#include <linux/mfd/da9063/registers.h>
+
+#include <linux/proc_fs.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+
+
+static struct resource da9063_regulators_resources[] = {
+ {
+ .name = "LDO_LIM",
+ .start = DA9063_IRQ_LDO_LIM,
+ .end = DA9063_IRQ_LDO_LIM,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource da9063_rtc_resources[] = {
+ {
+ .name = "ALARM",
+ .start = DA9063_IRQ_ALARM,
+ .end = DA9063_IRQ_ALARM,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "TICK",
+ .start = DA9063_IRQ_TICK,
+ .end = DA9063_IRQ_TICK,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct resource da9063_onkey_resources[] = {
+ {
+ .start = DA9063_IRQ_ONKEY,
+ .end = DA9063_IRQ_ONKEY,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource da9063_hwmon_resources[] = {
+ {
+ .start = DA9063_IRQ_ADC_RDY,
+ .end = DA9063_IRQ_ADC_RDY,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+
+static const struct mfd_cell da9063_devs[] = {
+ {
+ .name = DA9063_DRVNAME_REGULATORS,
+ .num_resources = ARRAY_SIZE(da9063_regulators_resources),
+ .resources = da9063_regulators_resources,
+ },
+ {
+ .name = DA9063_DRVNAME_LEDS,
+ },
+ {
+ .name = DA9063_DRVNAME_WATCHDOG,
+ },
+ {
+ .name = DA9063_DRVNAME_HWMON,
+ .num_resources = ARRAY_SIZE(da9063_hwmon_resources),
+ .resources = da9063_hwmon_resources,
+ },
+ {
+ .name = DA9063_DRVNAME_ONKEY,
+ .num_resources = ARRAY_SIZE(da9063_onkey_resources),
+ .resources = da9063_onkey_resources,
+ },
+ {
+ .name = DA9063_DRVNAME_RTC,
+ .num_resources = ARRAY_SIZE(da9063_rtc_resources),
+ .resources = da9063_rtc_resources,
+ },
+ {
+ .name = DA9063_DRVNAME_VIBRATION,
+ },
+};
+
+int da9063_device_init(struct da9063 *da9063, unsigned int irq)
+{
+ struct da9063_pdata *pdata = da9063->dev->platform_data;
+ int model, variant_id, variant_code;
+ int ret;
+
+ if (pdata) {
+ da9063->flags = pdata->flags;
+ da9063->irq_base = pdata->irq_base;
+ } else {
+ da9063->flags = 0;
+ da9063->irq_base = 0;
+ }
+ da9063->chip_irq = irq;
+
+ if (pdata && pdata->init != NULL) {
+ ret = pdata->init(da9063);
+ if (ret != 0) {
+ dev_err(da9063->dev,
+ "Platform initialization failed.\n");
+ return ret;
+ }
+ }
+
+ ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_ID, &model);
+ if (ret < 0) {
+ dev_err(da9063->dev, "Cannot read chip model id.\n");
+ return -EIO;
+ }
+ if (model != PMIC_DA9063) {
+ dev_err(da9063->dev, "Invalid chip model id: 0x%02x\n", model);
+ return -ENODEV;
+ }
+
+ ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_VARIANT, &variant_id);
+ if (ret < 0) {
+ dev_err(da9063->dev, "Cannot read chip variant id.\n");
+ return -EIO;
+ }
+
+ variant_code = variant_id >> DA9063_CHIP_VARIANT_SHIFT;
+
+ dev_info(da9063->dev,
+ "Device detected (chip-ID: 0x%02X, var-ID: 0x%02X)\n",
+ model, variant_id);
+
+ if (variant_code != PMIC_DA9063_BB) {
+ dev_err(da9063->dev, "Unknown chip variant code: 0x%02X\n",
+ variant_code);
+ return -ENODEV;
+ }
+
+ da9063->model = model;
+ da9063->variant_code = variant_code;
+
+ ret = da9063_irq_init(da9063);
+ if (ret) {
+ dev_err(da9063->dev, "Cannot initialize interrupts.\n");
+ return ret;
+ }
+
+ ret = mfd_add_devices(da9063->dev, -1, da9063_devs,
+ ARRAY_SIZE(da9063_devs), NULL, da9063->irq_base,
+ NULL);
+ if (ret)
+ dev_err(da9063->dev, "Cannot add MFD cells\n");
+
+ return ret;
+}
+
+void da9063_device_exit(struct da9063 *da9063)
+{
+ mfd_remove_devices(da9063->dev);
+ da9063_irq_exit(da9063);
+}
+
+MODULE_DESCRIPTION("PMIC driver for Dialog DA9063");
+MODULE_AUTHOR("Krystian Garbaciak <krystian.garbaciak@diasemi.com>, Michal Hajduk <michal.hajduk@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/da9063-i2c.c b/drivers/mfd/da9063-i2c.c
new file mode 100644
index 00000000000..8db5c805c64
--- /dev/null
+++ b/drivers/mfd/da9063-i2c.c
@@ -0,0 +1,182 @@
+/* da9063-i2c.c: Interrupt support for Dialog DA9063
+ *
+ * Copyright 2012 Dialog Semiconductor Ltd.
+ * Copyright 2013 Philipp Zabel, Pengutronix
+ *
+ * Author: Krystian Garbaciak <krystian.garbaciak@diasemi.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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/da9063/core.h>
+#include <linux/mfd/da9063/pdata.h>
+#include <linux/mfd/da9063/registers.h>
+
+static const struct regmap_range da9063_readable_ranges[] = {
+ {
+ .range_min = DA9063_REG_PAGE_CON,
+ .range_max = DA9063_REG_SECOND_D,
+ }, {
+ .range_min = DA9063_REG_SEQ,
+ .range_max = DA9063_REG_ID_32_31,
+ }, {
+ .range_min = DA9063_REG_SEQ_A,
+ .range_max = DA9063_REG_AUTO3_LOW,
+ }, {
+ .range_min = DA9063_REG_T_OFFSET,
+ .range_max = DA9063_REG_GP_ID_19,
+ }, {
+ .range_min = DA9063_REG_CHIP_ID,
+ .range_max = DA9063_REG_CHIP_VARIANT,
+ },
+};
+
+static const struct regmap_range da9063_writeable_ranges[] = {
+ {
+ .range_min = DA9063_REG_PAGE_CON,
+ .range_max = DA9063_REG_PAGE_CON,
+ }, {
+ .range_min = DA9063_REG_FAULT_LOG,
+ .range_max = DA9063_REG_VSYS_MON,
+ }, {
+ .range_min = DA9063_REG_COUNT_S,
+ .range_max = DA9063_REG_ALARM_Y,
+ }, {
+ .range_min = DA9063_REG_SEQ,
+ .range_max = DA9063_REG_ID_32_31,
+ }, {
+ .range_min = DA9063_REG_SEQ_A,
+ .range_max = DA9063_REG_AUTO3_LOW,
+ }, {
+ .range_min = DA9063_REG_CONFIG_I,
+ .range_max = DA9063_REG_MON_REG_4,
+ }, {
+ .range_min = DA9063_REG_GP_ID_0,
+ .range_max = DA9063_REG_GP_ID_19,
+ },
+};
+
+static const struct regmap_range da9063_volatile_ranges[] = {
+ {
+ .range_min = DA9063_REG_STATUS_A,
+ .range_max = DA9063_REG_EVENT_D,
+ }, {
+ .range_min = DA9063_REG_CONTROL_F,
+ .range_max = DA9063_REG_CONTROL_F,
+ }, {
+ .range_min = DA9063_REG_ADC_MAN,
+ .range_max = DA9063_REG_ADC_MAN,
+ }, {
+ .range_min = DA9063_REG_ADC_RES_L,
+ .range_max = DA9063_REG_SECOND_D,
+ }, {
+ .range_min = DA9063_REG_MON_REG_5,
+ .range_max = DA9063_REG_MON_REG_6,
+ },
+};
+
+static const struct regmap_access_table da9063_readable_table = {
+ .yes_ranges = da9063_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9063_readable_ranges),
+};
+
+static const struct regmap_access_table da9063_writeable_table = {
+ .yes_ranges = da9063_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9063_writeable_ranges),
+};
+
+static const struct regmap_access_table da9063_volatile_table = {
+ .yes_ranges = da9063_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9063_volatile_ranges),
+};
+
+static const struct regmap_range_cfg da9063_range_cfg[] = {
+ {
+ .range_min = DA9063_REG_PAGE_CON,
+ .range_max = DA9063_REG_CHIP_VARIANT,
+ .selector_reg = DA9063_REG_PAGE_CON,
+ .selector_mask = 1 << DA9063_I2C_PAGE_SEL_SHIFT,
+ .selector_shift = DA9063_I2C_PAGE_SEL_SHIFT,
+ .window_start = 0,
+ .window_len = 256,
+ }
+};
+
+static struct regmap_config da9063_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .ranges = da9063_range_cfg,
+ .num_ranges = ARRAY_SIZE(da9063_range_cfg),
+ .max_register = DA9063_REG_CHIP_VARIANT,
+
+ .cache_type = REGCACHE_RBTREE,
+
+ .rd_table = &da9063_readable_table,
+ .wr_table = &da9063_writeable_table,
+ .volatile_table = &da9063_volatile_table,
+};
+
+static int da9063_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct da9063 *da9063;
+ int ret;
+
+ da9063 = devm_kzalloc(&i2c->dev, sizeof(struct da9063), GFP_KERNEL);
+ if (da9063 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, da9063);
+ da9063->dev = &i2c->dev;
+ da9063->chip_irq = i2c->irq;
+
+ da9063->regmap = devm_regmap_init_i2c(i2c, &da9063_regmap_config);
+ if (IS_ERR(da9063->regmap)) {
+ ret = PTR_ERR(da9063->regmap);
+ dev_err(da9063->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ return da9063_device_init(da9063, i2c->irq);
+}
+
+static int da9063_i2c_remove(struct i2c_client *i2c)
+{
+ struct da9063 *da9063 = i2c_get_clientdata(i2c);
+
+ da9063_device_exit(da9063);
+
+ return 0;
+}
+
+static const struct i2c_device_id da9063_i2c_id[] = {
+ {"da9063", PMIC_DA9063},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, da9063_i2c_id);
+
+static struct i2c_driver da9063_i2c_driver = {
+ .driver = {
+ .name = "da9063",
+ .owner = THIS_MODULE,
+ },
+ .probe = da9063_i2c_probe,
+ .remove = da9063_i2c_remove,
+ .id_table = da9063_i2c_id,
+};
+
+module_i2c_driver(da9063_i2c_driver);
diff --git a/drivers/mfd/da9063-irq.c b/drivers/mfd/da9063-irq.c
new file mode 100644
index 00000000000..822922602ce
--- /dev/null
+++ b/drivers/mfd/da9063-irq.c
@@ -0,0 +1,193 @@
+/* da9063-irq.c: Interrupts support for Dialog DA9063
+ *
+ * Copyright 2012 Dialog Semiconductor Ltd.
+ * Copyright 2013 Philipp Zabel, Pengutronix
+ *
+ * Author: Michal Hajduk <michal.hajduk@diasemi.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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/mfd/da9063/core.h>
+#include <linux/mfd/da9063/pdata.h>
+
+#define DA9063_REG_EVENT_A_OFFSET 0
+#define DA9063_REG_EVENT_B_OFFSET 1
+#define DA9063_REG_EVENT_C_OFFSET 2
+#define DA9063_REG_EVENT_D_OFFSET 3
+#define EVENTS_BUF_LEN 4
+
+static const u8 mask_events_buf[] = { [0 ... (EVENTS_BUF_LEN - 1)] = ~0 };
+
+struct da9063_irq_data {
+ u16 reg;
+ u8 mask;
+};
+
+static struct regmap_irq da9063_irqs[] = {
+ /* DA9063 event A register */
+ [DA9063_IRQ_ONKEY] = {
+ .reg_offset = DA9063_REG_EVENT_A_OFFSET,
+ .mask = DA9063_M_ONKEY,
+ },
+ [DA9063_IRQ_ALARM] = {
+ .reg_offset = DA9063_REG_EVENT_A_OFFSET,
+ .mask = DA9063_M_ALARM,
+ },
+ [DA9063_IRQ_TICK] = {
+ .reg_offset = DA9063_REG_EVENT_A_OFFSET,
+ .mask = DA9063_M_TICK,
+ },
+ [DA9063_IRQ_ADC_RDY] = {
+ .reg_offset = DA9063_REG_EVENT_A_OFFSET,
+ .mask = DA9063_M_ADC_RDY,
+ },
+ [DA9063_IRQ_SEQ_RDY] = {
+ .reg_offset = DA9063_REG_EVENT_A_OFFSET,
+ .mask = DA9063_M_SEQ_RDY,
+ },
+ /* DA9063 event B register */
+ [DA9063_IRQ_WAKE] = {
+ .reg_offset = DA9063_REG_EVENT_B_OFFSET,
+ .mask = DA9063_M_WAKE,
+ },
+ [DA9063_IRQ_TEMP] = {
+ .reg_offset = DA9063_REG_EVENT_B_OFFSET,
+ .mask = DA9063_M_TEMP,
+ },
+ [DA9063_IRQ_COMP_1V2] = {
+ .reg_offset = DA9063_REG_EVENT_B_OFFSET,
+ .mask = DA9063_M_COMP_1V2,
+ },
+ [DA9063_IRQ_LDO_LIM] = {
+ .reg_offset = DA9063_REG_EVENT_B_OFFSET,
+ .mask = DA9063_M_LDO_LIM,
+ },
+ [DA9063_IRQ_REG_UVOV] = {
+ .reg_offset = DA9063_REG_EVENT_B_OFFSET,
+ .mask = DA9063_M_UVOV,
+ },
+ [DA9063_IRQ_VDD_MON] = {
+ .reg_offset = DA9063_REG_EVENT_B_OFFSET,
+ .mask = DA9063_M_VDD_MON,
+ },
+ [DA9063_IRQ_WARN] = {
+ .reg_offset = DA9063_REG_EVENT_B_OFFSET,
+ .mask = DA9063_M_VDD_WARN,
+ },
+ /* DA9063 event C register */
+ [DA9063_IRQ_GPI0] = {
+ .reg_offset = DA9063_REG_EVENT_C_OFFSET,
+ .mask = DA9063_M_GPI0,
+ },
+ [DA9063_IRQ_GPI1] = {
+ .reg_offset = DA9063_REG_EVENT_C_OFFSET,
+ .mask = DA9063_M_GPI1,
+ },
+ [DA9063_IRQ_GPI2] = {
+ .reg_offset = DA9063_REG_EVENT_C_OFFSET,
+ .mask = DA9063_M_GPI2,
+ },
+ [DA9063_IRQ_GPI3] = {
+ .reg_offset = DA9063_REG_EVENT_C_OFFSET,
+ .mask = DA9063_M_GPI3,
+ },
+ [DA9063_IRQ_GPI4] = {
+ .reg_offset = DA9063_REG_EVENT_C_OFFSET,
+ .mask = DA9063_M_GPI4,
+ },
+ [DA9063_IRQ_GPI5] = {
+ .reg_offset = DA9063_REG_EVENT_C_OFFSET,
+ .mask = DA9063_M_GPI5,
+ },
+ [DA9063_IRQ_GPI6] = {
+ .reg_offset = DA9063_REG_EVENT_C_OFFSET,
+ .mask = DA9063_M_GPI6,
+ },
+ [DA9063_IRQ_GPI7] = {
+ .reg_offset = DA9063_REG_EVENT_C_OFFSET,
+ .mask = DA9063_M_GPI7,
+ },
+ /* DA9063 event D register */
+ [DA9063_IRQ_GPI8] = {
+ .reg_offset = DA9063_REG_EVENT_D_OFFSET,
+ .mask = DA9063_M_GPI8,
+ },
+ [DA9063_IRQ_GPI9] = {
+ .reg_offset = DA9063_REG_EVENT_D_OFFSET,
+ .mask = DA9063_M_GPI9,
+ },
+ [DA9063_IRQ_GPI10] = {
+ .reg_offset = DA9063_REG_EVENT_D_OFFSET,
+ .mask = DA9063_M_GPI10,
+ },
+ [DA9063_IRQ_GPI11] = {
+ .reg_offset = DA9063_REG_EVENT_D_OFFSET,
+ .mask = DA9063_M_GPI11,
+ },
+ [DA9063_IRQ_GPI12] = {
+ .reg_offset = DA9063_REG_EVENT_D_OFFSET,
+ .mask = DA9063_M_GPI12,
+ },
+ [DA9063_IRQ_GPI13] = {
+ .reg_offset = DA9063_REG_EVENT_D_OFFSET,
+ .mask = DA9063_M_GPI13,
+ },
+ [DA9063_IRQ_GPI14] = {
+ .reg_offset = DA9063_REG_EVENT_D_OFFSET,
+ .mask = DA9063_M_GPI14,
+ },
+ [DA9063_IRQ_GPI15] = {
+ .reg_offset = DA9063_REG_EVENT_D_OFFSET,
+ .mask = DA9063_M_GPI15,
+ },
+};
+
+static struct regmap_irq_chip da9063_irq_chip = {
+ .name = "da9063-irq",
+ .irqs = da9063_irqs,
+ .num_irqs = DA9063_NUM_IRQ,
+
+ .num_regs = 4,
+ .status_base = DA9063_REG_EVENT_A,
+ .mask_base = DA9063_REG_IRQ_MASK_A,
+ .ack_base = DA9063_REG_EVENT_A,
+ .init_ack_masked = true,
+};
+
+int da9063_irq_init(struct da9063 *da9063)
+{
+ int ret;
+
+ if (!da9063->chip_irq) {
+ dev_err(da9063->dev, "No IRQ configured\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_add_irq_chip(da9063->regmap, da9063->chip_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED,
+ da9063->irq_base, &da9063_irq_chip,
+ &da9063->regmap_irq);
+ if (ret) {
+ dev_err(da9063->dev, "Failed to reguest IRQ %d: %d\n",
+ da9063->chip_irq, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void da9063_irq_exit(struct da9063 *da9063)
+{
+ regmap_del_irq_chip(da9063->chip_irq, da9063->regmap_irq);
+}
diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c
index 45e83a68641..013ba8159dc 100644
--- a/drivers/mfd/davinci_voicecodec.c
+++ b/drivers/mfd/davinci_voicecodec.c
@@ -27,75 +27,61 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/clk.h>
+#include <linux/regmap.h>
#include <sound/pcm.h>
#include <linux/mfd/davinci_voicecodec.h>
-u32 davinci_vc_read(struct davinci_vc *davinci_vc, int reg)
-{
- return __raw_readl(davinci_vc->base + reg);
-}
-
-void davinci_vc_write(struct davinci_vc *davinci_vc,
- int reg, u32 val)
-{
- __raw_writel(val, davinci_vc->base + reg);
-}
+static struct regmap_config davinci_vc_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+};
static int __init davinci_vc_probe(struct platform_device *pdev)
{
struct davinci_vc *davinci_vc;
- struct resource *res, *mem;
+ struct resource *res;
struct mfd_cell *cell = NULL;
int ret;
- davinci_vc = kzalloc(sizeof(struct davinci_vc), GFP_KERNEL);
+ davinci_vc = devm_kzalloc(&pdev->dev,
+ sizeof(struct davinci_vc), GFP_KERNEL);
if (!davinci_vc) {
dev_dbg(&pdev->dev,
"could not allocate memory for private data\n");
return -ENOMEM;
}
- davinci_vc->clk = clk_get(&pdev->dev, NULL);
+ davinci_vc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(davinci_vc->clk)) {
dev_dbg(&pdev->dev,
"could not get the clock for voice codec\n");
- ret = -ENODEV;
- goto fail1;
+ return -ENODEV;
}
clk_enable(davinci_vc->clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "no mem resource\n");
- ret = -ENODEV;
- goto fail2;
- }
- davinci_vc->pbase = res->start;
- davinci_vc->base_size = resource_size(res);
-
- mem = request_mem_region(davinci_vc->pbase, davinci_vc->base_size,
- pdev->name);
- if (!mem) {
- dev_err(&pdev->dev, "VCIF region already claimed\n");
- ret = -EBUSY;
- goto fail2;
+ davinci_vc->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(davinci_vc->base)) {
+ ret = PTR_ERR(davinci_vc->base);
+ goto fail;
}
- davinci_vc->base = ioremap(davinci_vc->pbase, davinci_vc->base_size);
- if (!davinci_vc->base) {
- dev_err(&pdev->dev, "can't ioremap mem resource.\n");
- ret = -ENOMEM;
- goto fail3;
+ davinci_vc->regmap = devm_regmap_init_mmio(&pdev->dev,
+ davinci_vc->base,
+ &davinci_vc_regmap);
+ if (IS_ERR(davinci_vc->regmap)) {
+ ret = PTR_ERR(davinci_vc->regmap);
+ goto fail;
}
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!res) {
dev_err(&pdev->dev, "no DMA resource\n");
ret = -ENXIO;
- goto fail4;
+ goto fail;
}
davinci_vc->davinci_vcif.dma_tx_channel = res->start;
@@ -106,7 +92,7 @@ static int __init davinci_vc_probe(struct platform_device *pdev)
if (!res) {
dev_err(&pdev->dev, "no DMA resource\n");
ret = -ENXIO;
- goto fail4;
+ goto fail;
}
davinci_vc->davinci_vcif.dma_rx_channel = res->start;
@@ -132,39 +118,24 @@ static int __init davinci_vc_probe(struct platform_device *pdev)
DAVINCI_VC_CELLS, NULL, 0, NULL);
if (ret != 0) {
dev_err(&pdev->dev, "fail to register client devices\n");
- goto fail4;
+ goto fail;
}
return 0;
-fail4:
- iounmap(davinci_vc->base);
-fail3:
- release_mem_region(davinci_vc->pbase, davinci_vc->base_size);
-fail2:
+fail:
clk_disable(davinci_vc->clk);
- clk_put(davinci_vc->clk);
- davinci_vc->clk = NULL;
-fail1:
- kfree(davinci_vc);
return ret;
}
-static int __devexit davinci_vc_remove(struct platform_device *pdev)
+static int davinci_vc_remove(struct platform_device *pdev)
{
struct davinci_vc *davinci_vc = platform_get_drvdata(pdev);
mfd_remove_devices(&pdev->dev);
- iounmap(davinci_vc->base);
- release_mem_region(davinci_vc->pbase, davinci_vc->base_size);
-
clk_disable(davinci_vc->clk);
- clk_put(davinci_vc->clk);
- davinci_vc->clk = NULL;
-
- kfree(davinci_vc);
return 0;
}
@@ -174,20 +145,10 @@ static struct platform_driver davinci_vc_driver = {
.name = "davinci_voicecodec",
.owner = THIS_MODULE,
},
- .remove = __devexit_p(davinci_vc_remove),
+ .remove = davinci_vc_remove,
};
-static int __init davinci_vc_init(void)
-{
- return platform_driver_probe(&davinci_vc_driver, davinci_vc_probe);
-}
-module_init(davinci_vc_init);
-
-static void __exit davinci_vc_exit(void)
-{
- platform_driver_unregister(&davinci_vc_driver);
-}
-module_exit(davinci_vc_exit);
+module_platform_driver_probe(davinci_vc_driver, davinci_vc_probe);
MODULE_AUTHOR("Miguel Aguilar");
MODULE_DESCRIPTION("Texas Instruments DaVinci Voice Codec Core Interface");
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index 00b8b0f3dfb..193cf168ba8 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -24,6 +24,8 @@
#include <linux/jiffies.h>
#include <linux/bitops.h>
#include <linux/fs.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/mfd/core.h>
@@ -31,16 +33,11 @@
#include <linux/mfd/abx500/ab8500.h>
#include <linux/regulator/db8500-prcmu.h>
#include <linux/regulator/machine.h>
-#include <asm/hardware/gic.h>
-#include <mach/hardware.h>
-#include <mach/irqs.h>
-#include <mach/db8500-regs.h>
-#include <mach/id.h>
+#include <linux/cpufreq.h>
+#include <linux/platform_data/ux500_wdt.h>
+#include <linux/platform_data/db8500_thermal.h>
#include "dbx500-prcmu-regs.h"
-/* Offset for the firmware version within the TCPM */
-#define PRCMU_FW_VERSION_OFFSET 0xA4
-
/* Index of different voltages to be used when accessing AVSData */
#define PRCM_AVS_BASE 0x2FC
#define PRCM_AVS_VBB_RET (PRCM_AVS_BASE + 0x0)
@@ -215,10 +212,8 @@
#define PRCM_REQ_MB5_I2C_HW_BITS (PRCM_REQ_MB5 + 0x1)
#define PRCM_REQ_MB5_I2C_REG (PRCM_REQ_MB5 + 0x2)
#define PRCM_REQ_MB5_I2C_VAL (PRCM_REQ_MB5 + 0x3)
-#define PRCMU_I2C_WRITE(slave) \
- (((slave) << 1) | (cpu_is_u8500v2() ? BIT(6) : 0))
-#define PRCMU_I2C_READ(slave) \
- (((slave) << 1) | BIT(0) | (cpu_is_u8500v2() ? BIT(6) : 0))
+#define PRCMU_I2C_WRITE(slave) (((slave) << 1) | BIT(6))
+#define PRCMU_I2C_READ(slave) (((slave) << 1) | BIT(0) | BIT(6))
#define PRCMU_I2C_STOP_EN BIT(3)
/* Mailbox 5 ACKs */
@@ -280,8 +275,34 @@ static struct irq_domain *db8500_irq_domain;
* the bits in the bit field are not. (The bits also have a tendency to move
* around, to further complicate matters.)
*/
-#define IRQ_INDEX(_name) ((IRQ_PRCMU_##_name) - IRQ_PRCMU_BASE)
+#define IRQ_INDEX(_name) ((IRQ_PRCMU_##_name))
#define IRQ_ENTRY(_name)[IRQ_INDEX(_name)] = (WAKEUP_BIT_##_name)
+
+#define IRQ_PRCMU_RTC 0
+#define IRQ_PRCMU_RTT0 1
+#define IRQ_PRCMU_RTT1 2
+#define IRQ_PRCMU_HSI0 3
+#define IRQ_PRCMU_HSI1 4
+#define IRQ_PRCMU_CA_WAKE 5
+#define IRQ_PRCMU_USB 6
+#define IRQ_PRCMU_ABB 7
+#define IRQ_PRCMU_ABB_FIFO 8
+#define IRQ_PRCMU_ARM 9
+#define IRQ_PRCMU_MODEM_SW_RESET_REQ 10
+#define IRQ_PRCMU_GPIO0 11
+#define IRQ_PRCMU_GPIO1 12
+#define IRQ_PRCMU_GPIO2 13
+#define IRQ_PRCMU_GPIO3 14
+#define IRQ_PRCMU_GPIO4 15
+#define IRQ_PRCMU_GPIO5 16
+#define IRQ_PRCMU_GPIO6 17
+#define IRQ_PRCMU_GPIO7 18
+#define IRQ_PRCMU_GPIO8 19
+#define IRQ_PRCMU_CA_SLEEP 20
+#define IRQ_PRCMU_HOTMON_LOW 21
+#define IRQ_PRCMU_HOTMON_HIGH 22
+#define NUM_PRCMU_WAKEUPS 23
+
static u32 prcmu_irq_bit[NUM_PRCMU_WAKEUPS] = {
IRQ_ENTRY(RTC),
IRQ_ENTRY(RTT0),
@@ -420,18 +441,16 @@ static struct {
static atomic_t ac_wake_req_state = ATOMIC_INIT(0);
-/* Functions definition */
-static void compute_armss_rate(void);
-
/* Spinlocks */
static DEFINE_SPINLOCK(prcmu_lock);
static DEFINE_SPINLOCK(clkout_lock);
/* Global var to runtime determine TCDM base for v2 or v1 */
static __iomem void *tcdm_base;
+static __iomem void *prcmu_base;
struct clk_mgt {
- void __iomem *reg;
+ u32 offset;
u32 pllsw;
int branch;
bool clk38div;
@@ -447,7 +466,7 @@ static DEFINE_SPINLOCK(clk_mgt_lock);
#define CLK_MGT_ENTRY(_name, _branch, _clk38div)[PRCMU_##_name] = \
{ (PRCM_##_name##_MGT), 0 , _branch, _clk38div}
-struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = {
+static struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = {
CLK_MGT_ENTRY(SGACLK, PLL_DIV, false),
CLK_MGT_ENTRY(UARTCLK, PLL_FIX, true),
CLK_MGT_ENTRY(MSP02CLK, PLL_FIX, true),
@@ -606,9 +625,9 @@ int db8500_prcmu_set_display_clocks(void)
while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
cpu_relax();
- writel(PRCMU_DSI_CLOCK_SETTING, PRCM_HDMICLK_MGT);
- writel(PRCMU_DSI_LP_CLOCK_SETTING, PRCM_TVCLK_MGT);
- writel(PRCMU_DPI_CLOCK_SETTING, PRCM_LCDCLK_MGT);
+ writel(PRCMU_DSI_CLOCK_SETTING, prcmu_base + PRCM_HDMICLK_MGT);
+ writel(PRCMU_DSI_LP_CLOCK_SETTING, prcmu_base + PRCM_TVCLK_MGT);
+ writel(PRCMU_DPI_CLOCK_SETTING, prcmu_base + PRCM_LCDCLK_MGT);
/* Release the HW semaphore. */
writel(0, PRCM_SEM);
@@ -620,7 +639,7 @@ int db8500_prcmu_set_display_clocks(void)
u32 db8500_prcmu_read(unsigned int reg)
{
- return readl(_PRCMU_BASE + reg);
+ return readl(prcmu_base + reg);
}
void db8500_prcmu_write(unsigned int reg, u32 value)
@@ -628,7 +647,7 @@ void db8500_prcmu_write(unsigned int reg, u32 value)
unsigned long flags;
spin_lock_irqsave(&prcmu_lock, flags);
- writel(value, (_PRCMU_BASE + reg));
+ writel(value, (prcmu_base + reg));
spin_unlock_irqrestore(&prcmu_lock, flags);
}
@@ -638,9 +657,9 @@ void db8500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value)
unsigned long flags;
spin_lock_irqsave(&prcmu_lock, flags);
- val = readl(_PRCMU_BASE + reg);
+ val = readl(prcmu_base + reg);
val = ((val & ~mask) | (value & mask));
- writel(val, (_PRCMU_BASE + reg));
+ writel(val, (prcmu_base + reg));
spin_unlock_irqrestore(&prcmu_lock, flags);
}
@@ -800,119 +819,6 @@ u8 db8500_prcmu_get_power_state_result(void)
return readb(tcdm_base + PRCM_ACK_MB0_AP_PWRSTTR_STATUS);
}
-/* This function decouple the gic from the prcmu */
-int db8500_prcmu_gic_decouple(void)
-{
- u32 val = readl(PRCM_A9_MASK_REQ);
-
- /* Set bit 0 register value to 1 */
- writel(val | PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ,
- PRCM_A9_MASK_REQ);
-
- /* Make sure the register is updated */
- readl(PRCM_A9_MASK_REQ);
-
- /* Wait a few cycles for the gic mask completion */
- udelay(1);
-
- return 0;
-}
-
-/* This function recouple the gic with the prcmu */
-int db8500_prcmu_gic_recouple(void)
-{
- u32 val = readl(PRCM_A9_MASK_REQ);
-
- /* Set bit 0 register value to 0 */
- writel(val & ~PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ, PRCM_A9_MASK_REQ);
-
- return 0;
-}
-
-#define PRCMU_GIC_NUMBER_REGS 5
-
-/*
- * This function checks if there are pending irq on the gic. It only
- * makes sense if the gic has been decoupled before with the
- * db8500_prcmu_gic_decouple function. Disabling an interrupt only
- * disables the forwarding of the interrupt to any CPU interface. It
- * does not prevent the interrupt from changing state, for example
- * becoming pending, or active and pending if it is already
- * active. Hence, we have to check the interrupt is pending *and* is
- * active.
- */
-bool db8500_prcmu_gic_pending_irq(void)
-{
- u32 pr; /* Pending register */
- u32 er; /* Enable register */
- void __iomem *dist_base = __io_address(U8500_GIC_DIST_BASE);
- int i;
-
- /* 5 registers. STI & PPI not skipped */
- for (i = 0; i < PRCMU_GIC_NUMBER_REGS; i++) {
-
- pr = readl_relaxed(dist_base + GIC_DIST_PENDING_SET + i * 4);
- er = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
-
- if (pr & er)
- return true; /* There is a pending interrupt */
- }
-
- return false;
-}
-
-/*
- * This function checks if there are pending interrupt on the
- * prcmu which has been delegated to monitor the irqs with the
- * db8500_prcmu_copy_gic_settings function.
- */
-bool db8500_prcmu_pending_irq(void)
-{
- u32 it, im;
- int i;
-
- for (i = 0; i < PRCMU_GIC_NUMBER_REGS - 1; i++) {
- it = readl(PRCM_ARMITVAL31TO0 + i * 4);
- im = readl(PRCM_ARMITMSK31TO0 + i * 4);
- if (it & im)
- return true; /* There is a pending interrupt */
- }
-
- return false;
-}
-
-/*
- * This function checks if the specified cpu is in in WFI. It's usage
- * makes sense only if the gic is decoupled with the db8500_prcmu_gic_decouple
- * function. Of course passing smp_processor_id() to this function will
- * always return false...
- */
-bool db8500_prcmu_is_cpu_in_wfi(int cpu)
-{
- return readl(PRCM_ARM_WFI_STANDBY) & cpu ? PRCM_ARM_WFI_STANDBY_WFI1 :
- PRCM_ARM_WFI_STANDBY_WFI0;
-}
-
-/*
- * This function copies the gic SPI settings to the prcmu in order to
- * monitor them and abort/finish the retention/off sequence or state.
- */
-int db8500_prcmu_copy_gic_settings(void)
-{
- u32 er; /* Enable register */
- void __iomem *dist_base = __io_address(U8500_GIC_DIST_BASE);
- int i;
-
- /* We skip the STI and PPI */
- for (i = 0; i < PRCMU_GIC_NUMBER_REGS - 1; i++) {
- er = readl_relaxed(dist_base +
- GIC_DIST_ENABLE_SET + (i + 1) * 4);
- writel(er, PRCM_ARMITMSK31TO0 + i * 4);
- }
-
- return 0;
-}
-
/* This function should only be called while mb0_transfer.lock is held. */
static void config_wakeups(void)
{
@@ -1019,7 +925,6 @@ int db8500_prcmu_set_arm_opp(u8 opp)
(mb1_transfer.ack.arm_opp != opp))
r = -EIO;
- compute_armss_rate();
mutex_unlock(&mb1_transfer.lock);
return r;
@@ -1052,12 +957,13 @@ int db8500_prcmu_get_ddr_opp(void)
*
* This function sets the operating point of the DDR.
*/
+static bool enable_set_ddr_opp;
int db8500_prcmu_set_ddr_opp(u8 opp)
{
if (opp < DDR_100_OPP || opp > DDR_25_OPP)
return -EINVAL;
/* Changing the DDR OPP can hang the hardware pre-v21 */
- if (cpu_is_u8500v20_or_later() && !cpu_is_u8500v20())
+ if (enable_set_ddr_opp)
writeb(opp, PRCM_DDR_SUBSYS_APE_MINBW);
return 0;
@@ -1066,7 +972,7 @@ int db8500_prcmu_set_ddr_opp(u8 opp)
/* Divide the frequency of certain clocks by 2 for APE_50_PARTLY_25_OPP. */
static void request_even_slower_clocks(bool enable)
{
- void __iomem *clock_reg[] = {
+ u32 clock_reg[] = {
PRCM_ACLK_MGT,
PRCM_DMACLK_MGT
};
@@ -1083,7 +989,7 @@ static void request_even_slower_clocks(bool enable)
u32 val;
u32 div;
- val = readl(clock_reg[i]);
+ val = readl(prcmu_base + clock_reg[i]);
div = (val & PRCM_CLK_MGT_CLKPLLDIV_MASK);
if (enable) {
if ((div <= 1) || (div > 15)) {
@@ -1099,7 +1005,7 @@ static void request_even_slower_clocks(bool enable)
}
val = ((val & ~PRCM_CLK_MGT_CLKPLLDIV_MASK) |
(div & PRCM_CLK_MGT_CLKPLLDIV_MASK));
- writel(val, clock_reg[i]);
+ writel(val, prcmu_base + clock_reg[i]);
}
unlock_and_return:
@@ -1169,12 +1075,12 @@ int db8500_prcmu_get_ape_opp(void)
}
/**
- * prcmu_request_ape_opp_100_voltage - Request APE OPP 100% voltage
+ * db8500_prcmu_request_ape_opp_100_voltage - Request APE OPP 100% voltage
* @enable: true to request the higher voltage, false to drop a request.
*
* Calls to this function to enable and disable requests must be balanced.
*/
-int prcmu_request_ape_opp_100_voltage(bool enable)
+int db8500_prcmu_request_ape_opp_100_voltage(bool enable)
{
int r = 0;
u8 header;
@@ -1453,14 +1359,14 @@ static int request_clock(u8 clock, bool enable)
while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
cpu_relax();
- val = readl(clk_mgt[clock].reg);
+ val = readl(prcmu_base + clk_mgt[clock].offset);
if (enable) {
val |= (PRCM_CLK_MGT_CLKEN | clk_mgt[clock].pllsw);
} else {
clk_mgt[clock].pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK);
val &= ~(PRCM_CLK_MGT_CLKEN | PRCM_CLK_MGT_CLKPLLSW_MASK);
}
- writel(val, clk_mgt[clock].reg);
+ writel(val, prcmu_base + clk_mgt[clock].offset);
/* Release the HW semaphore. */
writel(0, PRCM_SEM);
@@ -1636,7 +1542,7 @@ static unsigned long clock_rate(u8 clock)
u32 pllsw;
unsigned long rate = ROOT_CLOCK_RATE;
- val = readl(clk_mgt[clock].reg);
+ val = readl(prcmu_base + clk_mgt[clock].offset);
if (val & PRCM_CLK_MGT_CLK38) {
if (clk_mgt[clock].clk38div && (val & PRCM_CLK_MGT_CLK38DIV))
@@ -1669,13 +1575,8 @@ static unsigned long clock_rate(u8 clock)
else
return 0;
}
-static unsigned long latest_armss_rate;
-static unsigned long armss_rate(void)
-{
- return latest_armss_rate;
-}
-static void compute_armss_rate(void)
+static unsigned long armss_rate(void)
{
u32 r;
unsigned long rate;
@@ -1700,7 +1601,7 @@ static void compute_armss_rate(void)
rate = pll_rate(PRCM_PLLARM_FREQ, ROOT_CLOCK_RATE, PLL_DIV);
}
- latest_armss_rate = rate;
+ return rate;
}
static unsigned long dsiclk_rate(u8 n)
@@ -1713,6 +1614,8 @@ static unsigned long dsiclk_rate(u8 n)
if (divsel == PRCM_DSI_PLLOUT_SEL_OFF)
divsel = dsiclk[n].divsel;
+ else
+ dsiclk[n].divsel = divsel;
switch (divsel) {
case PRCM_DSI_PLLOUT_SEL_PHI_4:
@@ -1797,7 +1700,7 @@ static long round_clock_rate(u8 clock, unsigned long rate)
unsigned long src_rate;
long rounded_rate;
- val = readl(clk_mgt[clock].reg);
+ val = readl(prcmu_base + clk_mgt[clock].offset);
src_rate = clock_source_rate((val | clk_mgt[clock].pllsw),
clk_mgt[clock].branch);
div = clock_divider(src_rate, rate);
@@ -1820,6 +1723,34 @@ static long round_clock_rate(u8 clock, unsigned long rate)
return rounded_rate;
}
+/* CPU FREQ table, may be changed due to if MAX_OPP is supported. */
+static struct cpufreq_frequency_table db8500_cpufreq_table[] = {
+ { .frequency = 200000, .driver_data = ARM_EXTCLK,},
+ { .frequency = 400000, .driver_data = ARM_50_OPP,},
+ { .frequency = 800000, .driver_data = ARM_100_OPP,},
+ { .frequency = CPUFREQ_TABLE_END,}, /* To be used for MAX_OPP. */
+ { .frequency = CPUFREQ_TABLE_END,},
+};
+
+static long round_armss_rate(unsigned long rate)
+{
+ struct cpufreq_frequency_table *pos;
+ long freq = 0;
+
+ /* cpufreq table frequencies is in KHz. */
+ rate = rate / 1000;
+
+ /* Find the corresponding arm opp from the cpufreq table. */
+ cpufreq_for_each_entry(pos, db8500_cpufreq_table) {
+ freq = pos->frequency;
+ if (freq == rate)
+ break;
+ }
+
+ /* Return the last valid value, even if a match was not found. */
+ return freq * 1000;
+}
+
#define MIN_PLL_VCO_RATE 600000000ULL
#define MAX_PLL_VCO_RATE 1680640000ULL
@@ -1891,6 +1822,8 @@ long prcmu_round_clock_rate(u8 clock, unsigned long rate)
{
if (clock < PRCMU_NUM_REG_CLOCKS)
return round_clock_rate(clock, rate);
+ else if (clock == PRCMU_ARMSS)
+ return round_armss_rate(rate);
else if (clock == PRCMU_PLLDSI)
return round_plldsi_rate(rate);
else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK))
@@ -1914,7 +1847,7 @@ static void set_clock_rate(u8 clock, unsigned long rate)
while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
cpu_relax();
- val = readl(clk_mgt[clock].reg);
+ val = readl(prcmu_base + clk_mgt[clock].offset);
src_rate = clock_source_rate((val | clk_mgt[clock].pllsw),
clk_mgt[clock].branch);
div = clock_divider(src_rate, rate);
@@ -1942,7 +1875,7 @@ static void set_clock_rate(u8 clock, unsigned long rate)
val &= ~PRCM_CLK_MGT_CLKPLLDIV_MASK;
val |= min(div, (u32)31);
}
- writel(val, clk_mgt[clock].reg);
+ writel(val, prcmu_base + clk_mgt[clock].offset);
/* Release the HW semaphore. */
writel(0, PRCM_SEM);
@@ -1950,6 +1883,25 @@ static void set_clock_rate(u8 clock, unsigned long rate)
spin_unlock_irqrestore(&clk_mgt_lock, flags);
}
+static int set_armss_rate(unsigned long rate)
+{
+ struct cpufreq_frequency_table *pos;
+
+ /* cpufreq table frequencies is in KHz. */
+ rate = rate / 1000;
+
+ /* Find the corresponding arm opp from the cpufreq table. */
+ cpufreq_for_each_entry(pos, db8500_cpufreq_table)
+ if (pos->frequency == rate)
+ break;
+
+ if (pos->frequency != rate)
+ return -EINVAL;
+
+ /* Set the new arm opp. */
+ return db8500_prcmu_set_arm_opp(pos->driver_data);
+}
+
static int set_plldsi_rate(unsigned long rate)
{
unsigned long src_rate;
@@ -2030,6 +1982,8 @@ int prcmu_set_clock_rate(u8 clock, unsigned long rate)
{
if (clock < PRCMU_NUM_REG_CLOCKS)
set_clock_rate(clock, rate);
+ else if (clock == PRCMU_ARMSS)
+ return set_armss_rate(rate);
else if (clock == PRCMU_PLLDSI)
return set_plldsi_rate(rate);
else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK))
@@ -2166,21 +2120,25 @@ int db8500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
sleep_auto_off ? A9WDOG_AUTO_OFF_EN :
A9WDOG_AUTO_OFF_DIS);
}
+EXPORT_SYMBOL(db8500_prcmu_config_a9wdog);
int db8500_prcmu_enable_a9wdog(u8 id)
{
return prcmu_a9wdog(MB4H_A9WDOG_EN, id, 0, 0, 0);
}
+EXPORT_SYMBOL(db8500_prcmu_enable_a9wdog);
int db8500_prcmu_disable_a9wdog(u8 id)
{
return prcmu_a9wdog(MB4H_A9WDOG_DIS, id, 0, 0, 0);
}
+EXPORT_SYMBOL(db8500_prcmu_disable_a9wdog);
int db8500_prcmu_kick_a9wdog(u8 id)
{
return prcmu_a9wdog(MB4H_A9WDOG_KICK, id, 0, 0, 0);
}
+EXPORT_SYMBOL(db8500_prcmu_kick_a9wdog);
/*
* timeout is 28 bit, in ms.
@@ -2198,6 +2156,7 @@ int db8500_prcmu_load_a9wdog(u8 id, u32 timeout)
(u8)((timeout >> 12) & 0xff),
(u8)((timeout >> 20) & 0xff));
}
+EXPORT_SYMBOL(db8500_prcmu_load_a9wdog);
/**
* prcmu_abb_read() - Read register value(s) from the ABB.
@@ -2341,9 +2300,6 @@ int prcmu_ac_wake_req(void)
if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
msecs_to_jiffies(5000))) {
-#if defined(CONFIG_DBX500_PRCMU_DEBUG)
- db8500_prcmu_debug_dump(__func__, true, true);
-#endif
pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
__func__);
ret = -EFAULT;
@@ -2357,7 +2313,7 @@ unlock_and_return:
/**
* prcmu_ac_sleep_req - called when ARM no longer needs to talk to modem
*/
-void prcmu_ac_sleep_req()
+void prcmu_ac_sleep_req(void)
{
u32 val;
@@ -2478,7 +2434,7 @@ static bool read_mailbox_0(void)
for (n = 0; n < NUM_PRCMU_WAKEUPS; n++) {
if (ev & prcmu_irq_bit[n])
- generic_handle_irq(IRQ_PRCMU_BASE + n);
+ generic_handle_irq(irq_find_mapping(db8500_irq_domain, n));
}
r = true;
break;
@@ -2660,21 +2616,43 @@ static struct irq_chip prcmu_irq_chip = {
.irq_unmask = prcmu_irq_unmask,
};
-static char *fw_project_name(u8 project)
+static __init char *fw_project_name(u32 project)
{
switch (project) {
case PRCMU_FW_PROJECT_U8500:
return "U8500";
- case PRCMU_FW_PROJECT_U8500_C2:
- return "U8500 C2";
+ case PRCMU_FW_PROJECT_U8400:
+ return "U8400";
case PRCMU_FW_PROJECT_U9500:
return "U9500";
- case PRCMU_FW_PROJECT_U9500_C2:
- return "U9500 C2";
+ case PRCMU_FW_PROJECT_U8500_MBB:
+ return "U8500 MBB";
+ case PRCMU_FW_PROJECT_U8500_C1:
+ return "U8500 C1";
+ case PRCMU_FW_PROJECT_U8500_C2:
+ return "U8500 C2";
+ case PRCMU_FW_PROJECT_U8500_C3:
+ return "U8500 C3";
+ case PRCMU_FW_PROJECT_U8500_C4:
+ return "U8500 C4";
+ case PRCMU_FW_PROJECT_U9500_MBL:
+ return "U9500 MBL";
+ case PRCMU_FW_PROJECT_U8500_MBL:
+ return "U8500 MBL";
+ case PRCMU_FW_PROJECT_U8500_MBL2:
+ return "U8500 MBL2";
case PRCMU_FW_PROJECT_U8520:
- return "U8520";
+ return "U8520 MBL";
case PRCMU_FW_PROJECT_U8420:
return "U8420";
+ case PRCMU_FW_PROJECT_U9540:
+ return "U9540";
+ case PRCMU_FW_PROJECT_A9420:
+ return "A9420";
+ case PRCMU_FW_PROJECT_L8540:
+ return "L8540";
+ case PRCMU_FW_PROJECT_L8580:
+ return "L8580";
default:
return "Unknown";
}
@@ -2691,49 +2669,80 @@ static int db8500_irq_map(struct irq_domain *d, unsigned int virq,
}
static struct irq_domain_ops db8500_irq_ops = {
- .map = db8500_irq_map,
- .xlate = irq_domain_xlate_twocell,
+ .map = db8500_irq_map,
+ .xlate = irq_domain_xlate_twocell,
};
static int db8500_irq_init(struct device_node *np)
{
- db8500_irq_domain = irq_domain_add_legacy(
- np, NUM_PRCMU_WAKEUPS, IRQ_PRCMU_BASE,
- 0, &db8500_irq_ops, NULL);
+ int i;
+
+ db8500_irq_domain = irq_domain_add_simple(
+ np, NUM_PRCMU_WAKEUPS, 0,
+ &db8500_irq_ops, NULL);
if (!db8500_irq_domain) {
pr_err("Failed to create irqdomain\n");
return -ENOSYS;
}
+ /* All wakeups will be used, so create mappings for all */
+ for (i = 0; i < NUM_PRCMU_WAKEUPS; i++)
+ irq_create_mapping(db8500_irq_domain, i);
+
return 0;
}
-void __init db8500_prcmu_early_init(void)
-{
- if (cpu_is_u8500v2()) {
- void *tcpm_base = ioremap_nocache(U8500_PRCMU_TCPM_BASE, SZ_4K);
-
- if (tcpm_base != NULL) {
- u32 version;
- version = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET);
- fw_info.version.project = version & 0xFF;
- fw_info.version.api_version = (version >> 8) & 0xFF;
- fw_info.version.func_version = (version >> 16) & 0xFF;
- fw_info.version.errata = (version >> 24) & 0xFF;
- fw_info.valid = true;
- pr_info("PRCMU firmware: %s, version %d.%d.%d\n",
- fw_project_name(fw_info.version.project),
- (version >> 8) & 0xFF, (version >> 16) & 0xFF,
- (version >> 24) & 0xFF);
- iounmap(tcpm_base);
- }
+static void dbx500_fw_version_init(struct platform_device *pdev,
+ u32 version_offset)
+{
+ struct resource *res;
+ void __iomem *tcpm_base;
+ u32 version;
- tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE);
- } else {
- pr_err("prcmu: Unsupported chip version\n");
- BUG();
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "prcmu-tcpm");
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Error: no prcmu tcpm memory region provided\n");
+ return;
}
+ tcpm_base = ioremap(res->start, resource_size(res));
+ if (!tcpm_base) {
+ dev_err(&pdev->dev, "no prcmu tcpm mem region provided\n");
+ return;
+ }
+
+ version = readl(tcpm_base + version_offset);
+ fw_info.version.project = (version & 0xFF);
+ fw_info.version.api_version = (version >> 8) & 0xFF;
+ fw_info.version.func_version = (version >> 16) & 0xFF;
+ fw_info.version.errata = (version >> 24) & 0xFF;
+ strncpy(fw_info.version.project_name,
+ fw_project_name(fw_info.version.project),
+ PRCMU_FW_PROJECT_NAME_LEN);
+ fw_info.valid = true;
+ pr_info("PRCMU firmware: %s(%d), version %d.%d.%d\n",
+ fw_info.version.project_name,
+ fw_info.version.project,
+ fw_info.version.api_version,
+ fw_info.version.func_version,
+ fw_info.version.errata);
+ iounmap(tcpm_base);
+}
+
+void __init db8500_prcmu_early_init(u32 phy_base, u32 size)
+{
+ /*
+ * This is a temporary remap to bring up the clocks. It is
+ * subsequently replaces with a real remap. After the merge of
+ * the mailbox subsystem all of this early code goes away, and the
+ * clock driver can probe independently. An early initcall will
+ * still be needed, but it can be diverted into drivers/clk/ux500.
+ */
+ prcmu_base = ioremap(phy_base, size);
+ if (!prcmu_base)
+ pr_err("%s: ioremap() of prcmu registers failed!\n", __func__);
spin_lock_init(&mb0_transfer.lock);
spin_lock_init(&mb0_transfer.dbb_irqs_lock);
@@ -2754,8 +2763,6 @@ void __init db8500_prcmu_early_init(void)
init_completion(&mb5_transfer.work);
INIT_WORK(&mb0_transfer.mask_work, prcmu_mask_work);
-
- compute_armss_rate();
}
static void __init init_prcm_registers(void)
@@ -3002,15 +3009,68 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
},
};
-static struct resource ab8500_resources[] = {
- [0] = {
- .start = IRQ_DB8500_AB8500,
- .end = IRQ_DB8500_AB8500,
- .flags = IORESOURCE_IRQ
- }
+static struct ux500_wdt_data db8500_wdt_pdata = {
+ .timeout = 600, /* 10 minutes */
+ .has_28_bits_resolution = true,
+};
+/*
+ * Thermal Sensor
+ */
+
+static struct resource db8500_thsens_resources[] = {
+ {
+ .name = "IRQ_HOTMON_LOW",
+ .start = IRQ_PRCMU_HOTMON_LOW,
+ .end = IRQ_PRCMU_HOTMON_LOW,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "IRQ_HOTMON_HIGH",
+ .start = IRQ_PRCMU_HOTMON_HIGH,
+ .end = IRQ_PRCMU_HOTMON_HIGH,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct db8500_thsens_platform_data db8500_thsens_data = {
+ .trip_points[0] = {
+ .temp = 70000,
+ .type = THERMAL_TRIP_ACTIVE,
+ .cdev_name = {
+ [0] = "thermal-cpufreq-0",
+ },
+ },
+ .trip_points[1] = {
+ .temp = 75000,
+ .type = THERMAL_TRIP_ACTIVE,
+ .cdev_name = {
+ [0] = "thermal-cpufreq-0",
+ },
+ },
+ .trip_points[2] = {
+ .temp = 80000,
+ .type = THERMAL_TRIP_ACTIVE,
+ .cdev_name = {
+ [0] = "thermal-cpufreq-0",
+ },
+ },
+ .trip_points[3] = {
+ .temp = 85000,
+ .type = THERMAL_TRIP_CRITICAL,
+ },
+ .num_trips = 4,
+};
+
+static const struct mfd_cell common_prcmu_devs[] = {
+ {
+ .name = "ux500_wdt",
+ .platform_data = &db8500_wdt_pdata,
+ .pdata_size = sizeof(db8500_wdt_pdata),
+ .id = -1,
+ },
};
-static struct mfd_cell db8500_prcmu_devs[] = {
+static const struct mfd_cell db8500_prcmu_devs[] = {
{
.name = "db8500-prcmu-regulators",
.of_compatible = "stericsson,db8500-prcmu-regulator",
@@ -3018,41 +3078,104 @@ static struct mfd_cell db8500_prcmu_devs[] = {
.pdata_size = sizeof(db8500_regulators),
},
{
- .name = "cpufreq-u8500",
- .of_compatible = "stericsson,cpufreq-u8500",
+ .name = "cpufreq-ux500",
+ .of_compatible = "stericsson,cpufreq-ux500",
+ .platform_data = &db8500_cpufreq_table,
+ .pdata_size = sizeof(db8500_cpufreq_table),
+ },
+ {
+ .name = "cpuidle-dbx500",
+ .of_compatible = "stericsson,cpuidle-dbx500",
},
{
+ .name = "db8500-thermal",
+ .num_resources = ARRAY_SIZE(db8500_thsens_resources),
+ .resources = db8500_thsens_resources,
+ .platform_data = &db8500_thsens_data,
+ .pdata_size = sizeof(db8500_thsens_data),
+ },
+};
+
+static void db8500_prcmu_update_cpufreq(void)
+{
+ if (prcmu_has_arm_maxopp()) {
+ db8500_cpufreq_table[3].frequency = 1000000;
+ db8500_cpufreq_table[3].driver_data = ARM_MAX_OPP;
+ }
+}
+
+static int db8500_prcmu_register_ab8500(struct device *parent,
+ struct ab8500_platform_data *pdata)
+{
+ struct device_node *np;
+ struct resource ab8500_resource;
+ const struct mfd_cell ab8500_cell = {
.name = "ab8500-core",
.of_compatible = "stericsson,ab8500",
- .num_resources = ARRAY_SIZE(ab8500_resources),
- .resources = ab8500_resources,
.id = AB8500_VERSION_AB8500,
- },
-};
+ .platform_data = pdata,
+ .pdata_size = sizeof(struct ab8500_platform_data),
+ .resources = &ab8500_resource,
+ .num_resources = 1,
+ };
+
+ if (!parent->of_node)
+ return -ENODEV;
+
+ /* Look up the device node, sneak the IRQ out of it */
+ for_each_child_of_node(parent->of_node, np) {
+ if (of_device_is_compatible(np, ab8500_cell.of_compatible))
+ break;
+ }
+ if (!np) {
+ dev_info(parent, "could not find AB8500 node in the device tree\n");
+ return -ENODEV;
+ }
+ of_irq_to_resource_table(np, &ab8500_resource, 1);
+
+ return mfd_add_devices(parent, 0, &ab8500_cell, 1, NULL, 0, NULL);
+}
/**
* prcmu_fw_init - arch init call for the Linux PRCMU fw init logic
*
*/
-static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
+static int db8500_prcmu_probe(struct platform_device *pdev)
{
- struct ab8500_platform_data *ab8500_platdata = pdev->dev.platform_data;
struct device_node *np = pdev->dev.of_node;
- int irq = 0, err = 0, i;
-
- if (ux500_is_svp())
- return -ENODEV;
-
+ struct prcmu_pdata *pdata = dev_get_platdata(&pdev->dev);
+ int irq = 0, err = 0;
+ struct resource *res;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu");
+ if (!res) {
+ dev_err(&pdev->dev, "no prcmu memory region provided\n");
+ return -ENOENT;
+ }
+ prcmu_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!prcmu_base) {
+ dev_err(&pdev->dev,
+ "failed to ioremap prcmu register memory\n");
+ return -ENOENT;
+ }
init_prcm_registers();
+ dbx500_fw_version_init(pdev, pdata->version_offset);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm");
+ if (!res) {
+ dev_err(&pdev->dev, "no prcmu tcdm region provided\n");
+ return -ENOENT;
+ }
+ tcdm_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
/* Clean up the mailbox interrupts after pre-kernel code. */
writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
- if (np)
- irq = platform_get_irq(pdev, 0);
-
- if (!np || irq <= 0)
- irq = IRQ_DB8500_PRCMU1;
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev, "no prcmu irq provided\n");
+ return -ENOENT;
+ }
err = request_threaded_irq(irq, prcmu_irq_handler,
prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL);
@@ -3064,23 +3187,36 @@ static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
db8500_irq_init(np);
- for (i = 0; i < ARRAY_SIZE(db8500_prcmu_devs); i++) {
- if (!strcmp(db8500_prcmu_devs[i].name, "ab8500-core")) {
- db8500_prcmu_devs[i].platform_data = ab8500_platdata;
- db8500_prcmu_devs[i].pdata_size = sizeof(struct ab8500_platform_data);
- }
- }
+ prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
- if (cpu_is_u8500v20_or_later())
- prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
+ db8500_prcmu_update_cpufreq();
- err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
- ARRAY_SIZE(db8500_prcmu_devs), NULL, 0, NULL);
+ err = mfd_add_devices(&pdev->dev, 0, common_prcmu_devs,
+ ARRAY_SIZE(common_prcmu_devs), NULL, 0, db8500_irq_domain);
if (err) {
pr_err("prcmu: Failed to add subdevices\n");
return err;
}
+ /* TODO: Remove restriction when clk definitions are available. */
+ if (!of_machine_is_compatible("st-ericsson,u8540")) {
+ err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
+ ARRAY_SIZE(db8500_prcmu_devs), NULL, 0,
+ db8500_irq_domain);
+ if (err) {
+ mfd_remove_devices(&pdev->dev);
+ pr_err("prcmu: Failed to add subdevices\n");
+ goto no_irq_return;
+ }
+ }
+
+ err = db8500_prcmu_register_ab8500(&pdev->dev, pdata->ab_platdata);
+ if (err) {
+ mfd_remove_devices(&pdev->dev);
+ pr_err("prcmu: Failed to add ab8500 subdevice\n");
+ goto no_irq_return;
+ }
+
pr_info("DB8500 PRCMU initialized\n");
no_irq_return:
diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h
index 79c76ebdba5..7cc32a8ff01 100644
--- a/drivers/mfd/dbx500-prcmu-regs.h
+++ b/drivers/mfd/dbx500-prcmu-regs.h
@@ -13,136 +13,110 @@
#ifndef __DB8500_PRCMU_REGS_H
#define __DB8500_PRCMU_REGS_H
-#include <mach/hardware.h>
-
#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end))
-#define PRCM_CLK_MGT(_offset) (void __iomem *)(IO_ADDRESS(U8500_PRCMU_BASE) \
- + _offset)
-#define PRCM_ACLK_MGT PRCM_CLK_MGT(0x004)
-#define PRCM_SVACLK_MGT PRCM_CLK_MGT(0x008)
-#define PRCM_SIACLK_MGT PRCM_CLK_MGT(0x00C)
-#define PRCM_SGACLK_MGT PRCM_CLK_MGT(0x014)
-#define PRCM_UARTCLK_MGT PRCM_CLK_MGT(0x018)
-#define PRCM_MSP02CLK_MGT PRCM_CLK_MGT(0x01C)
-#define PRCM_I2CCLK_MGT PRCM_CLK_MGT(0x020)
-#define PRCM_SDMMCCLK_MGT PRCM_CLK_MGT(0x024)
-#define PRCM_SLIMCLK_MGT PRCM_CLK_MGT(0x028)
-#define PRCM_PER1CLK_MGT PRCM_CLK_MGT(0x02C)
-#define PRCM_PER2CLK_MGT PRCM_CLK_MGT(0x030)
-#define PRCM_PER3CLK_MGT PRCM_CLK_MGT(0x034)
-#define PRCM_PER5CLK_MGT PRCM_CLK_MGT(0x038)
-#define PRCM_PER6CLK_MGT PRCM_CLK_MGT(0x03C)
-#define PRCM_PER7CLK_MGT PRCM_CLK_MGT(0x040)
-#define PRCM_LCDCLK_MGT PRCM_CLK_MGT(0x044)
-#define PRCM_BMLCLK_MGT PRCM_CLK_MGT(0x04C)
-#define PRCM_HSITXCLK_MGT PRCM_CLK_MGT(0x050)
-#define PRCM_HSIRXCLK_MGT PRCM_CLK_MGT(0x054)
-#define PRCM_HDMICLK_MGT PRCM_CLK_MGT(0x058)
-#define PRCM_APEATCLK_MGT PRCM_CLK_MGT(0x05C)
-#define PRCM_APETRACECLK_MGT PRCM_CLK_MGT(0x060)
-#define PRCM_MCDECLK_MGT PRCM_CLK_MGT(0x064)
-#define PRCM_IPI2CCLK_MGT PRCM_CLK_MGT(0x068)
-#define PRCM_DSIALTCLK_MGT PRCM_CLK_MGT(0x06C)
-#define PRCM_DMACLK_MGT PRCM_CLK_MGT(0x074)
-#define PRCM_B2R2CLK_MGT PRCM_CLK_MGT(0x078)
-#define PRCM_TVCLK_MGT PRCM_CLK_MGT(0x07C)
-#define PRCM_UNIPROCLK_MGT PRCM_CLK_MGT(0x278)
-#define PRCM_SSPCLK_MGT PRCM_CLK_MGT(0x280)
-#define PRCM_RNGCLK_MGT PRCM_CLK_MGT(0x284)
-#define PRCM_UICCCLK_MGT PRCM_CLK_MGT(0x27C)
-#define PRCM_MSP1CLK_MGT PRCM_CLK_MGT(0x288)
-
-#define PRCM_ARM_PLLDIVPS (_PRCMU_BASE + 0x118)
+#define PRCM_ACLK_MGT (0x004)
+#define PRCM_SVAMMCSPCLK_MGT (0x008)
+#define PRCM_SIAMMDSPCLK_MGT (0x00C)
+#define PRCM_SGACLK_MGT (0x014)
+#define PRCM_UARTCLK_MGT (0x018)
+#define PRCM_MSP02CLK_MGT (0x01C)
+#define PRCM_I2CCLK_MGT (0x020)
+#define PRCM_SDMMCCLK_MGT (0x024)
+#define PRCM_SLIMCLK_MGT (0x028)
+#define PRCM_PER1CLK_MGT (0x02C)
+#define PRCM_PER2CLK_MGT (0x030)
+#define PRCM_PER3CLK_MGT (0x034)
+#define PRCM_PER5CLK_MGT (0x038)
+#define PRCM_PER6CLK_MGT (0x03C)
+#define PRCM_PER7CLK_MGT (0x040)
+#define PRCM_LCDCLK_MGT (0x044)
+#define PRCM_BMLCLK_MGT (0x04C)
+#define PRCM_HSITXCLK_MGT (0x050)
+#define PRCM_HSIRXCLK_MGT (0x054)
+#define PRCM_HDMICLK_MGT (0x058)
+#define PRCM_APEATCLK_MGT (0x05C)
+#define PRCM_APETRACECLK_MGT (0x060)
+#define PRCM_MCDECLK_MGT (0x064)
+#define PRCM_IPI2CCLK_MGT (0x068)
+#define PRCM_DSIALTCLK_MGT (0x06C)
+#define PRCM_DMACLK_MGT (0x074)
+#define PRCM_B2R2CLK_MGT (0x078)
+#define PRCM_TVCLK_MGT (0x07C)
+#define PRCM_UNIPROCLK_MGT (0x278)
+#define PRCM_SSPCLK_MGT (0x280)
+#define PRCM_RNGCLK_MGT (0x284)
+#define PRCM_UICCCLK_MGT (0x27C)
+#define PRCM_MSP1CLK_MGT (0x288)
+
+#define PRCM_ARM_PLLDIVPS (prcmu_base + 0x118)
#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE 0x3f
#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xf
-#define PRCM_PLLARM_LOCKP (_PRCMU_BASE + 0x0a8)
+#define PRCM_PLLARM_LOCKP (prcmu_base + 0x0a8)
#define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 0x2
-#define PRCM_ARM_CHGCLKREQ (_PRCMU_BASE + 0x114)
+#define PRCM_ARM_CHGCLKREQ (prcmu_base + 0x114)
#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ BIT(0)
#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_DIVSEL BIT(16)
-#define PRCM_PLLARM_ENABLE (_PRCMU_BASE + 0x98)
+#define PRCM_PLLARM_ENABLE (prcmu_base + 0x98)
#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE 0x1
#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON 0x100
-#define PRCM_ARMCLKFIX_MGT (_PRCMU_BASE + 0x0)
-#define PRCM_A9PL_FORCE_CLKEN (_PRCMU_BASE + 0x19C)
-#define PRCM_A9_RESETN_CLR (_PRCMU_BASE + 0x1f4)
-#define PRCM_A9_RESETN_SET (_PRCMU_BASE + 0x1f0)
-#define PRCM_ARM_LS_CLAMP (_PRCMU_BASE + 0x30c)
-#define PRCM_SRAM_A9 (_PRCMU_BASE + 0x308)
+#define PRCM_ARMCLKFIX_MGT (prcmu_base + 0x0)
+#define PRCM_A9PL_FORCE_CLKEN (prcmu_base + 0x19C)
+#define PRCM_A9_RESETN_CLR (prcmu_base + 0x1f4)
+#define PRCM_A9_RESETN_SET (prcmu_base + 0x1f0)
+#define PRCM_ARM_LS_CLAMP (prcmu_base + 0x30c)
+#define PRCM_SRAM_A9 (prcmu_base + 0x308)
#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN BIT(0)
#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN BIT(1)
-/* ARM WFI Standby signal register */
-#define PRCM_ARM_WFI_STANDBY (_PRCMU_BASE + 0x130)
-#define PRCM_ARM_WFI_STANDBY_WFI0 0x08
-#define PRCM_ARM_WFI_STANDBY_WFI1 0x10
-#define PRCM_IOCR (_PRCMU_BASE + 0x310)
-#define PRCM_IOCR_IOFORCE 0x1
-
/* CPU mailbox registers */
-#define PRCM_MBOX_CPU_VAL (_PRCMU_BASE + 0x0fc)
-#define PRCM_MBOX_CPU_SET (_PRCMU_BASE + 0x100)
-#define PRCM_MBOX_CPU_CLR (_PRCMU_BASE + 0x104)
-
-/* Dual A9 core interrupt management unit registers */
-#define PRCM_A9_MASK_REQ (_PRCMU_BASE + 0x328)
-#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ 0x1
-
-#define PRCM_A9_MASK_ACK (_PRCMU_BASE + 0x32c)
-#define PRCM_ARMITMSK31TO0 (_PRCMU_BASE + 0x11c)
-#define PRCM_ARMITMSK63TO32 (_PRCMU_BASE + 0x120)
-#define PRCM_ARMITMSK95TO64 (_PRCMU_BASE + 0x124)
-#define PRCM_ARMITMSK127TO96 (_PRCMU_BASE + 0x128)
-#define PRCM_POWER_STATE_VAL (_PRCMU_BASE + 0x25C)
-#define PRCM_ARMITVAL31TO0 (_PRCMU_BASE + 0x260)
-#define PRCM_ARMITVAL63TO32 (_PRCMU_BASE + 0x264)
-#define PRCM_ARMITVAL95TO64 (_PRCMU_BASE + 0x268)
-#define PRCM_ARMITVAL127TO96 (_PRCMU_BASE + 0x26C)
-
-#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334)
+#define PRCM_MBOX_CPU_VAL (prcmu_base + 0x0fc)
+#define PRCM_MBOX_CPU_SET (prcmu_base + 0x100)
+#define PRCM_MBOX_CPU_CLR (prcmu_base + 0x104)
+
+#define PRCM_HOSTACCESS_REQ (prcmu_base + 0x334)
#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1
#define PRCM_HOSTACCESS_REQ_WAKE_REQ BIT(16)
#define ARM_WAKEUP_MODEM 0x1
-#define PRCM_ARM_IT1_CLR (_PRCMU_BASE + 0x48C)
-#define PRCM_ARM_IT1_VAL (_PRCMU_BASE + 0x494)
-#define PRCM_HOLD_EVT (_PRCMU_BASE + 0x174)
+#define PRCM_ARM_IT1_CLR (prcmu_base + 0x48C)
+#define PRCM_ARM_IT1_VAL (prcmu_base + 0x494)
+#define PRCM_HOLD_EVT (prcmu_base + 0x174)
-#define PRCM_MOD_AWAKE_STATUS (_PRCMU_BASE + 0x4A0)
+#define PRCM_MOD_AWAKE_STATUS (prcmu_base + 0x4A0)
#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE BIT(0)
#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE BIT(1)
#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_VMODEM_OFF_ISO BIT(2)
-#define PRCM_ITSTATUS0 (_PRCMU_BASE + 0x148)
-#define PRCM_ITSTATUS1 (_PRCMU_BASE + 0x150)
-#define PRCM_ITSTATUS2 (_PRCMU_BASE + 0x158)
-#define PRCM_ITSTATUS3 (_PRCMU_BASE + 0x160)
-#define PRCM_ITSTATUS4 (_PRCMU_BASE + 0x168)
-#define PRCM_ITSTATUS5 (_PRCMU_BASE + 0x484)
-#define PRCM_ITCLEAR5 (_PRCMU_BASE + 0x488)
-#define PRCM_ARMIT_MASKXP70_IT (_PRCMU_BASE + 0x1018)
+#define PRCM_ITSTATUS0 (prcmu_base + 0x148)
+#define PRCM_ITSTATUS1 (prcmu_base + 0x150)
+#define PRCM_ITSTATUS2 (prcmu_base + 0x158)
+#define PRCM_ITSTATUS3 (prcmu_base + 0x160)
+#define PRCM_ITSTATUS4 (prcmu_base + 0x168)
+#define PRCM_ITSTATUS5 (prcmu_base + 0x484)
+#define PRCM_ITCLEAR5 (prcmu_base + 0x488)
+#define PRCM_ARMIT_MASKXP70_IT (prcmu_base + 0x1018)
/* System reset register */
-#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228)
+#define PRCM_APE_SOFTRST (prcmu_base + 0x228)
/* Level shifter and clamp control registers */
-#define PRCM_MMIP_LS_CLAMP_SET (_PRCMU_BASE + 0x420)
-#define PRCM_MMIP_LS_CLAMP_CLR (_PRCMU_BASE + 0x424)
+#define PRCM_MMIP_LS_CLAMP_SET (prcmu_base + 0x420)
+#define PRCM_MMIP_LS_CLAMP_CLR (prcmu_base + 0x424)
#define PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP BIT(11)
#define PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI BIT(22)
/* PRCMU clock/PLL/reset registers */
-#define PRCM_PLLSOC0_FREQ (_PRCMU_BASE + 0x080)
-#define PRCM_PLLSOC1_FREQ (_PRCMU_BASE + 0x084)
-#define PRCM_PLLARM_FREQ (_PRCMU_BASE + 0x088)
-#define PRCM_PLLDDR_FREQ (_PRCMU_BASE + 0x08C)
+#define PRCM_PLLSOC0_FREQ (prcmu_base + 0x080)
+#define PRCM_PLLSOC1_FREQ (prcmu_base + 0x084)
+#define PRCM_PLLARM_FREQ (prcmu_base + 0x088)
+#define PRCM_PLLDDR_FREQ (prcmu_base + 0x08C)
#define PRCM_PLL_FREQ_D_SHIFT 0
#define PRCM_PLL_FREQ_D_MASK BITS(0, 7)
#define PRCM_PLL_FREQ_N_SHIFT 8
@@ -152,14 +126,14 @@
#define PRCM_PLL_FREQ_SELDIV2 BIT(24)
#define PRCM_PLL_FREQ_DIV2EN BIT(25)
-#define PRCM_PLLDSI_FREQ (_PRCMU_BASE + 0x500)
-#define PRCM_PLLDSI_ENABLE (_PRCMU_BASE + 0x504)
-#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508)
-#define PRCM_DSI_PLLOUT_SEL (_PRCMU_BASE + 0x530)
-#define PRCM_DSITVCLK_DIV (_PRCMU_BASE + 0x52C)
-#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508)
-#define PRCM_APE_RESETN_SET (_PRCMU_BASE + 0x1E4)
-#define PRCM_APE_RESETN_CLR (_PRCMU_BASE + 0x1E8)
+#define PRCM_PLLDSI_FREQ (prcmu_base + 0x500)
+#define PRCM_PLLDSI_ENABLE (prcmu_base + 0x504)
+#define PRCM_PLLDSI_LOCKP (prcmu_base + 0x508)
+#define PRCM_DSI_PLLOUT_SEL (prcmu_base + 0x530)
+#define PRCM_DSITVCLK_DIV (prcmu_base + 0x52C)
+#define PRCM_PLLDSI_LOCKP (prcmu_base + 0x508)
+#define PRCM_APE_RESETN_SET (prcmu_base + 0x1E4)
+#define PRCM_APE_RESETN_CLR (prcmu_base + 0x1E8)
#define PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE BIT(0)
@@ -188,30 +162,30 @@
#define PRCM_APE_RESETN_DSIPLL_RESETN BIT(14)
-#define PRCM_CLKOCR (_PRCMU_BASE + 0x1CC)
+#define PRCM_CLKOCR (prcmu_base + 0x1CC)
#define PRCM_CLKOCR_CLKOUT0_REF_CLK (1 << 0)
#define PRCM_CLKOCR_CLKOUT0_MASK BITS(0, 13)
#define PRCM_CLKOCR_CLKOUT1_REF_CLK (1 << 16)
#define PRCM_CLKOCR_CLKOUT1_MASK BITS(16, 29)
/* ePOD and memory power signal control registers */
-#define PRCM_EPOD_C_SET (_PRCMU_BASE + 0x410)
-#define PRCM_SRAM_LS_SLEEP (_PRCMU_BASE + 0x304)
+#define PRCM_EPOD_C_SET (prcmu_base + 0x410)
+#define PRCM_SRAM_LS_SLEEP (prcmu_base + 0x304)
/* Debug power control unit registers */
-#define PRCM_POWER_STATE_SET (_PRCMU_BASE + 0x254)
+#define PRCM_POWER_STATE_SET (prcmu_base + 0x254)
/* Miscellaneous unit registers */
-#define PRCM_DSI_SW_RESET (_PRCMU_BASE + 0x324)
-#define PRCM_GPIOCR (_PRCMU_BASE + 0x138)
+#define PRCM_DSI_SW_RESET (prcmu_base + 0x324)
+#define PRCM_GPIOCR (prcmu_base + 0x138)
#define PRCM_GPIOCR_DBG_STM_MOD_CMD1 0x800
#define PRCM_GPIOCR_DBG_UARTMOD_CMD0 0x1
/* PRCMU HW semaphore */
-#define PRCM_SEM (_PRCMU_BASE + 0x400)
+#define PRCM_SEM (prcmu_base + 0x400)
#define PRCM_SEM_PRCM_SEM BIT(0)
-#define PRCM_TCR (_PRCMU_BASE + 0x1C8)
+#define PRCM_TCR (prcmu_base + 0x1C8)
#define PRCM_TCR_TENSEL_MASK BITS(0, 7)
#define PRCM_TCR_STOP_TIMERS BIT(16)
#define PRCM_TCR_DOZE_MODE BIT(17)
@@ -239,15 +213,15 @@
/* GPIOCR register */
#define PRCM_GPIOCR_SPI2_SELECT BIT(23)
-#define PRCM_DDR_SUBSYS_APE_MINBW (_PRCMU_BASE + 0x438)
-#define PRCM_CGATING_BYPASS (_PRCMU_BASE + 0x134)
+#define PRCM_DDR_SUBSYS_APE_MINBW (prcmu_base + 0x438)
+#define PRCM_CGATING_BYPASS (prcmu_base + 0x134)
#define PRCM_CGATING_BYPASS_ICN2 BIT(6)
/* Miscellaneous unit registers */
-#define PRCM_RESOUTN_SET (_PRCMU_BASE + 0x214)
-#define PRCM_RESOUTN_CLR (_PRCMU_BASE + 0x218)
+#define PRCM_RESOUTN_SET (prcmu_base + 0x214)
+#define PRCM_RESOUTN_CLR (prcmu_base + 0x218)
/* System reset register */
-#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228)
+#define PRCM_APE_SOFTRST (prcmu_base + 0x228)
#endif /* __DB8500_PRCMU_REGS_H */
diff --git a/drivers/mfd/dm355evm_msp.c b/drivers/mfd/dm355evm_msp.c
index 7710227d284..7a55c0071fa 100644
--- a/drivers/mfd/dm355evm_msp.c
+++ b/drivers/mfd/dm355evm_msp.c
@@ -315,8 +315,8 @@ static int add_children(struct i2c_client *client)
}
/* MMC/SD inputs -- right after the last config input */
- if (client->dev.platform_data) {
- void (*mmcsd_setup)(unsigned) = client->dev.platform_data;
+ if (dev_get_platdata(&client->dev)) {
+ void (*mmcsd_setup)(unsigned) = dev_get_platdata(&client->dev);
mmcsd_setup(dm355evm_msp_gpio.base + 8 + 5);
}
diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c
index db662e2dcfa..2ed774e7d34 100644
--- a/drivers/mfd/ezx-pcap.c
+++ b/drivers/mfd/ezx-pcap.c
@@ -177,7 +177,7 @@ static void pcap_msr_work(struct work_struct *work)
static void pcap_isr_work(struct work_struct *work)
{
struct pcap_chip *pcap = container_of(work, struct pcap_chip, isr_work);
- struct pcap_platform_data *pdata = pcap->spi->dev.platform_data;
+ struct pcap_platform_data *pdata = dev_get_platdata(&pcap->spi->dev);
u32 msr, isr, int_sel, service;
int irq;
@@ -371,7 +371,7 @@ static int pcap_remove_subdev(struct device *dev, void *unused)
return 0;
}
-static int __devinit pcap_add_subdev(struct pcap_chip *pcap,
+static int pcap_add_subdev(struct pcap_chip *pcap,
struct pcap_subdev *subdev)
{
struct platform_device *pdev;
@@ -391,19 +391,15 @@ static int __devinit pcap_add_subdev(struct pcap_chip *pcap,
return ret;
}
-static int __devexit ezx_pcap_remove(struct spi_device *spi)
+static int ezx_pcap_remove(struct spi_device *spi)
{
- struct pcap_chip *pcap = dev_get_drvdata(&spi->dev);
- struct pcap_platform_data *pdata = spi->dev.platform_data;
- int i, adc_irq;
+ struct pcap_chip *pcap = spi_get_drvdata(spi);
+ int i;
/* remove all registered subdevs */
device_for_each_child(&spi->dev, NULL, pcap_remove_subdev);
/* cleanup ADC */
- adc_irq = pcap_to_irq(pcap, (pdata->config & PCAP_SECOND_PORT) ?
- PCAP_IRQ_ADCDONE2 : PCAP_IRQ_ADCDONE);
- free_irq(adc_irq, pcap);
mutex_lock(&pcap->adc_mutex);
for (i = 0; i < PCAP_ADC_MAXQ; i++)
kfree(pcap->adc_queue[i]);
@@ -415,14 +411,12 @@ static int __devexit ezx_pcap_remove(struct spi_device *spi)
destroy_workqueue(pcap->workqueue);
- kfree(pcap);
-
return 0;
}
-static int __devinit ezx_pcap_probe(struct spi_device *spi)
+static int ezx_pcap_probe(struct spi_device *spi)
{
- struct pcap_platform_data *pdata = spi->dev.platform_data;
+ struct pcap_platform_data *pdata = dev_get_platdata(&spi->dev);
struct pcap_chip *pcap;
int i, adc_irq;
int ret = -ENODEV;
@@ -431,7 +425,7 @@ static int __devinit ezx_pcap_probe(struct spi_device *spi)
if (!pdata)
goto ret;
- pcap = kzalloc(sizeof(*pcap), GFP_KERNEL);
+ pcap = devm_kzalloc(&spi->dev, sizeof(*pcap), GFP_KERNEL);
if (!pcap) {
ret = -ENOMEM;
goto ret;
@@ -441,14 +435,14 @@ static int __devinit ezx_pcap_probe(struct spi_device *spi)
mutex_init(&pcap->adc_mutex);
INIT_WORK(&pcap->isr_work, pcap_isr_work);
INIT_WORK(&pcap->msr_work, pcap_msr_work);
- dev_set_drvdata(&spi->dev, pcap);
+ spi_set_drvdata(spi, pcap);
/* setup spi */
spi->bits_per_word = 32;
spi->mode = SPI_MODE_0 | (pdata->config & PCAP_CS_AH ? SPI_CS_HIGH : 0);
ret = spi_setup(spi);
if (ret)
- goto free_pcap;
+ goto ret;
pcap->spi = spi;
@@ -458,7 +452,7 @@ static int __devinit ezx_pcap_probe(struct spi_device *spi)
if (!pcap->workqueue) {
ret = -ENOMEM;
dev_err(&spi->dev, "can't create pcap thread\n");
- goto free_pcap;
+ goto ret;
}
/* redirect interrupts to AP, except adcdone2 */
@@ -491,7 +485,8 @@ static int __devinit ezx_pcap_probe(struct spi_device *spi)
adc_irq = pcap_to_irq(pcap, (pdata->config & PCAP_SECOND_PORT) ?
PCAP_IRQ_ADCDONE2 : PCAP_IRQ_ADCDONE);
- ret = request_irq(adc_irq, pcap_adc_irq, 0, "ADC", pcap);
+ ret = devm_request_irq(&spi->dev, adc_irq, pcap_adc_irq, 0, "ADC",
+ pcap);
if (ret)
goto free_irqchip;
@@ -510,22 +505,18 @@ static int __devinit ezx_pcap_probe(struct spi_device *spi)
remove_subdevs:
device_for_each_child(&spi->dev, NULL, pcap_remove_subdev);
-/* free_adc: */
- free_irq(adc_irq, pcap);
free_irqchip:
for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++)
irq_set_chip_and_handler(i, NULL, NULL);
/* destroy_workqueue: */
destroy_workqueue(pcap->workqueue);
-free_pcap:
- kfree(pcap);
ret:
return ret;
}
static struct spi_driver ezxpcap_driver = {
.probe = ezx_pcap_probe,
- .remove = __devexit_p(ezx_pcap_remove),
+ .remove = ezx_pcap_remove,
.driver = {
.name = "ezx-pcap",
.owner = THIS_MODULE,
diff --git a/drivers/mfd/htc-egpio.c b/drivers/mfd/htc-egpio.c
index bbaec0ccba8..49f39feca78 100644
--- a/drivers/mfd/htc-egpio.c
+++ b/drivers/mfd/htc-egpio.c
@@ -261,7 +261,7 @@ static void egpio_write_cache(struct egpio_info *ei)
static int __init egpio_probe(struct platform_device *pdev)
{
- struct htc_egpio_platform_data *pdata = pdev->dev.platform_data;
+ struct htc_egpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct resource *res;
struct egpio_info *ei;
struct gpio_chip *chip;
@@ -270,7 +270,7 @@ static int __init egpio_probe(struct platform_device *pdev)
int ret;
/* Initialize ei data structure. */
- ei = kzalloc(sizeof(*ei), GFP_KERNEL);
+ ei = devm_kzalloc(&pdev->dev, sizeof(*ei), GFP_KERNEL);
if (!ei)
return -ENOMEM;
@@ -286,7 +286,8 @@ static int __init egpio_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
goto fail;
- ei->base_addr = ioremap_nocache(res->start, resource_size(res));
+ ei->base_addr = devm_ioremap_nocache(&pdev->dev, res->start,
+ resource_size(res));
if (!ei->base_addr)
goto fail;
pr_debug("EGPIO phys=%08x virt=%p\n", (u32)res->start, ei->base_addr);
@@ -306,7 +307,9 @@ static int __init egpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ei);
ei->nchips = pdata->num_chips;
- ei->chip = kzalloc(sizeof(struct egpio_chip) * ei->nchips, GFP_KERNEL);
+ ei->chip = devm_kzalloc(&pdev->dev,
+ sizeof(struct egpio_chip) * ei->nchips,
+ GFP_KERNEL);
if (!ei->chip) {
ret = -ENOMEM;
goto fail;
@@ -361,7 +364,6 @@ static int __init egpio_probe(struct platform_device *pdev)
fail:
printk(KERN_ERR "EGPIO failed to setup\n");
- kfree(ei);
return ret;
}
@@ -379,9 +381,6 @@ static int __exit egpio_remove(struct platform_device *pdev)
irq_set_chained_handler(ei->chained_irq, NULL);
device_init_wakeup(&pdev->dev, 0);
}
- iounmap(ei->base_addr);
- kfree(ei->chip);
- kfree(ei);
return 0;
}
diff --git a/drivers/mfd/htc-i2cpld.c b/drivers/mfd/htc-i2cpld.c
index d55065cc324..d7b2a75aca3 100644
--- a/drivers/mfd/htc-i2cpld.c
+++ b/drivers/mfd/htc-i2cpld.c
@@ -327,7 +327,7 @@ static void htcpld_chip_reset(struct i2c_client *client)
client, (chip_data->cache_out = chip_data->reset));
}
-static int __devinit htcpld_setup_chip_irq(
+static int htcpld_setup_chip_irq(
struct platform_device *pdev,
int chip_index)
{
@@ -340,7 +340,7 @@ static int __devinit htcpld_setup_chip_irq(
int ret = 0;
/* Get the platform and driver data */
- pdata = dev->platform_data;
+ pdata = dev_get_platdata(dev);
htcpld = platform_get_drvdata(pdev);
chip = &htcpld->chip[chip_index];
plat_chip_data = &pdata->chip[chip_index];
@@ -361,7 +361,7 @@ static int __devinit htcpld_setup_chip_irq(
return ret;
}
-static int __devinit htcpld_register_chip_i2c(
+static int htcpld_register_chip_i2c(
struct platform_device *pdev,
int chip_index)
{
@@ -375,7 +375,7 @@ static int __devinit htcpld_register_chip_i2c(
struct i2c_board_info info;
/* Get the platform and driver data */
- pdata = dev->platform_data;
+ pdata = dev_get_platdata(dev);
htcpld = platform_get_drvdata(pdev);
chip = &htcpld->chip[chip_index];
plat_chip_data = &pdata->chip[chip_index];
@@ -419,7 +419,7 @@ static int __devinit htcpld_register_chip_i2c(
return 0;
}
-static void __devinit htcpld_unregister_chip_i2c(
+static void htcpld_unregister_chip_i2c(
struct platform_device *pdev,
int chip_index)
{
@@ -434,7 +434,7 @@ static void __devinit htcpld_unregister_chip_i2c(
i2c_unregister_device(chip->client);
}
-static int __devinit htcpld_register_chip_gpio(
+static int htcpld_register_chip_gpio(
struct platform_device *pdev,
int chip_index)
{
@@ -447,7 +447,7 @@ static int __devinit htcpld_register_chip_gpio(
int ret = 0;
/* Get the platform and driver data */
- pdata = dev->platform_data;
+ pdata = dev_get_platdata(dev);
htcpld = platform_get_drvdata(pdev);
chip = &htcpld->chip[chip_index];
plat_chip_data = &pdata->chip[chip_index];
@@ -501,7 +501,7 @@ static int __devinit htcpld_register_chip_gpio(
return 0;
}
-static int __devinit htcpld_setup_chips(struct platform_device *pdev)
+static int htcpld_setup_chips(struct platform_device *pdev)
{
struct htcpld_data *htcpld;
struct device *dev = &pdev->dev;
@@ -509,13 +509,13 @@ static int __devinit htcpld_setup_chips(struct platform_device *pdev)
int i;
/* Get the platform and driver data */
- pdata = dev->platform_data;
+ pdata = dev_get_platdata(dev);
htcpld = platform_get_drvdata(pdev);
/* Setup each chip's output GPIOs */
htcpld->nchips = pdata->num_chip;
- htcpld->chip = kzalloc(sizeof(struct htcpld_chip) * htcpld->nchips,
- GFP_KERNEL);
+ htcpld->chip = devm_kzalloc(dev, sizeof(struct htcpld_chip) * htcpld->nchips,
+ GFP_KERNEL);
if (!htcpld->chip) {
dev_warn(dev, "Unable to allocate memory for chips\n");
return -ENOMEM;
@@ -563,7 +563,7 @@ static int __devinit htcpld_setup_chips(struct platform_device *pdev)
return 0;
}
-static int __devinit htcpld_core_probe(struct platform_device *pdev)
+static int htcpld_core_probe(struct platform_device *pdev)
{
struct htcpld_data *htcpld;
struct device *dev = &pdev->dev;
@@ -574,18 +574,17 @@ static int __devinit htcpld_core_probe(struct platform_device *pdev)
if (!dev)
return -ENODEV;
- pdata = dev->platform_data;
+ pdata = dev_get_platdata(dev);
if (!pdata) {
dev_warn(dev, "Platform data not found for htcpld core!\n");
return -ENXIO;
}
- htcpld = kzalloc(sizeof(struct htcpld_data), GFP_KERNEL);
+ htcpld = devm_kzalloc(dev, sizeof(struct htcpld_data), GFP_KERNEL);
if (!htcpld)
return -ENOMEM;
/* Find chained irq */
- ret = -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res) {
int flags;
@@ -598,7 +597,7 @@ static int __devinit htcpld_core_probe(struct platform_device *pdev)
flags, pdev->name, htcpld);
if (ret) {
dev_warn(dev, "Unable to setup chained irq handler: %d\n", ret);
- goto fail;
+ return ret;
} else
device_init_wakeup(dev, 0);
}
@@ -609,7 +608,7 @@ static int __devinit htcpld_core_probe(struct platform_device *pdev)
/* Setup the htcpld chips */
ret = htcpld_setup_chips(pdev);
if (ret)
- goto fail;
+ return ret;
/* Request the GPIO(s) for the int reset and set them up */
if (pdata->int_reset_gpio_hi) {
@@ -644,10 +643,6 @@ static int __devinit htcpld_core_probe(struct platform_device *pdev)
dev_info(dev, "Initialized successfully\n");
return 0;
-
-fail:
- kfree(htcpld);
- return ret;
}
/* The I2C Driver -- used internally */
diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c
index 9e5453d21a6..e88d4f6fef4 100644
--- a/drivers/mfd/htc-pasic3.c
+++ b/drivers/mfd/htc-pasic3.c
@@ -114,7 +114,7 @@ static struct resource ds1wm_resources[] __initdata = {
},
};
-static struct mfd_cell ds1wm_cell __initdata = {
+static const struct mfd_cell ds1wm_cell __initconst = {
.name = "ds1wm",
.enable = ds1wm_enable,
.disable = ds1wm_disable,
@@ -126,7 +126,7 @@ static struct mfd_cell ds1wm_cell __initdata = {
static int __init pasic3_probe(struct platform_device *pdev)
{
- struct pasic3_platform_data *pdata = pdev->dev.platform_data;
+ struct pasic3_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct device *dev = &pdev->dev;
struct pasic3_data *asic;
struct resource *r;
@@ -147,7 +147,7 @@ static int __init pasic3_probe(struct platform_device *pdev)
if (!request_mem_region(r->start, resource_size(r), "pasic3"))
return -EBUSY;
- asic = kzalloc(sizeof(struct pasic3_data), GFP_KERNEL);
+ asic = devm_kzalloc(dev, sizeof(struct pasic3_data), GFP_KERNEL);
if (!asic)
return -ENOMEM;
@@ -156,7 +156,6 @@ static int __init pasic3_probe(struct platform_device *pdev)
asic->mapping = ioremap(r->start, resource_size(r));
if (!asic->mapping) {
dev_err(dev, "couldn't ioremap PASIC3\n");
- kfree(asic);
return -ENOMEM;
}
@@ -195,7 +194,6 @@ static int pasic3_remove(struct platform_device *pdev)
iounmap(asic->mapping);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(r->start, resource_size(r));
- kfree(asic);
return 0;
}
@@ -208,18 +206,7 @@ static struct platform_driver pasic3_driver = {
.remove = pasic3_remove,
};
-static int __init pasic3_base_init(void)
-{
- return platform_driver_probe(&pasic3_driver, pasic3_probe);
-}
-
-static void __exit pasic3_base_exit(void)
-{
- platform_driver_unregister(&pasic3_driver);
-}
-
-module_init(pasic3_base_init);
-module_exit(pasic3_base_exit);
+module_platform_driver_probe(pasic3_driver, pasic3_probe);
MODULE_AUTHOR("Philipp Zabel <philipp.zabel@gmail.com>");
MODULE_DESCRIPTION("Core driver for HTC PASIC3");
diff --git a/drivers/mfd/intel_msic.c b/drivers/mfd/intel_msic.c
index 266bdc5bd96..049fd23af54 100644
--- a/drivers/mfd/intel_msic.c
+++ b/drivers/mfd/intel_msic.c
@@ -9,6 +9,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -177,7 +178,7 @@ static struct mfd_cell msic_devs[] = {
* These devices appear only after the MSIC driver itself is initialized so
* we can guarantee that the SCU IPC interface is ready.
*/
-static struct mfd_cell msic_other_devs[] = {
+static const struct mfd_cell msic_other_devs[] = {
/* Audio codec in the MSIC */
{
.id = -1,
@@ -306,10 +307,10 @@ int intel_msic_irq_read(struct intel_msic *msic, unsigned short reg, u8 *val)
}
EXPORT_SYMBOL_GPL(intel_msic_irq_read);
-static int __devinit intel_msic_init_devices(struct intel_msic *msic)
+static int intel_msic_init_devices(struct intel_msic *msic)
{
struct platform_device *pdev = msic->pdev;
- struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
+ struct intel_msic_platform_data *pdata = dev_get_platdata(&pdev->dev);
int ret, i;
if (pdata->gpio) {
@@ -322,7 +323,8 @@ static int __devinit intel_msic_init_devices(struct intel_msic *msic)
if (pdata->ocd) {
unsigned gpio = pdata->ocd->gpio;
- ret = gpio_request_one(gpio, GPIOF_IN, "ocd_gpio");
+ ret = devm_gpio_request_one(&pdev->dev, gpio,
+ GPIOF_IN, "ocd_gpio");
if (ret) {
dev_err(&pdev->dev, "failed to register OCD GPIO\n");
return ret;
@@ -331,7 +333,6 @@ static int __devinit intel_msic_init_devices(struct intel_msic *msic)
ret = gpio_to_irq(gpio);
if (ret < 0) {
dev_err(&pdev->dev, "no IRQ number for OCD GPIO\n");
- gpio_free(gpio);
return ret;
}
@@ -358,26 +359,20 @@ static int __devinit intel_msic_init_devices(struct intel_msic *msic)
fail:
mfd_remove_devices(&pdev->dev);
- if (pdata->ocd)
- gpio_free(pdata->ocd->gpio);
return ret;
}
-static void __devexit intel_msic_remove_devices(struct intel_msic *msic)
+static void intel_msic_remove_devices(struct intel_msic *msic)
{
struct platform_device *pdev = msic->pdev;
- struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
mfd_remove_devices(&pdev->dev);
-
- if (pdata->ocd)
- gpio_free(pdata->ocd->gpio);
}
-static int __devinit intel_msic_probe(struct platform_device *pdev)
+static int intel_msic_probe(struct platform_device *pdev)
{
- struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
+ struct intel_msic_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct intel_msic *msic;
struct resource *res;
u8 id0, id1;
@@ -419,16 +414,9 @@ static int __devinit intel_msic_probe(struct platform_device *pdev)
* the clients via intel_msic_irq_read().
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "failed to get SRAM iomem resource\n");
- return -ENODEV;
- }
-
- msic->irq_base = devm_request_and_ioremap(&pdev->dev, res);
- if (!msic->irq_base) {
- dev_err(&pdev->dev, "failed to map SRAM memory\n");
- return -ENOMEM;
- }
+ msic->irq_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(msic->irq_base))
+ return PTR_ERR(msic->irq_base);
platform_set_drvdata(pdev, msic);
@@ -445,19 +433,18 @@ static int __devinit intel_msic_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit intel_msic_remove(struct platform_device *pdev)
+static int intel_msic_remove(struct platform_device *pdev)
{
struct intel_msic *msic = platform_get_drvdata(pdev);
intel_msic_remove_devices(msic);
- platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver intel_msic_driver = {
.probe = intel_msic_probe,
- .remove = __devexit_p(intel_msic_remove),
+ .remove = intel_msic_remove,
.driver = {
.name = "intel_msic",
.owner = THIS_MODULE,
diff --git a/drivers/mfd/ipaq-micro.c b/drivers/mfd/ipaq-micro.c
new file mode 100644
index 00000000000..7e50fe0118e
--- /dev/null
+++ b/drivers/mfd/ipaq-micro.c
@@ -0,0 +1,482 @@
+/*
+ * Compaq iPAQ h3xxx Atmel microcontroller companion support
+ *
+ * This is an Atmel AT90LS8535 with a special flashed-in firmware that
+ * implements the special protocol used by this driver.
+ *
+ * based on previous kernel 2.4 version by Andrew Christian
+ * Author : Alessandro Gardich <gremlin@gremlin.it>
+ * Author : Dmitry Artamonow <mad_soft@inbox.ru>
+ * Author : Linus Walleij <linus.walleij@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/ipaq-micro.h>
+#include <linux/string.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+#include <mach/hardware.h>
+
+static void ipaq_micro_trigger_tx(struct ipaq_micro *micro)
+{
+ struct ipaq_micro_txdev *tx = &micro->tx;
+ struct ipaq_micro_msg *msg = micro->msg;
+ int i, bp;
+ u8 checksum;
+ u32 val;
+
+ bp = 0;
+ tx->buf[bp++] = CHAR_SOF;
+
+ checksum = ((msg->id & 0x0f) << 4) | (msg->tx_len & 0x0f);
+ tx->buf[bp++] = checksum;
+
+ for (i = 0; i < msg->tx_len; i++) {
+ tx->buf[bp++] = msg->tx_data[i];
+ checksum += msg->tx_data[i];
+ }
+
+ tx->buf[bp++] = checksum;
+ tx->len = bp;
+ tx->index = 0;
+ print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_OFFSET, 16, 1,
+ tx->buf, tx->len, true);
+
+ /* Enable interrupt */
+ val = readl(micro->base + UTCR3);
+ val |= UTCR3_TIE;
+ writel(val, micro->base + UTCR3);
+}
+
+int ipaq_micro_tx_msg(struct ipaq_micro *micro, struct ipaq_micro_msg *msg)
+{
+ unsigned long flags;
+
+ dev_dbg(micro->dev, "TX msg: %02x, %d bytes\n", msg->id, msg->tx_len);
+
+ spin_lock_irqsave(&micro->lock, flags);
+ if (micro->msg) {
+ list_add_tail(&msg->node, &micro->queue);
+ spin_unlock_irqrestore(&micro->lock, flags);
+ return 0;
+ }
+ micro->msg = msg;
+ ipaq_micro_trigger_tx(micro);
+ spin_unlock_irqrestore(&micro->lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL(ipaq_micro_tx_msg);
+
+static void micro_rx_msg(struct ipaq_micro *micro, u8 id, int len, u8 *data)
+{
+ int i;
+
+ dev_dbg(micro->dev, "RX msg: %02x, %d bytes\n", id, len);
+
+ spin_lock(&micro->lock);
+ switch (id) {
+ case MSG_VERSION:
+ case MSG_EEPROM_READ:
+ case MSG_EEPROM_WRITE:
+ case MSG_BACKLIGHT:
+ case MSG_NOTIFY_LED:
+ case MSG_THERMAL_SENSOR:
+ case MSG_BATTERY:
+ /* Handle synchronous messages */
+ if (micro->msg && micro->msg->id == id) {
+ struct ipaq_micro_msg *msg = micro->msg;
+
+ memcpy(msg->rx_data, data, len);
+ msg->rx_len = len;
+ complete(&micro->msg->ack);
+ if (!list_empty(&micro->queue)) {
+ micro->msg = list_entry(micro->queue.next,
+ struct ipaq_micro_msg,
+ node);
+ list_del_init(&micro->msg->node);
+ ipaq_micro_trigger_tx(micro);
+ } else
+ micro->msg = NULL;
+ dev_dbg(micro->dev, "OK RX message 0x%02x\n", id);
+ } else {
+ dev_err(micro->dev,
+ "out of band RX message 0x%02x\n", id);
+ if(!micro->msg)
+ dev_info(micro->dev, "no message queued\n");
+ else
+ dev_info(micro->dev, "expected message %02x\n",
+ micro->msg->id);
+ }
+ break;
+ case MSG_KEYBOARD:
+ if (micro->key)
+ micro->key(micro->key_data, len, data);
+ else
+ dev_dbg(micro->dev, "key message ignored, no handle \n");
+ break;
+ case MSG_TOUCHSCREEN:
+ if (micro->ts)
+ micro->ts(micro->ts_data, len, data);
+ else
+ dev_dbg(micro->dev, "touchscreen message ignored, no handle \n");
+ break;
+ default:
+ dev_err(micro->dev,
+ "unknown msg %d [%d] ", id, len);
+ for (i = 0; i < len; ++i)
+ pr_cont("0x%02x ", data[i]);
+ pr_cont("\n");
+ }
+ spin_unlock(&micro->lock);
+}
+
+static void micro_process_char(struct ipaq_micro *micro, u8 ch)
+{
+ struct ipaq_micro_rxdev *rx = &micro->rx;
+
+ switch (rx->state) {
+ case STATE_SOF: /* Looking for SOF */
+ if (ch == CHAR_SOF)
+ rx->state = STATE_ID; /* Next byte is the id and len */
+ break;
+ case STATE_ID: /* Looking for id and len byte */
+ rx->id = (ch & 0xf0) >> 4 ;
+ rx->len = (ch & 0x0f);
+ rx->index = 0;
+ rx->chksum = ch;
+ rx->state = (rx->len > 0) ? STATE_DATA : STATE_CHKSUM;
+ break;
+ case STATE_DATA: /* Looking for 'len' data bytes */
+ rx->chksum += ch;
+ rx->buf[rx->index] = ch;
+ if (++rx->index == rx->len)
+ rx->state = STATE_CHKSUM;
+ break;
+ case STATE_CHKSUM: /* Looking for the checksum */
+ if (ch == rx->chksum)
+ micro_rx_msg(micro, rx->id, rx->len, rx->buf);
+ rx->state = STATE_SOF;
+ break;
+ }
+}
+
+static void micro_rx_chars(struct ipaq_micro *micro)
+{
+ u32 status, ch;
+
+ while ((status = readl(micro->base + UTSR1)) & UTSR1_RNE) {
+ ch = readl(micro->base + UTDR);
+ if (status & UTSR1_PRE)
+ dev_err(micro->dev, "rx: parity error\n");
+ else if (status & UTSR1_FRE)
+ dev_err(micro->dev, "rx: framing error\n");
+ else if (status & UTSR1_ROR)
+ dev_err(micro->dev, "rx: overrun error\n");
+ micro_process_char(micro, ch);
+ }
+}
+
+static void ipaq_micro_get_version(struct ipaq_micro *micro)
+{
+ struct ipaq_micro_msg msg = {
+ .id = MSG_VERSION,
+ };
+
+ ipaq_micro_tx_msg_sync(micro, &msg);
+ if (msg.rx_len == 4) {
+ memcpy(micro->version, msg.rx_data, 4);
+ micro->version[4] = '\0';
+ } else if (msg.rx_len == 9) {
+ memcpy(micro->version, msg.rx_data, 4);
+ micro->version[4] = '\0';
+ /* Bytes 4-7 are "pack", byte 8 is "boot type" */
+ } else {
+ dev_err(micro->dev,
+ "illegal version message %d bytes\n", msg.rx_len);
+ }
+}
+
+static void ipaq_micro_eeprom_read(struct ipaq_micro *micro,
+ u8 address, u8 len, u8 *data)
+{
+ struct ipaq_micro_msg msg = {
+ .id = MSG_EEPROM_READ,
+ };
+ u8 i;
+
+ for (i = 0; i < len; i++) {
+ msg.tx_data[0] = address + i;
+ msg.tx_data[1] = 1;
+ msg.tx_len = 2;
+ ipaq_micro_tx_msg_sync(micro, &msg);
+ memcpy(data + (i * 2), msg.rx_data, 2);
+ }
+}
+
+static char *ipaq_micro_str(u8 *wchar, u8 len)
+{
+ char retstr[256];
+ u8 i;
+
+ for (i = 0; i < len / 2; i++)
+ retstr[i] = wchar[i * 2];
+ return kstrdup(retstr, GFP_KERNEL);
+}
+
+static u16 ipaq_micro_to_u16(u8 *data)
+{
+ return data[1] << 8 | data[0];
+}
+
+static void ipaq_micro_eeprom_dump(struct ipaq_micro *micro)
+{
+ u8 dump[256];
+ char *str;
+
+ ipaq_micro_eeprom_read(micro, 0, 128, dump);
+ str = ipaq_micro_str(dump, 10);
+ if (str) {
+ dev_info(micro->dev, "HM version %s\n", str);
+ kfree(str);
+ }
+ str = ipaq_micro_str(dump+10, 40);
+ if (str) {
+ dev_info(micro->dev, "serial number: %s\n", str);
+ /* Feed the random pool with this */
+ add_device_randomness(str, strlen(str));
+ kfree(str);
+ }
+ str = ipaq_micro_str(dump+50, 20);
+ if (str) {
+ dev_info(micro->dev, "module ID: %s\n", str);
+ kfree(str);
+ }
+ str = ipaq_micro_str(dump+70, 10);
+ if (str) {
+ dev_info(micro->dev, "product revision: %s\n", str);
+ kfree(str);
+ }
+ dev_info(micro->dev, "product ID: %u\n", ipaq_micro_to_u16(dump+80));
+ dev_info(micro->dev, "frame rate: %u fps\n",
+ ipaq_micro_to_u16(dump+82));
+ dev_info(micro->dev, "page mode: %u\n", ipaq_micro_to_u16(dump+84));
+ dev_info(micro->dev, "country ID: %u\n", ipaq_micro_to_u16(dump+86));
+ dev_info(micro->dev, "color display: %s\n",
+ ipaq_micro_to_u16(dump+88) ? "yes" : "no");
+ dev_info(micro->dev, "ROM size: %u MiB\n", ipaq_micro_to_u16(dump+90));
+ dev_info(micro->dev, "RAM size: %u KiB\n", ipaq_micro_to_u16(dump+92));
+ dev_info(micro->dev, "screen: %u x %u\n",
+ ipaq_micro_to_u16(dump+94), ipaq_micro_to_u16(dump+96));
+ print_hex_dump(KERN_DEBUG, "eeprom: ", DUMP_PREFIX_OFFSET, 16, 1,
+ dump, 256, true);
+
+}
+
+static void micro_tx_chars(struct ipaq_micro *micro)
+{
+ struct ipaq_micro_txdev *tx = &micro->tx;
+ u32 val;
+
+ while ((tx->index < tx->len) &&
+ (readl(micro->base + UTSR1) & UTSR1_TNF)) {
+ writel(tx->buf[tx->index], micro->base + UTDR);
+ tx->index++;
+ }
+
+ /* Stop interrupts */
+ val = readl(micro->base + UTCR3);
+ val &= ~UTCR3_TIE;
+ writel(val, micro->base + UTCR3);
+}
+
+static void micro_reset_comm(struct ipaq_micro *micro)
+{
+ struct ipaq_micro_rxdev *rx = &micro->rx;
+ u32 val;
+
+ if (micro->msg)
+ complete(&micro->msg->ack);
+
+ /* Initialize Serial channel protocol frame */
+ rx->state = STATE_SOF; /* Reset the state machine */
+
+ /* Set up interrupts */
+ writel(0x01, micro->sdlc + 0x0); /* Select UART mode */
+
+ /* Clean up CR3 */
+ writel(0x0, micro->base + UTCR3);
+
+ /* Format: 8N1 */
+ writel(UTCR0_8BitData | UTCR0_1StpBit, micro->base + UTCR0);
+
+ /* Baud rate: 115200 */
+ writel(0x0, micro->base + UTCR1);
+ writel(0x1, micro->base + UTCR2);
+
+ /* Clear SR0 */
+ writel(0xff, micro->base + UTSR0);
+
+ /* Enable RX int, disable TX int */
+ writel(UTCR3_TXE | UTCR3_RXE | UTCR3_RIE, micro->base + UTCR3);
+ val = readl(micro->base + UTCR3);
+ val &= ~UTCR3_TIE;
+ writel(val, micro->base + UTCR3);
+}
+
+static irqreturn_t micro_serial_isr(int irq, void *dev_id)
+{
+ struct ipaq_micro *micro = dev_id;
+ struct ipaq_micro_txdev *tx = &micro->tx;
+ u32 status;
+
+ status = readl(micro->base + UTSR0);
+ do {
+ if (status & (UTSR0_RID | UTSR0_RFS)) {
+ if (status & UTSR0_RID)
+ /* Clear the Receiver IDLE bit */
+ writel(UTSR0_RID, micro->base + UTSR0);
+ micro_rx_chars(micro);
+ }
+
+ /* Clear break bits */
+ if (status & (UTSR0_RBB | UTSR0_REB))
+ writel(status & (UTSR0_RBB | UTSR0_REB),
+ micro->base + UTSR0);
+
+ if (status & UTSR0_TFS)
+ micro_tx_chars(micro);
+
+ status = readl(micro->base + UTSR0);
+
+ } while (((tx->index < tx->len) && (status & UTSR0_TFS)) ||
+ (status & (UTSR0_RFS | UTSR0_RID)));
+
+ return IRQ_HANDLED;
+}
+
+static const struct mfd_cell micro_cells[] = {
+ { .name = "ipaq-micro-backlight", },
+ { .name = "ipaq-micro-battery", },
+ { .name = "ipaq-micro-keys", },
+ { .name = "ipaq-micro-ts", },
+ { .name = "ipaq-micro-leds", },
+};
+
+static int micro_resume(struct device *dev)
+{
+ struct ipaq_micro *micro = dev_get_drvdata(dev);
+
+ micro_reset_comm(micro);
+ mdelay(10);
+
+ return 0;
+}
+
+static int micro_probe(struct platform_device *pdev)
+{
+ struct ipaq_micro *micro;
+ struct resource *res;
+ int ret;
+ int irq;
+
+ micro = devm_kzalloc(&pdev->dev, sizeof(*micro), GFP_KERNEL);
+ if (!micro)
+ return -ENOMEM;
+
+ micro->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ micro->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(micro->base))
+ return PTR_ERR(micro->base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res)
+ return -EINVAL;
+
+ micro->sdlc = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(micro->sdlc))
+ return PTR_ERR(micro->sdlc);
+
+ micro_reset_comm(micro);
+
+ irq = platform_get_irq(pdev, 0);
+ if (!irq)
+ return -EINVAL;
+ ret = devm_request_irq(&pdev->dev, irq, micro_serial_isr,
+ IRQF_SHARED, "ipaq-micro",
+ micro);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to grab serial port IRQ\n");
+ return ret;
+ } else
+ dev_info(&pdev->dev, "grabbed serial port IRQ\n");
+
+ spin_lock_init(&micro->lock);
+ INIT_LIST_HEAD(&micro->queue);
+ platform_set_drvdata(pdev, micro);
+
+ ret = mfd_add_devices(&pdev->dev, pdev->id, micro_cells,
+ ARRAY_SIZE(micro_cells), NULL, 0, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "error adding MFD cells");
+ return ret;
+ }
+
+ /* Check version */
+ ipaq_micro_get_version(micro);
+ dev_info(&pdev->dev, "Atmel micro ASIC version %s\n", micro->version);
+ ipaq_micro_eeprom_dump(micro);
+
+ return 0;
+}
+
+static int micro_remove(struct platform_device *pdev)
+{
+ struct ipaq_micro *micro = platform_get_drvdata(pdev);
+ u32 val;
+
+ mfd_remove_devices(&pdev->dev);
+
+ val = readl(micro->base + UTCR3);
+ val &= ~(UTCR3_RXE | UTCR3_RIE); /* disable receive interrupt */
+ val &= ~(UTCR3_TXE | UTCR3_TIE); /* disable transmit interrupt */
+ writel(val, micro->base + UTCR3);
+
+ return 0;
+}
+
+static const struct dev_pm_ops micro_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(NULL, micro_resume)
+};
+
+static struct platform_driver micro_device_driver = {
+ .driver = {
+ .name = "ipaq-h3xxx-micro",
+ .pm = &micro_dev_pm_ops,
+ },
+ .probe = micro_probe,
+ .remove = micro_remove,
+ /* .shutdown = micro_suspend, // FIXME */
+};
+module_platform_driver(micro_device_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("driver for iPAQ Atmel micro core and backlight");
diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c
index 965c4801df8..433f823037d 100644
--- a/drivers/mfd/janz-cmodio.c
+++ b/drivers/mfd/janz-cmodio.c
@@ -13,7 +13,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
@@ -63,7 +62,7 @@ struct cmodio_device {
* Subdevices using the mfd-core API
*/
-static int __devinit cmodio_setup_subdevice(struct cmodio_device *priv,
+static int cmodio_setup_subdevice(struct cmodio_device *priv,
char *name, unsigned int devno,
unsigned int modno)
{
@@ -120,7 +119,7 @@ static int __devinit cmodio_setup_subdevice(struct cmodio_device *priv,
}
/* Probe each submodule using kernel parameters */
-static int __devinit cmodio_probe_submodules(struct cmodio_device *priv)
+static int cmodio_probe_submodules(struct cmodio_device *priv)
{
struct pci_dev *pdev = priv->pdev;
unsigned int num_probed = 0;
@@ -177,17 +176,16 @@ static const struct attribute_group cmodio_sysfs_attr_group = {
* PCI Driver
*/
-static int __devinit cmodio_pci_probe(struct pci_dev *dev,
+static int cmodio_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
struct cmodio_device *priv;
int ret;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
dev_err(&dev->dev, "unable to allocate private data\n");
- ret = -ENOMEM;
- goto out_return;
+ return -ENOMEM;
}
pci_set_drvdata(dev, priv);
@@ -197,7 +195,7 @@ static int __devinit cmodio_pci_probe(struct pci_dev *dev,
ret = pci_enable_device(dev);
if (ret) {
dev_err(&dev->dev, "unable to enable device\n");
- goto out_free_priv;
+ return ret;
}
pci_set_master(dev);
@@ -248,13 +246,11 @@ out_pci_release_regions:
pci_release_regions(dev);
out_pci_disable_device:
pci_disable_device(dev);
-out_free_priv:
- kfree(priv);
-out_return:
+
return ret;
}
-static void __devexit cmodio_pci_remove(struct pci_dev *dev)
+static void cmodio_pci_remove(struct pci_dev *dev)
{
struct cmodio_device *priv = pci_get_drvdata(dev);
@@ -263,13 +259,12 @@ static void __devexit cmodio_pci_remove(struct pci_dev *dev)
iounmap(priv->ctrl);
pci_release_regions(dev);
pci_disable_device(dev);
- kfree(priv);
}
#define PCI_VENDOR_ID_JANZ 0x13c3
/* The list of devices that this module will support */
-static DEFINE_PCI_DEVICE_TABLE(cmodio_pci_ids) = {
+static const struct pci_device_id cmodio_pci_ids[] = {
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_JANZ, 0x0101 },
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_JANZ, 0x0100 },
{ 0, }
@@ -280,7 +275,7 @@ static struct pci_driver cmodio_pci_driver = {
.name = DRV_NAME,
.id_table = cmodio_pci_ids,
.probe = cmodio_pci_probe,
- .remove = __devexit_p(cmodio_pci_remove),
+ .remove = cmodio_pci_remove,
};
module_pci_driver(cmodio_pci_driver);
diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c
index c6b6d7dda51..7a51c0d0d4f 100644
--- a/drivers/mfd/jz4740-adc.c
+++ b/drivers/mfd/jz4740-adc.c
@@ -86,13 +86,13 @@ static void jz4740_adc_irq_demux(unsigned int irq, struct irq_desc *desc)
static inline void jz4740_adc_clk_enable(struct jz4740_adc *adc)
{
if (atomic_inc_return(&adc->clk_ref) == 1)
- clk_enable(adc->clk);
+ clk_prepare_enable(adc->clk);
}
static inline void jz4740_adc_clk_disable(struct jz4740_adc *adc)
{
if (atomic_dec_return(&adc->clk_ref) == 0)
- clk_disable(adc->clk);
+ clk_disable_unprepare(adc->clk);
}
static inline void jz4740_adc_set_enabled(struct jz4740_adc *adc, int engine,
@@ -181,7 +181,7 @@ static struct resource jz4740_battery_resources[] = {
},
};
-static struct mfd_cell jz4740_adc_cells[] = {
+static const struct mfd_cell jz4740_adc_cells[] = {
{
.id = 0,
.name = "jz4740-hwmon",
@@ -202,7 +202,7 @@ static struct mfd_cell jz4740_adc_cells[] = {
},
};
-static int __devinit jz4740_adc_probe(struct platform_device *pdev)
+static int jz4740_adc_probe(struct platform_device *pdev)
{
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
@@ -211,7 +211,7 @@ static int __devinit jz4740_adc_probe(struct platform_device *pdev)
int ret;
int irq_base;
- adc = kmalloc(sizeof(*adc), GFP_KERNEL);
+ adc = devm_kzalloc(&pdev->dev, sizeof(*adc), GFP_KERNEL);
if (!adc) {
dev_err(&pdev->dev, "Failed to allocate driver structure\n");
return -ENOMEM;
@@ -221,30 +221,27 @@ static int __devinit jz4740_adc_probe(struct platform_device *pdev)
if (adc->irq < 0) {
ret = adc->irq;
dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
- goto err_free;
+ return ret;
}
irq_base = platform_get_irq(pdev, 1);
if (irq_base < 0) {
- ret = irq_base;
- dev_err(&pdev->dev, "Failed to get irq base: %d\n", ret);
- goto err_free;
+ dev_err(&pdev->dev, "Failed to get irq base: %d\n", irq_base);
+ return irq_base;
}
mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem_base) {
- ret = -ENOENT;
dev_err(&pdev->dev, "Failed to get platform mmio resource\n");
- goto err_free;
+ return -ENOENT;
}
/* Only request the shared registers for the MFD driver */
adc->mem = request_mem_region(mem_base->start, JZ_REG_ADC_STATUS,
pdev->name);
if (!adc->mem) {
- ret = -EBUSY;
dev_err(&pdev->dev, "Failed to request mmio memory region\n");
- goto err_free;
+ return -EBUSY;
}
adc->base = ioremap_nocache(adc->mem->start, resource_size(adc->mem));
@@ -297,17 +294,13 @@ static int __devinit jz4740_adc_probe(struct platform_device *pdev)
err_clk_put:
clk_put(adc->clk);
err_iounmap:
- platform_set_drvdata(pdev, NULL);
iounmap(adc->base);
err_release_mem_region:
release_mem_region(adc->mem->start, resource_size(adc->mem));
-err_free:
- kfree(adc);
-
return ret;
}
-static int __devexit jz4740_adc_remove(struct platform_device *pdev)
+static int jz4740_adc_remove(struct platform_device *pdev)
{
struct jz4740_adc *adc = platform_get_drvdata(pdev);
@@ -323,16 +316,12 @@ static int __devexit jz4740_adc_remove(struct platform_device *pdev)
clk_put(adc->clk);
- platform_set_drvdata(pdev, NULL);
-
- kfree(adc);
-
return 0;
}
static struct platform_driver jz4740_adc_driver = {
.probe = jz4740_adc_probe,
- .remove = __devexit_p(jz4740_adc_remove),
+ .remove = jz4740_adc_remove,
.driver = {
.name = "jz4740-adc",
.owner = THIS_MODULE,
diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c
new file mode 100644
index 00000000000..f7ff0188603
--- /dev/null
+++ b/drivers/mfd/kempld-core.c
@@ -0,0 +1,771 @@
+/*
+ * Kontron PLD MFD core driver
+ *
+ * Copyright (c) 2010-2013 Kontron Europe GmbH
+ * Author: Michael Brunner <michael.brunner@kontron.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/kempld.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#define MAX_ID_LEN 4
+static char force_device_id[MAX_ID_LEN + 1] = "";
+module_param_string(force_device_id, force_device_id, sizeof(force_device_id), 0);
+MODULE_PARM_DESC(force_device_id, "Override detected product");
+
+/*
+ * Get hardware mutex to block firmware from accessing the pld.
+ * It is possible for the firmware may hold the mutex for an extended length of
+ * time. This function will block until access has been granted.
+ */
+static void kempld_get_hardware_mutex(struct kempld_device_data *pld)
+{
+ /* The mutex bit will read 1 until access has been granted */
+ while (ioread8(pld->io_index) & KEMPLD_MUTEX_KEY)
+ msleep(1);
+}
+
+static void kempld_release_hardware_mutex(struct kempld_device_data *pld)
+{
+ /* The harware mutex is released when 1 is written to the mutex bit. */
+ iowrite8(KEMPLD_MUTEX_KEY, pld->io_index);
+}
+
+static int kempld_get_info_generic(struct kempld_device_data *pld)
+{
+ u16 version;
+ u8 spec;
+
+ kempld_get_mutex(pld);
+
+ version = kempld_read16(pld, KEMPLD_VERSION);
+ spec = kempld_read8(pld, KEMPLD_SPEC);
+ pld->info.buildnr = kempld_read16(pld, KEMPLD_BUILDNR);
+
+ pld->info.minor = KEMPLD_VERSION_GET_MINOR(version);
+ pld->info.major = KEMPLD_VERSION_GET_MAJOR(version);
+ pld->info.number = KEMPLD_VERSION_GET_NUMBER(version);
+ pld->info.type = KEMPLD_VERSION_GET_TYPE(version);
+
+ if (spec == 0xff) {
+ pld->info.spec_minor = 0;
+ pld->info.spec_major = 1;
+ } else {
+ pld->info.spec_minor = KEMPLD_SPEC_GET_MINOR(spec);
+ pld->info.spec_major = KEMPLD_SPEC_GET_MAJOR(spec);
+ }
+
+ if (pld->info.spec_major > 0)
+ pld->feature_mask = kempld_read16(pld, KEMPLD_FEATURE);
+ else
+ pld->feature_mask = 0;
+
+ kempld_release_mutex(pld);
+
+ return 0;
+}
+
+enum kempld_cells {
+ KEMPLD_I2C = 0,
+ KEMPLD_WDT,
+ KEMPLD_GPIO,
+ KEMPLD_UART,
+};
+
+static const struct mfd_cell kempld_devs[] = {
+ [KEMPLD_I2C] = {
+ .name = "kempld-i2c",
+ },
+ [KEMPLD_WDT] = {
+ .name = "kempld-wdt",
+ },
+ [KEMPLD_GPIO] = {
+ .name = "kempld-gpio",
+ },
+ [KEMPLD_UART] = {
+ .name = "kempld-uart",
+ },
+};
+
+#define KEMPLD_MAX_DEVS ARRAY_SIZE(kempld_devs)
+
+static int kempld_register_cells_generic(struct kempld_device_data *pld)
+{
+ struct mfd_cell devs[KEMPLD_MAX_DEVS];
+ int i = 0;
+
+ if (pld->feature_mask & KEMPLD_FEATURE_BIT_I2C)
+ devs[i++] = kempld_devs[KEMPLD_I2C];
+
+ if (pld->feature_mask & KEMPLD_FEATURE_BIT_WATCHDOG)
+ devs[i++] = kempld_devs[KEMPLD_WDT];
+
+ if (pld->feature_mask & KEMPLD_FEATURE_BIT_GPIO)
+ devs[i++] = kempld_devs[KEMPLD_GPIO];
+
+ if (pld->feature_mask & KEMPLD_FEATURE_MASK_UART)
+ devs[i++] = kempld_devs[KEMPLD_UART];
+
+ return mfd_add_devices(pld->dev, -1, devs, i, NULL, 0, NULL);
+}
+
+static struct resource kempld_ioresource = {
+ .start = KEMPLD_IOINDEX,
+ .end = KEMPLD_IODATA,
+ .flags = IORESOURCE_IO,
+};
+
+static const struct kempld_platform_data kempld_platform_data_generic = {
+ .pld_clock = KEMPLD_CLK,
+ .ioresource = &kempld_ioresource,
+ .get_hardware_mutex = kempld_get_hardware_mutex,
+ .release_hardware_mutex = kempld_release_hardware_mutex,
+ .get_info = kempld_get_info_generic,
+ .register_cells = kempld_register_cells_generic,
+};
+
+static struct platform_device *kempld_pdev;
+
+static int kempld_create_platform_device(const struct dmi_system_id *id)
+{
+ struct kempld_platform_data *pdata = id->driver_data;
+ int ret;
+
+ kempld_pdev = platform_device_alloc("kempld", -1);
+ if (!kempld_pdev)
+ return -ENOMEM;
+
+ ret = platform_device_add_data(kempld_pdev, pdata, sizeof(*pdata));
+ if (ret)
+ goto err;
+
+ ret = platform_device_add_resources(kempld_pdev, pdata->ioresource, 1);
+ if (ret)
+ goto err;
+
+ ret = platform_device_add(kempld_pdev);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ platform_device_put(kempld_pdev);
+ return ret;
+}
+
+/**
+ * kempld_read8 - read 8 bit register
+ * @pld: kempld_device_data structure describing the PLD
+ * @index: register index on the chip
+ *
+ * kempld_get_mutex must be called prior to calling this function.
+ */
+u8 kempld_read8(struct kempld_device_data *pld, u8 index)
+{
+ iowrite8(index, pld->io_index);
+ return ioread8(pld->io_data);
+}
+EXPORT_SYMBOL_GPL(kempld_read8);
+
+/**
+ * kempld_write8 - write 8 bit register
+ * @pld: kempld_device_data structure describing the PLD
+ * @index: register index on the chip
+ * @data: new register value
+ *
+ * kempld_get_mutex must be called prior to calling this function.
+ */
+void kempld_write8(struct kempld_device_data *pld, u8 index, u8 data)
+{
+ iowrite8(index, pld->io_index);
+ iowrite8(data, pld->io_data);
+}
+EXPORT_SYMBOL_GPL(kempld_write8);
+
+/**
+ * kempld_read16 - read 16 bit register
+ * @pld: kempld_device_data structure describing the PLD
+ * @index: register index on the chip
+ *
+ * kempld_get_mutex must be called prior to calling this function.
+ */
+u16 kempld_read16(struct kempld_device_data *pld, u8 index)
+{
+ return kempld_read8(pld, index) | kempld_read8(pld, index + 1) << 8;
+}
+EXPORT_SYMBOL_GPL(kempld_read16);
+
+/**
+ * kempld_write16 - write 16 bit register
+ * @pld: kempld_device_data structure describing the PLD
+ * @index: register index on the chip
+ * @data: new register value
+ *
+ * kempld_get_mutex must be called prior to calling this function.
+ */
+void kempld_write16(struct kempld_device_data *pld, u8 index, u16 data)
+{
+ kempld_write8(pld, index, (u8)data);
+ kempld_write8(pld, index + 1, (u8)(data >> 8));
+}
+EXPORT_SYMBOL_GPL(kempld_write16);
+
+/**
+ * kempld_read32 - read 32 bit register
+ * @pld: kempld_device_data structure describing the PLD
+ * @index: register index on the chip
+ *
+ * kempld_get_mutex must be called prior to calling this function.
+ */
+u32 kempld_read32(struct kempld_device_data *pld, u8 index)
+{
+ return kempld_read16(pld, index) | kempld_read16(pld, index + 2) << 16;
+}
+EXPORT_SYMBOL_GPL(kempld_read32);
+
+/**
+ * kempld_write32 - write 32 bit register
+ * @pld: kempld_device_data structure describing the PLD
+ * @index: register index on the chip
+ * @data: new register value
+ *
+ * kempld_get_mutex must be called prior to calling this function.
+ */
+void kempld_write32(struct kempld_device_data *pld, u8 index, u32 data)
+{
+ kempld_write16(pld, index, (u16)data);
+ kempld_write16(pld, index + 2, (u16)(data >> 16));
+}
+EXPORT_SYMBOL_GPL(kempld_write32);
+
+/**
+ * kempld_get_mutex - acquire PLD mutex
+ * @pld: kempld_device_data structure describing the PLD
+ */
+void kempld_get_mutex(struct kempld_device_data *pld)
+{
+ struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
+
+ mutex_lock(&pld->lock);
+ pdata->get_hardware_mutex(pld);
+}
+EXPORT_SYMBOL_GPL(kempld_get_mutex);
+
+/**
+ * kempld_release_mutex - release PLD mutex
+ * @pld: kempld_device_data structure describing the PLD
+ */
+void kempld_release_mutex(struct kempld_device_data *pld)
+{
+ struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
+
+ pdata->release_hardware_mutex(pld);
+ mutex_unlock(&pld->lock);
+}
+EXPORT_SYMBOL_GPL(kempld_release_mutex);
+
+/**
+ * kempld_get_info - update device specific information
+ * @pld: kempld_device_data structure describing the PLD
+ *
+ * This function calls the configured board specific kempld_get_info_XXXX
+ * function which is responsible for gathering information about the specific
+ * hardware. The information is then stored within the pld structure.
+ */
+static int kempld_get_info(struct kempld_device_data *pld)
+{
+ int ret;
+ struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
+ char major, minor;
+
+ ret = pdata->get_info(pld);
+ if (ret)
+ return ret;
+
+ /* The Kontron PLD firmware version string has the following format:
+ * Pwxy.zzzz
+ * P: Fixed
+ * w: PLD number - 1 hex digit
+ * x: Major version - 1 alphanumerical digit (0-9A-V)
+ * y: Minor version - 1 alphanumerical digit (0-9A-V)
+ * zzzz: Build number - 4 zero padded hex digits */
+
+ if (pld->info.major < 10)
+ major = pld->info.major + '0';
+ else
+ major = (pld->info.major - 10) + 'A';
+ if (pld->info.minor < 10)
+ minor = pld->info.minor + '0';
+ else
+ minor = (pld->info.minor - 10) + 'A';
+
+ ret = scnprintf(pld->info.version, sizeof(pld->info.version),
+ "P%X%c%c.%04X", pld->info.number, major, minor,
+ pld->info.buildnr);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * kempld_register_cells - register cell drivers
+ *
+ * This function registers cell drivers for the detected hardware by calling
+ * the configured kempld_register_cells_XXXX function which is responsible
+ * to detect and register the needed cell drivers.
+ */
+static int kempld_register_cells(struct kempld_device_data *pld)
+{
+ struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
+
+ return pdata->register_cells(pld);
+}
+
+static const char *kempld_get_type_string(struct kempld_device_data *pld)
+{
+ const char *version_type;
+
+ switch (pld->info.type) {
+ case 0:
+ version_type = "release";
+ break;
+ case 1:
+ version_type = "debug";
+ break;
+ case 2:
+ version_type = "custom";
+ break;
+ default:
+ version_type = "unspecified";
+ break;
+ }
+
+ return version_type;
+}
+
+static ssize_t kempld_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct kempld_device_data *pld = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", pld->info.version);
+}
+
+static ssize_t kempld_specification_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct kempld_device_data *pld = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%d.%d\n", pld->info.spec_major,
+ pld->info.spec_minor);
+}
+
+static ssize_t kempld_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct kempld_device_data *pld = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", kempld_get_type_string(pld));
+}
+
+static DEVICE_ATTR(pld_version, S_IRUGO, kempld_version_show, NULL);
+static DEVICE_ATTR(pld_specification, S_IRUGO, kempld_specification_show,
+ NULL);
+static DEVICE_ATTR(pld_type, S_IRUGO, kempld_type_show, NULL);
+
+static struct attribute *pld_attributes[] = {
+ &dev_attr_pld_version.attr,
+ &dev_attr_pld_specification.attr,
+ &dev_attr_pld_type.attr,
+ NULL
+};
+
+static const struct attribute_group pld_attr_group = {
+ .attrs = pld_attributes,
+};
+
+static int kempld_detect_device(struct kempld_device_data *pld)
+{
+ u8 index_reg;
+ int ret;
+
+ mutex_lock(&pld->lock);
+
+ /* Check for empty IO space */
+ index_reg = ioread8(pld->io_index);
+ if (index_reg == 0xff && ioread8(pld->io_data) == 0xff) {
+ mutex_unlock(&pld->lock);
+ return -ENODEV;
+ }
+
+ /* Release hardware mutex if acquired */
+ if (!(index_reg & KEMPLD_MUTEX_KEY)) {
+ iowrite8(KEMPLD_MUTEX_KEY, pld->io_index);
+ /* PXT and COMe-cPC2 boards may require a second release */
+ iowrite8(KEMPLD_MUTEX_KEY, pld->io_index);
+ }
+
+ mutex_unlock(&pld->lock);
+
+ ret = kempld_get_info(pld);
+ if (ret)
+ return ret;
+
+ dev_info(pld->dev, "Found Kontron PLD - %s (%s), spec %d.%d\n",
+ pld->info.version, kempld_get_type_string(pld),
+ pld->info.spec_major, pld->info.spec_minor);
+
+ ret = sysfs_create_group(&pld->dev->kobj, &pld_attr_group);
+ if (ret)
+ return ret;
+
+ ret = kempld_register_cells(pld);
+ if (ret)
+ sysfs_remove_group(&pld->dev->kobj, &pld_attr_group);
+
+ return ret;
+}
+
+static int kempld_probe(struct platform_device *pdev)
+{
+ struct kempld_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct device *dev = &pdev->dev;
+ struct kempld_device_data *pld;
+ struct resource *ioport;
+ int ret;
+
+ pld = devm_kzalloc(dev, sizeof(*pld), GFP_KERNEL);
+ if (!pld)
+ return -ENOMEM;
+
+ ioport = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!ioport)
+ return -EINVAL;
+
+ pld->io_base = devm_ioport_map(dev, ioport->start,
+ ioport->end - ioport->start);
+ if (!pld->io_base)
+ return -ENOMEM;
+
+ pld->io_index = pld->io_base;
+ pld->io_data = pld->io_base + 1;
+ pld->pld_clock = pdata->pld_clock;
+ pld->dev = dev;
+
+ mutex_init(&pld->lock);
+ platform_set_drvdata(pdev, pld);
+
+ ret = kempld_detect_device(pld);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int kempld_remove(struct platform_device *pdev)
+{
+ struct kempld_device_data *pld = platform_get_drvdata(pdev);
+ struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
+
+ sysfs_remove_group(&pld->dev->kobj, &pld_attr_group);
+
+ mfd_remove_devices(&pdev->dev);
+ pdata->release_hardware_mutex(pld);
+
+ return 0;
+}
+
+static struct platform_driver kempld_driver = {
+ .driver = {
+ .name = "kempld",
+ .owner = THIS_MODULE,
+ },
+ .probe = kempld_probe,
+ .remove = kempld_remove,
+};
+
+static struct dmi_system_id __initdata kempld_dmi_table[] = {
+ {
+ .ident = "BHL6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-bHL6"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ },
+ {
+ .ident = "CCR2",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-bIP2"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "CCR6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-bIP6"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "CHL6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-cHL6"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "CHR2",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "ETXexpress-SC T2"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "CHR2",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "ETXe-SC T2"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "CHR2",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-bSC2"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "CHR6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "ETXexpress-SC T6"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "CHR6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "ETXe-SC T6"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "CHR6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-bSC6"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "CNTG",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "ETXexpress-PC"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "CNTG",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-bPC2"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "CNTX",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "PXT"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "CVV6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-cBT"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "FRI2",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BIOS_VERSION, "FRI2"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "FRI2",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Fish River Island II"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "MBR1",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "ETX-OH"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "MVV1",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-mBT"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "NTC1",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "nanoETXexpress-TT"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "NTC1",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "nETXe-TT"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "NTC1",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-mTT"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "NUP1",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-mCT"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "UNP1",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "microETXexpress-DC"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "UNP1",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-cDC2"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "UNTG",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "microETXexpress-PC"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "UNTG",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-cPC2"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
+ .ident = "UUP6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-cCT6"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ },
+ {
+ .ident = "UTH6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-cTH6"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(dmi, kempld_dmi_table);
+
+static int __init kempld_init(void)
+{
+ const struct dmi_system_id *id;
+ int ret;
+
+ if (force_device_id[0]) {
+ for (id = kempld_dmi_table; id->matches[0].slot != DMI_NONE; id++)
+ if (strstr(id->ident, force_device_id))
+ if (id->callback && id->callback(id))
+ break;
+ if (id->matches[0].slot == DMI_NONE)
+ return -ENODEV;
+ } else {
+ if (!dmi_check_system(kempld_dmi_table))
+ return -ENODEV;
+ }
+
+ ret = platform_driver_register(&kempld_driver);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void __exit kempld_exit(void)
+{
+ if (kempld_pdev)
+ platform_device_unregister(kempld_pdev);
+
+ platform_driver_unregister(&kempld_driver);
+}
+
+module_init(kempld_init);
+module_exit(kempld_exit);
+
+MODULE_DESCRIPTION("KEM PLD Core Driver");
+MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:kempld-core");
diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c
index 24212f45b20..8c29f7b2732 100644
--- a/drivers/mfd/lm3533-core.c
+++ b/drivers/mfd/lm3533-core.c
@@ -382,9 +382,9 @@ static struct attribute_group lm3533_attribute_group = {
.attrs = lm3533_attributes
};
-static int __devinit lm3533_device_als_init(struct lm3533 *lm3533)
+static int lm3533_device_als_init(struct lm3533 *lm3533)
{
- struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
+ struct lm3533_platform_data *pdata = dev_get_platdata(lm3533->dev);
int ret;
if (!pdata->als)
@@ -405,9 +405,9 @@ static int __devinit lm3533_device_als_init(struct lm3533 *lm3533)
return 0;
}
-static int __devinit lm3533_device_bl_init(struct lm3533 *lm3533)
+static int lm3533_device_bl_init(struct lm3533 *lm3533)
{
- struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
+ struct lm3533_platform_data *pdata = dev_get_platdata(lm3533->dev);
int i;
int ret;
@@ -434,9 +434,9 @@ static int __devinit lm3533_device_bl_init(struct lm3533 *lm3533)
return 0;
}
-static int __devinit lm3533_device_led_init(struct lm3533 *lm3533)
+static int lm3533_device_led_init(struct lm3533 *lm3533)
{
- struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
+ struct lm3533_platform_data *pdata = dev_get_platdata(lm3533->dev);
int i;
int ret;
@@ -463,7 +463,7 @@ static int __devinit lm3533_device_led_init(struct lm3533 *lm3533)
return 0;
}
-static int __devinit lm3533_device_setup(struct lm3533 *lm3533,
+static int lm3533_device_setup(struct lm3533 *lm3533,
struct lm3533_platform_data *pdata)
{
int ret;
@@ -479,9 +479,9 @@ static int __devinit lm3533_device_setup(struct lm3533 *lm3533,
return 0;
}
-static int __devinit lm3533_device_init(struct lm3533 *lm3533)
+static int lm3533_device_init(struct lm3533 *lm3533)
{
- struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
+ struct lm3533_platform_data *pdata = dev_get_platdata(lm3533->dev);
int ret;
dev_dbg(lm3533->dev, "%s\n", __func__);
@@ -496,8 +496,8 @@ static int __devinit lm3533_device_init(struct lm3533 *lm3533)
dev_set_drvdata(lm3533->dev, lm3533);
if (gpio_is_valid(lm3533->gpio_hwen)) {
- ret = gpio_request_one(lm3533->gpio_hwen, GPIOF_OUT_INIT_LOW,
- "lm3533-hwen");
+ ret = devm_gpio_request_one(lm3533->dev, lm3533->gpio_hwen,
+ GPIOF_OUT_INIT_LOW, "lm3533-hwen");
if (ret < 0) {
dev_err(lm3533->dev,
"failed to request HWEN GPIO %d\n",
@@ -528,13 +528,11 @@ err_unregister:
mfd_remove_devices(lm3533->dev);
err_disable:
lm3533_disable(lm3533);
- if (gpio_is_valid(lm3533->gpio_hwen))
- gpio_free(lm3533->gpio_hwen);
return ret;
}
-static void __devexit lm3533_device_exit(struct lm3533 *lm3533)
+static void lm3533_device_exit(struct lm3533 *lm3533)
{
dev_dbg(lm3533->dev, "%s\n", __func__);
@@ -542,8 +540,6 @@ static void __devexit lm3533_device_exit(struct lm3533 *lm3533)
mfd_remove_devices(lm3533->dev);
lm3533_disable(lm3533);
- if (gpio_is_valid(lm3533->gpio_hwen))
- gpio_free(lm3533->gpio_hwen);
}
static bool lm3533_readable_register(struct device *dev, unsigned int reg)
@@ -596,7 +592,7 @@ static struct regmap_config regmap_config = {
.precious_reg = lm3533_precious_register,
};
-static int __devinit lm3533_i2c_probe(struct i2c_client *i2c,
+static int lm3533_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct lm3533 *lm3533;
@@ -624,7 +620,7 @@ static int __devinit lm3533_i2c_probe(struct i2c_client *i2c,
return 0;
}
-static int __devexit lm3533_i2c_remove(struct i2c_client *i2c)
+static int lm3533_i2c_remove(struct i2c_client *i2c)
{
struct lm3533 *lm3533 = i2c_get_clientdata(i2c);
@@ -648,7 +644,7 @@ static struct i2c_driver lm3533_i2c_driver = {
},
.id_table = lm3533_i2c_ids,
.probe = lm3533_i2c_probe,
- .remove = __devexit_p(lm3533_i2c_remove),
+ .remove = lm3533_i2c_remove,
};
static int __init lm3533_i2c_init(void)
diff --git a/drivers/mfd/lp3943.c b/drivers/mfd/lp3943.c
new file mode 100644
index 00000000000..335b930112b
--- /dev/null
+++ b/drivers/mfd/lp3943.c
@@ -0,0 +1,167 @@
+/*
+ * TI/National Semiconductor LP3943 MFD Core Driver
+ *
+ * Copyright 2013 Texas Instruments
+ *
+ * Author: Milo Kim <milo.kim@ti.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.
+ *
+ * Driver structure:
+ * LP3943 is an integrated device capable of driving 16 output channels.
+ * It can be used for a GPIO expander and PWM generators.
+ *
+ * LED control General usage for a device
+ * ___________ ____________________________
+ *
+ * LP3943 MFD ---- GPIO expander leds-gpio eg) HW enable pin
+ * |
+ * --- PWM generator leds-pwm eg) PWM input
+ *
+ * Internal two PWM channels are used for LED dimming effect.
+ * And each output pin can be used as a GPIO as well.
+ * The LED functionality can work with GPIOs or PWMs.
+ * LEDs can be controlled with legacy leds-gpio(static brightness) or
+ * leds-pwm drivers(dynamic brightness control).
+ * Alternatively, it can be used for generic GPIO and PWM controller.
+ * For example, a GPIO is HW enable pin of a device.
+ * A PWM is input pin of a backlight device.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/lp3943.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#define LP3943_MAX_REGISTERS 0x09
+
+/* Register configuration for pin MUX */
+static const struct lp3943_reg_cfg lp3943_mux_cfg[] = {
+ /* address, mask, shift */
+ { LP3943_REG_MUX0, 0x03, 0 },
+ { LP3943_REG_MUX0, 0x0C, 2 },
+ { LP3943_REG_MUX0, 0x30, 4 },
+ { LP3943_REG_MUX0, 0xC0, 6 },
+ { LP3943_REG_MUX1, 0x03, 0 },
+ { LP3943_REG_MUX1, 0x0C, 2 },
+ { LP3943_REG_MUX1, 0x30, 4 },
+ { LP3943_REG_MUX1, 0xC0, 6 },
+ { LP3943_REG_MUX2, 0x03, 0 },
+ { LP3943_REG_MUX2, 0x0C, 2 },
+ { LP3943_REG_MUX2, 0x30, 4 },
+ { LP3943_REG_MUX2, 0xC0, 6 },
+ { LP3943_REG_MUX3, 0x03, 0 },
+ { LP3943_REG_MUX3, 0x0C, 2 },
+ { LP3943_REG_MUX3, 0x30, 4 },
+ { LP3943_REG_MUX3, 0xC0, 6 },
+};
+
+static const struct mfd_cell lp3943_devs[] = {
+ {
+ .name = "lp3943-pwm",
+ .of_compatible = "ti,lp3943-pwm",
+ },
+ {
+ .name = "lp3943-gpio",
+ .of_compatible = "ti,lp3943-gpio",
+ },
+};
+
+int lp3943_read_byte(struct lp3943 *lp3943, u8 reg, u8 *read)
+{
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(lp3943->regmap, reg, &val);
+ if (ret < 0)
+ return ret;
+
+ *read = (u8)val;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lp3943_read_byte);
+
+int lp3943_write_byte(struct lp3943 *lp3943, u8 reg, u8 data)
+{
+ return regmap_write(lp3943->regmap, reg, data);
+}
+EXPORT_SYMBOL_GPL(lp3943_write_byte);
+
+int lp3943_update_bits(struct lp3943 *lp3943, u8 reg, u8 mask, u8 data)
+{
+ return regmap_update_bits(lp3943->regmap, reg, mask, data);
+}
+EXPORT_SYMBOL_GPL(lp3943_update_bits);
+
+static const struct regmap_config lp3943_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = LP3943_MAX_REGISTERS,
+};
+
+static int lp3943_probe(struct i2c_client *cl, const struct i2c_device_id *id)
+{
+ struct lp3943 *lp3943;
+ struct device *dev = &cl->dev;
+
+ lp3943 = devm_kzalloc(dev, sizeof(*lp3943), GFP_KERNEL);
+ if (!lp3943)
+ return -ENOMEM;
+
+ lp3943->regmap = devm_regmap_init_i2c(cl, &lp3943_regmap_config);
+ if (IS_ERR(lp3943->regmap))
+ return PTR_ERR(lp3943->regmap);
+
+ lp3943->pdata = dev_get_platdata(dev);
+ lp3943->dev = dev;
+ lp3943->mux_cfg = lp3943_mux_cfg;
+ i2c_set_clientdata(cl, lp3943);
+
+ return mfd_add_devices(dev, -1, lp3943_devs, ARRAY_SIZE(lp3943_devs),
+ NULL, 0, NULL);
+}
+
+static int lp3943_remove(struct i2c_client *cl)
+{
+ struct lp3943 *lp3943 = i2c_get_clientdata(cl);
+
+ mfd_remove_devices(lp3943->dev);
+ return 0;
+}
+
+static const struct i2c_device_id lp3943_ids[] = {
+ { "lp3943", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lp3943_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id lp3943_of_match[] = {
+ { .compatible = "ti,lp3943", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lp3943_of_match);
+#endif
+
+static struct i2c_driver lp3943_driver = {
+ .probe = lp3943_probe,
+ .remove = lp3943_remove,
+ .driver = {
+ .name = "lp3943",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(lp3943_of_match),
+ },
+ .id_table = lp3943_ids,
+};
+
+module_i2c_driver(lp3943_driver);
+
+MODULE_DESCRIPTION("LP3943 MFD Core Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/lp8788.c b/drivers/mfd/lp8788.c
index 3e94a699833..a30bc15fe5b 100644
--- a/drivers/mfd/lp8788.c
+++ b/drivers/mfd/lp8788.c
@@ -71,7 +71,7 @@ static struct resource rtc_irqs[] = {
},
};
-static struct mfd_cell lp8788_devs[] = {
+static const struct mfd_cell lp8788_devs[] = {
/* 4 bucks */
MFD_DEV_WITH_ID(BUCK, 1),
MFD_DEV_WITH_ID(BUCK, 2),
@@ -173,7 +173,7 @@ static const struct regmap_config lp8788_regmap_config = {
static int lp8788_probe(struct i2c_client *cl, const struct i2c_device_id *id)
{
struct lp8788 *lp;
- struct lp8788_platform_data *pdata = cl->dev.platform_data;
+ struct lp8788_platform_data *pdata = dev_get_platdata(&cl->dev);
int ret;
lp = devm_kzalloc(&cl->dev, sizeof(struct lp8788), GFP_KERNEL);
@@ -203,7 +203,7 @@ static int lp8788_probe(struct i2c_client *cl, const struct i2c_device_id *id)
ARRAY_SIZE(lp8788_devs), NULL, 0, NULL);
}
-static int __devexit lp8788_remove(struct i2c_client *cl)
+static int lp8788_remove(struct i2c_client *cl)
{
struct lp8788 *lp = i2c_get_clientdata(cl);
@@ -224,7 +224,7 @@ static struct i2c_driver lp8788_driver = {
.owner = THIS_MODULE,
},
.probe = lp8788_probe,
- .remove = __devexit_p(lp8788_remove),
+ .remove = lp8788_remove,
.id_table = lp8788_ids,
};
diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c
index a22544fe531..7d8482ff586 100644
--- a/drivers/mfd/lpc_ich.c
+++ b/drivers/mfd/lpc_ich.c
@@ -50,11 +50,14 @@
* document number TBD : Panther Point
* document number TBD : Lynx Point
* document number TBD : Lynx Point-LP
+ * document number TBD : Wellsburg
+ * document number TBD : Avoton SoC
+ * document number TBD : Coleto Creek
+ * document number TBD : Wildcat Point-LP
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
@@ -68,15 +71,19 @@
#define ACPIBASE_GPE_END 0x2f
#define ACPIBASE_SMI_OFF 0x30
#define ACPIBASE_SMI_END 0x33
+#define ACPIBASE_PMC_OFF 0x08
+#define ACPIBASE_PMC_END 0x0c
#define ACPIBASE_TCO_OFF 0x60
#define ACPIBASE_TCO_END 0x7f
-#define ACPICTRL 0x44
+#define ACPICTRL_PMCBASE 0x44
#define ACPIBASE_GCS_OFF 0x3410
#define ACPIBASE_GCS_END 0x3414
-#define GPIOBASE 0x48
-#define GPIOCTRL 0x4C
+#define GPIOBASE_ICH0 0x58
+#define GPIOCTRL_ICH0 0x5C
+#define GPIOBASE_ICH6 0x48
+#define GPIOCTRL_ICH6 0x4C
#define RCBABASE 0xf0
@@ -84,8 +91,18 @@
#define wdt_mem_res(i) wdt_res(ICH_RES_MEM_OFF, i)
#define wdt_res(b, i) (&wdt_ich_res[(b) + (i)])
-static int lpc_ich_acpi_save = -1;
-static int lpc_ich_gpio_save = -1;
+struct lpc_ich_priv {
+ int chipset;
+
+ int abase; /* ACPI base */
+ int actrl_pbase; /* ACPI control or PMC base */
+ int gbase; /* GPIO base */
+ int gctrl; /* GPIO control */
+
+ int abase_save; /* Cached ACPI base value */
+ int actrl_pbase_save; /* Cached ACPI control or PMC base value */
+ int gctrl_save; /* Cached GPIO control value */
+};
static struct resource wdt_ich_res[] = {
/* ACPI - TCO */
@@ -96,7 +113,7 @@ static struct resource wdt_ich_res[] = {
{
.flags = IORESOURCE_IO,
},
- /* GCS */
+ /* GCS or PMC */
{
.flags = IORESOURCE_MEM,
},
@@ -194,9 +211,14 @@ enum lpc_chipsets {
LPC_PPT, /* Panther Point */
LPC_LPT, /* Lynx Point */
LPC_LPT_LP, /* Lynx Point-LP */
+ LPC_WBG, /* Wellsburg */
+ LPC_AVN, /* Avoton SoC */
+ LPC_BAYTRAIL, /* Bay Trail SoC */
+ LPC_COLETO, /* Coleto Creek */
+ LPC_WPT_LP, /* Wildcat Point-LP */
};
-struct lpc_ich_info lpc_chipset_info[] __devinitdata = {
+static struct lpc_ich_info lpc_chipset_info[] = {
[LPC_ICH] = {
.name = "ICH",
.iTCO_version = 1,
@@ -284,6 +306,7 @@ struct lpc_ich_info lpc_chipset_info[] __devinitdata = {
[LPC_NM10] = {
.name = "NM10",
.iTCO_version = 2,
+ .gpio_version = ICH_V7_GPIO,
},
[LPC_ICH8] = {
.name = "ICH8 or ICH8R",
@@ -465,6 +488,7 @@ struct lpc_ich_info lpc_chipset_info[] __devinitdata = {
[LPC_PPT] = {
.name = "Panther Point",
.iTCO_version = 2,
+ .gpio_version = ICH_V5_GPIO,
},
[LPC_LPT] = {
.name = "Lynx Point",
@@ -474,6 +498,27 @@ struct lpc_ich_info lpc_chipset_info[] __devinitdata = {
.name = "Lynx Point_LP",
.iTCO_version = 2,
},
+ [LPC_WBG] = {
+ .name = "Wellsburg",
+ .iTCO_version = 2,
+ },
+ [LPC_AVN] = {
+ .name = "Avoton SoC",
+ .iTCO_version = 3,
+ .gpio_version = AVOTON_GPIO,
+ },
+ [LPC_BAYTRAIL] = {
+ .name = "Bay Trail SoC",
+ .iTCO_version = 3,
+ },
+ [LPC_COLETO] = {
+ .name = "Coleto Creek",
+ .iTCO_version = 2,
+ },
+ [LPC_WPT_LP] = {
+ .name = "Wildcat Point_LP",
+ .iTCO_version = 2,
+ },
};
/*
@@ -482,7 +527,7 @@ struct lpc_ich_info lpc_chipset_info[] __devinitdata = {
* pci_driver, because the I/O Controller Hub has also other
* functions that probably will be registered by other drivers.
*/
-static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = {
+static const struct pci_device_id lpc_ich_ids[] = {
{ PCI_VDEVICE(INTEL, 0x2410), LPC_ICH},
{ PCI_VDEVICE(INTEL, 0x2420), LPC_ICH0},
{ PCI_VDEVICE(INTEL, 0x2440), LPC_ICH2},
@@ -655,45 +700,129 @@ static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = {
{ PCI_VDEVICE(INTEL, 0x9c45), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c46), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c47), LPC_LPT_LP},
+ { PCI_VDEVICE(INTEL, 0x8d40), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d41), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d42), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d43), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d44), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d45), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d46), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d47), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d48), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d49), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d4a), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d4b), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d4c), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d4d), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d4e), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d4f), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d50), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d51), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d52), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d53), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d54), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d55), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d56), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d57), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d58), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d59), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d5a), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d5b), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d5c), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d5d), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d5e), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x8d5f), LPC_WBG},
+ { PCI_VDEVICE(INTEL, 0x1f38), LPC_AVN},
+ { PCI_VDEVICE(INTEL, 0x1f39), LPC_AVN},
+ { PCI_VDEVICE(INTEL, 0x1f3a), LPC_AVN},
+ { PCI_VDEVICE(INTEL, 0x1f3b), LPC_AVN},
+ { PCI_VDEVICE(INTEL, 0x0f1c), LPC_BAYTRAIL},
+ { PCI_VDEVICE(INTEL, 0x2390), LPC_COLETO},
+ { PCI_VDEVICE(INTEL, 0x9cc1), LPC_WPT_LP},
+ { PCI_VDEVICE(INTEL, 0x9cc2), LPC_WPT_LP},
+ { PCI_VDEVICE(INTEL, 0x9cc3), LPC_WPT_LP},
+ { PCI_VDEVICE(INTEL, 0x9cc5), LPC_WPT_LP},
+ { PCI_VDEVICE(INTEL, 0x9cc6), LPC_WPT_LP},
+ { PCI_VDEVICE(INTEL, 0x9cc7), LPC_WPT_LP},
+ { PCI_VDEVICE(INTEL, 0x9cc9), LPC_WPT_LP},
{ 0, }, /* End of list */
};
MODULE_DEVICE_TABLE(pci, lpc_ich_ids);
static void lpc_ich_restore_config_space(struct pci_dev *dev)
{
- if (lpc_ich_acpi_save >= 0) {
- pci_write_config_byte(dev, ACPICTRL, lpc_ich_acpi_save);
- lpc_ich_acpi_save = -1;
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
+
+ if (priv->abase_save >= 0) {
+ pci_write_config_byte(dev, priv->abase, priv->abase_save);
+ priv->abase_save = -1;
+ }
+
+ if (priv->actrl_pbase_save >= 0) {
+ pci_write_config_byte(dev, priv->actrl_pbase,
+ priv->actrl_pbase_save);
+ priv->actrl_pbase_save = -1;
}
- if (lpc_ich_gpio_save >= 0) {
- pci_write_config_byte(dev, GPIOCTRL, lpc_ich_gpio_save);
- lpc_ich_gpio_save = -1;
+ if (priv->gctrl_save >= 0) {
+ pci_write_config_byte(dev, priv->gctrl, priv->gctrl_save);
+ priv->gctrl_save = -1;
+ }
+}
+
+static void lpc_ich_enable_acpi_space(struct pci_dev *dev)
+{
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
+ u8 reg_save;
+
+ switch (lpc_chipset_info[priv->chipset].iTCO_version) {
+ case 3:
+ /*
+ * Some chipsets (eg Avoton) enable the ACPI space in the
+ * ACPI BASE register.
+ */
+ pci_read_config_byte(dev, priv->abase, &reg_save);
+ pci_write_config_byte(dev, priv->abase, reg_save | 0x2);
+ priv->abase_save = reg_save;
+ break;
+ default:
+ /*
+ * Most chipsets enable the ACPI space in the ACPI control
+ * register.
+ */
+ pci_read_config_byte(dev, priv->actrl_pbase, &reg_save);
+ pci_write_config_byte(dev, priv->actrl_pbase, reg_save | 0x80);
+ priv->actrl_pbase_save = reg_save;
+ break;
}
}
-static void __devinit lpc_ich_enable_acpi_space(struct pci_dev *dev)
+static void lpc_ich_enable_gpio_space(struct pci_dev *dev)
{
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
u8 reg_save;
- pci_read_config_byte(dev, ACPICTRL, &reg_save);
- pci_write_config_byte(dev, ACPICTRL, reg_save | 0x10);
- lpc_ich_acpi_save = reg_save;
+ pci_read_config_byte(dev, priv->gctrl, &reg_save);
+ pci_write_config_byte(dev, priv->gctrl, reg_save | 0x10);
+ priv->gctrl_save = reg_save;
}
-static void __devinit lpc_ich_enable_gpio_space(struct pci_dev *dev)
+static void lpc_ich_enable_pmc_space(struct pci_dev *dev)
{
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
u8 reg_save;
- pci_read_config_byte(dev, GPIOCTRL, &reg_save);
- pci_write_config_byte(dev, GPIOCTRL, reg_save | 0x10);
- lpc_ich_gpio_save = reg_save;
+ pci_read_config_byte(dev, priv->actrl_pbase, &reg_save);
+ pci_write_config_byte(dev, priv->actrl_pbase, reg_save | 0x2);
+
+ priv->actrl_pbase_save = reg_save;
}
-static void __devinit lpc_ich_finalize_cell(struct mfd_cell *cell,
- const struct pci_device_id *id)
+static void lpc_ich_finalize_cell(struct pci_dev *dev, struct mfd_cell *cell)
{
- cell->platform_data = &lpc_chipset_info[id->driver_data];
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
+
+ cell->platform_data = &lpc_chipset_info[priv->chipset];
cell->pdata_size = sizeof(struct lpc_ich_info);
}
@@ -702,7 +831,7 @@ static void __devinit lpc_ich_finalize_cell(struct mfd_cell *cell,
* GPIO groups and it's enough to have access to one of these to instantiate
* the device.
*/
-static int __devinit lpc_ich_check_conflict_gpio(struct resource *res)
+static int lpc_ich_check_conflict_gpio(struct resource *res)
{
int ret;
u8 use_gpio = 0;
@@ -721,9 +850,9 @@ static int __devinit lpc_ich_check_conflict_gpio(struct resource *res)
return use_gpio ? use_gpio : ret;
}
-static int __devinit lpc_ich_init_gpio(struct pci_dev *dev,
- const struct pci_device_id *id)
+static int lpc_ich_init_gpio(struct pci_dev *dev)
{
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
u32 base_addr_cfg;
u32 base_addr;
int ret;
@@ -731,10 +860,10 @@ static int __devinit lpc_ich_init_gpio(struct pci_dev *dev,
struct resource *res;
/* Setup power management base register */
- pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg);
+ pci_read_config_dword(dev, priv->abase, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
- dev_err(&dev->dev, "I/O space for ACPI uninitialized\n");
+ dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n");
lpc_ich_cells[LPC_GPIO].num_resources--;
goto gpe0_done;
}
@@ -757,10 +886,10 @@ static int __devinit lpc_ich_init_gpio(struct pci_dev *dev,
gpe0_done:
/* Setup GPIO base register */
- pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
+ pci_read_config_dword(dev, priv->gbase, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
- dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
+ dev_notice(&dev->dev, "I/O space for GPIO uninitialized\n");
ret = -ENODEV;
goto gpio_done;
}
@@ -768,7 +897,7 @@ gpe0_done:
/* Older devices provide fewer GPIO and have a smaller resource size. */
res = &gpio_ich_res[ICH_RES_GPIO];
res->start = base_addr;
- switch (lpc_chipset_info[id->driver_data].gpio_version) {
+ switch (lpc_chipset_info[priv->chipset].gpio_version) {
case ICH_V5_GPIO:
case ICH_V10CORP_GPIO:
res->end = res->start + 128 - 1;
@@ -784,10 +913,10 @@ gpe0_done:
acpi_conflict = true;
goto gpio_done;
}
- lpc_chipset_info[id->driver_data].use_gpio = ret;
+ lpc_chipset_info[priv->chipset].use_gpio = ret;
lpc_ich_enable_gpio_space(dev);
- lpc_ich_finalize_cell(&lpc_ich_cells[LPC_GPIO], id);
+ lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_GPIO]);
ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO],
1, NULL, 0, NULL);
@@ -798,19 +927,19 @@ gpio_done:
return ret;
}
-static int __devinit lpc_ich_init_wdt(struct pci_dev *dev,
- const struct pci_device_id *id)
+static int lpc_ich_init_wdt(struct pci_dev *dev)
{
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
u32 base_addr_cfg;
u32 base_addr;
int ret;
struct resource *res;
/* Setup power management base register */
- pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg);
+ pci_read_config_dword(dev, priv->abase, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
- dev_err(&dev->dev, "I/O space for ACPI uninitialized\n");
+ dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n");
ret = -ENODEV;
goto wdt_done;
}
@@ -826,25 +955,42 @@ static int __devinit lpc_ich_init_wdt(struct pci_dev *dev,
lpc_ich_enable_acpi_space(dev);
/*
+ * iTCO v2:
* Get the Memory-Mapped GCS register. To get access to it
* we have to read RCBA from PCI Config space 0xf0 and use
* it as base. GCS = RCBA + ICH6_GCS(0x3410).
+ *
+ * iTCO v3:
+ * Get the Power Management Configuration register. To get access
+ * to it we have to read the PMC BASE from config space and address
+ * the register at offset 0x8.
*/
- if (lpc_chipset_info[id->driver_data].iTCO_version == 2) {
+ if (lpc_chipset_info[priv->chipset].iTCO_version == 1) {
+ /* Don't register iomem for TCO ver 1 */
+ lpc_ich_cells[LPC_WDT].num_resources--;
+ } else if (lpc_chipset_info[priv->chipset].iTCO_version == 2) {
pci_read_config_dword(dev, RCBABASE, &base_addr_cfg);
base_addr = base_addr_cfg & 0xffffc000;
if (!(base_addr_cfg & 1)) {
- pr_err("RCBA is disabled by hardware/BIOS, "
- "device disabled\n");
+ dev_notice(&dev->dev, "RCBA is disabled by "
+ "hardware/BIOS, device disabled\n");
ret = -ENODEV;
goto wdt_done;
}
- res = wdt_mem_res(ICH_RES_MEM_GCS);
+ res = wdt_mem_res(ICH_RES_MEM_GCS_PMC);
res->start = base_addr + ACPIBASE_GCS_OFF;
res->end = base_addr + ACPIBASE_GCS_END;
+ } else if (lpc_chipset_info[priv->chipset].iTCO_version == 3) {
+ lpc_ich_enable_pmc_space(dev);
+ pci_read_config_dword(dev, ACPICTRL_PMCBASE, &base_addr_cfg);
+ base_addr = base_addr_cfg & 0xfffffe00;
+
+ res = wdt_mem_res(ICH_RES_MEM_GCS_PMC);
+ res->start = base_addr + ACPIBASE_PMC_OFF;
+ res->end = base_addr + ACPIBASE_PMC_END;
}
- lpc_ich_finalize_cell(&lpc_ich_cells[LPC_WDT], id);
+ lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]);
ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT],
1, NULL, 0, NULL);
@@ -852,25 +998,55 @@ wdt_done:
return ret;
}
-static int __devinit lpc_ich_probe(struct pci_dev *dev,
+static int lpc_ich_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
+ struct lpc_ich_priv *priv;
int ret;
bool cell_added = false;
- ret = lpc_ich_init_wdt(dev, id);
- if (!ret)
- cell_added = true;
+ priv = devm_kzalloc(&dev->dev,
+ sizeof(struct lpc_ich_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
- ret = lpc_ich_init_gpio(dev, id);
- if (!ret)
- cell_added = true;
+ priv->chipset = id->driver_data;
+
+ priv->actrl_pbase_save = -1;
+ priv->abase_save = -1;
+
+ priv->abase = ACPIBASE;
+ priv->actrl_pbase = ACPICTRL_PMCBASE;
+
+ priv->gctrl_save = -1;
+ if (priv->chipset <= LPC_ICH5) {
+ priv->gbase = GPIOBASE_ICH0;
+ priv->gctrl = GPIOCTRL_ICH0;
+ } else {
+ priv->gbase = GPIOBASE_ICH6;
+ priv->gctrl = GPIOCTRL_ICH6;
+ }
+
+ pci_set_drvdata(dev, priv);
+
+ if (lpc_chipset_info[priv->chipset].iTCO_version) {
+ ret = lpc_ich_init_wdt(dev);
+ if (!ret)
+ cell_added = true;
+ }
+
+ if (lpc_chipset_info[priv->chipset].gpio_version) {
+ ret = lpc_ich_init_gpio(dev);
+ if (!ret)
+ cell_added = true;
+ }
/*
* We only care if at least one or none of the cells registered
* successfully.
*/
if (!cell_added) {
+ dev_warn(&dev->dev, "No MFD cells added\n");
lpc_ich_restore_config_space(dev);
return -ENODEV;
}
@@ -878,7 +1054,7 @@ static int __devinit lpc_ich_probe(struct pci_dev *dev,
return 0;
}
-static void __devexit lpc_ich_remove(struct pci_dev *dev)
+static void lpc_ich_remove(struct pci_dev *dev)
{
mfd_remove_devices(&dev->dev);
lpc_ich_restore_config_space(dev);
@@ -888,21 +1064,10 @@ static struct pci_driver lpc_ich_driver = {
.name = "lpc_ich",
.id_table = lpc_ich_ids,
.probe = lpc_ich_probe,
- .remove = __devexit_p(lpc_ich_remove),
+ .remove = lpc_ich_remove,
};
-static int __init lpc_ich_init(void)
-{
- return pci_register_driver(&lpc_ich_driver);
-}
-
-static void __exit lpc_ich_exit(void)
-{
- pci_unregister_driver(&lpc_ich_driver);
-}
-
-module_init(lpc_ich_init);
-module_exit(lpc_ich_exit);
+module_pci_driver(lpc_ich_driver);
MODULE_AUTHOR("Aaron Sierra <asierra@xes-inc.com>");
MODULE_DESCRIPTION("LPC interface for Intel ICH");
diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c
index f6b9c5c96b2..4ee755034f3 100644
--- a/drivers/mfd/lpc_sch.c
+++ b/drivers/mfd/lpc_sch.c
@@ -23,7 +23,6 @@
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
@@ -45,37 +44,38 @@ static struct resource smbus_sch_resource = {
.flags = IORESOURCE_IO,
};
-
static struct resource gpio_sch_resource = {
.flags = IORESOURCE_IO,
};
-static struct mfd_cell lpc_sch_cells[] = {
- {
- .name = "isch_smbus",
- .num_resources = 1,
- .resources = &smbus_sch_resource,
- },
- {
- .name = "sch_gpio",
- .num_resources = 1,
- .resources = &gpio_sch_resource,
- },
-};
-
static struct resource wdt_sch_resource = {
.flags = IORESOURCE_IO,
};
-static struct mfd_cell tunnelcreek_cells[] = {
- {
- .name = "ie6xx_wdt",
- .num_resources = 1,
- .resources = &wdt_sch_resource,
- },
+static struct mfd_cell lpc_sch_cells[3];
+
+static struct mfd_cell isch_smbus_cell = {
+ .name = "isch_smbus",
+ .num_resources = 1,
+ .resources = &smbus_sch_resource,
+ .ignore_resource_conflicts = true,
+};
+
+static struct mfd_cell sch_gpio_cell = {
+ .name = "sch_gpio",
+ .num_resources = 1,
+ .resources = &gpio_sch_resource,
+ .ignore_resource_conflicts = true,
};
-static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = {
+static struct mfd_cell wdt_sch_cell = {
+ .name = "ie6xx_wdt",
+ .num_resources = 1,
+ .resources = &wdt_sch_resource,
+ .ignore_resource_conflicts = true,
+};
+
+static const struct pci_device_id lpc_sch_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB) },
@@ -83,88 +83,85 @@ static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = {
};
MODULE_DEVICE_TABLE(pci, lpc_sch_ids);
-static int __devinit lpc_sch_probe(struct pci_dev *dev,
+static int lpc_sch_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
unsigned int base_addr_cfg;
unsigned short base_addr;
- int i;
+ int i, cells = 0;
int ret;
pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
- if (!(base_addr_cfg & (1 << 31))) {
- dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n");
- return -ENODEV;
- }
- base_addr = (unsigned short)base_addr_cfg;
- if (base_addr == 0) {
- dev_err(&dev->dev, "I/O space for SMBus uninitialized\n");
- return -ENODEV;
- }
-
- smbus_sch_resource.start = base_addr;
- smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
+ base_addr = 0;
+ if (!(base_addr_cfg & (1 << 31)))
+ dev_warn(&dev->dev, "Decode of the SMBus I/O range disabled\n");
+ else
+ base_addr = (unsigned short)base_addr_cfg;
- pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
- if (!(base_addr_cfg & (1 << 31))) {
- dev_err(&dev->dev, "Decode of the GPIO I/O range disabled\n");
- return -ENODEV;
- }
- base_addr = (unsigned short)base_addr_cfg;
if (base_addr == 0) {
- dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
- return -ENODEV;
+ dev_warn(&dev->dev, "I/O space for SMBus uninitialized\n");
+ } else {
+ lpc_sch_cells[cells++] = isch_smbus_cell;
+ smbus_sch_resource.start = base_addr;
+ smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
}
- gpio_sch_resource.start = base_addr;
-
- if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB)
- gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1;
+ pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
+ base_addr = 0;
+ if (!(base_addr_cfg & (1 << 31)))
+ dev_warn(&dev->dev, "Decode of the GPIO I/O range disabled\n");
else
- gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
-
- for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++)
- lpc_sch_cells[i].id = id->device;
+ base_addr = (unsigned short)base_addr_cfg;
- ret = mfd_add_devices(&dev->dev, 0,
- lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL,
- 0, NULL);
- if (ret)
- goto out_dev;
+ if (base_addr == 0) {
+ dev_warn(&dev->dev, "I/O space for GPIO uninitialized\n");
+ } else {
+ lpc_sch_cells[cells++] = sch_gpio_cell;
+ gpio_sch_resource.start = base_addr;
+ if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB)
+ gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1;
+ else
+ gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
+ }
if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC
- || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) {
+ || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) {
pci_read_config_dword(dev, WDTBASE, &base_addr_cfg);
- if (!(base_addr_cfg & (1 << 31))) {
- dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n");
- ret = -ENODEV;
- goto out_dev;
+ base_addr = 0;
+ if (!(base_addr_cfg & (1 << 31)))
+ dev_warn(&dev->dev, "Decode of the WDT I/O range disabled\n");
+ else
+ base_addr = (unsigned short)base_addr_cfg;
+ if (base_addr == 0)
+ dev_warn(&dev->dev, "I/O space for WDT uninitialized\n");
+ else {
+ lpc_sch_cells[cells++] = wdt_sch_cell;
+ wdt_sch_resource.start = base_addr;
+ wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1;
}
- base_addr = (unsigned short)base_addr_cfg;
- if (base_addr == 0) {
- dev_err(&dev->dev, "I/O space for WDT uninitialized\n");
- ret = -ENODEV;
- goto out_dev;
- }
-
- wdt_sch_resource.start = base_addr;
- wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1;
+ }
- for (i = 0; i < ARRAY_SIZE(tunnelcreek_cells); i++)
- tunnelcreek_cells[i].id = id->device;
+ if (WARN_ON(cells > ARRAY_SIZE(lpc_sch_cells))) {
+ dev_err(&dev->dev, "Cell count exceeds array size");
+ return -ENODEV;
+ }
- ret = mfd_add_devices(&dev->dev, 0, tunnelcreek_cells,
- ARRAY_SIZE(tunnelcreek_cells), NULL,
- 0, NULL);
+ if (cells == 0) {
+ dev_err(&dev->dev, "All decode registers disabled.\n");
+ return -ENODEV;
}
- return ret;
-out_dev:
- mfd_remove_devices(&dev->dev);
+ for (i = 0; i < cells; i++)
+ lpc_sch_cells[i].id = id->device;
+
+ ret = mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL);
+ if (ret)
+ mfd_remove_devices(&dev->dev);
+
return ret;
}
-static void __devexit lpc_sch_remove(struct pci_dev *dev)
+static void lpc_sch_remove(struct pci_dev *dev)
{
mfd_remove_devices(&dev->dev);
}
@@ -173,7 +170,7 @@ static struct pci_driver lpc_sch_driver = {
.name = "lpc_sch",
.id_table = lpc_sch_ids,
.probe = lpc_sch_probe,
- .remove = __devexit_p(lpc_sch_remove),
+ .remove = lpc_sch_remove,
};
module_pci_driver(lpc_sch_driver);
diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c
new file mode 100644
index 00000000000..4a5e885383f
--- /dev/null
+++ b/drivers/mfd/max14577.c
@@ -0,0 +1,478 @@
+/*
+ * max14577.c - mfd core driver for the Maxim 14577/77836
+ *
+ * Copyright (C) 2014 Samsung Electrnoics
+ * Chanwoo Choi <cw00.choi@samsung.com>
+ * Krzysztof Kozlowski <k.kozlowski@samsung.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.
+ *
+ * This driver is based on max8997.c
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/of_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max14577.h>
+#include <linux/mfd/max14577-private.h>
+
+static const struct mfd_cell max14577_devs[] = {
+ {
+ .name = "max14577-muic",
+ .of_compatible = "maxim,max14577-muic",
+ },
+ {
+ .name = "max14577-regulator",
+ .of_compatible = "maxim,max14577-regulator",
+ },
+ { .name = "max14577-charger", },
+};
+
+static const struct mfd_cell max77836_devs[] = {
+ {
+ .name = "max77836-muic",
+ .of_compatible = "maxim,max77836-muic",
+ },
+ {
+ .name = "max77836-regulator",
+ .of_compatible = "maxim,max77836-regulator",
+ },
+ {
+ .name = "max77836-charger",
+ .of_compatible = "maxim,max77836-charger",
+ },
+ {
+ .name = "max77836-battery",
+ .of_compatible = "maxim,max77836-battery",
+ },
+};
+
+static const struct of_device_id max14577_dt_match[] = {
+ {
+ .compatible = "maxim,max14577",
+ .data = (void *)MAXIM_DEVICE_TYPE_MAX14577,
+ },
+ {
+ .compatible = "maxim,max77836",
+ .data = (void *)MAXIM_DEVICE_TYPE_MAX77836,
+ },
+ {},
+};
+
+static bool max14577_muic_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX14577_REG_INT1 ... MAX14577_REG_STATUS3:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static bool max77836_muic_volatile_reg(struct device *dev, unsigned int reg)
+{
+ /* Any max14577 volatile registers are also max77836 volatile. */
+ if (max14577_muic_volatile_reg(dev, reg))
+ return true;
+
+ switch (reg) {
+ case MAX77836_FG_REG_VCELL_MSB ... MAX77836_FG_REG_SOC_LSB:
+ case MAX77836_FG_REG_CRATE_MSB ... MAX77836_FG_REG_CRATE_LSB:
+ case MAX77836_FG_REG_STATUS_H ... MAX77836_FG_REG_STATUS_L:
+ case MAX77836_PMIC_REG_INTSRC:
+ case MAX77836_PMIC_REG_TOPSYS_INT:
+ case MAX77836_PMIC_REG_TOPSYS_STAT:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static const struct regmap_config max14577_muic_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = max14577_muic_volatile_reg,
+ .max_register = MAX14577_REG_END,
+};
+
+static const struct regmap_config max77836_pmic_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = max77836_muic_volatile_reg,
+ .max_register = MAX77836_PMIC_REG_END,
+};
+
+static const struct regmap_irq max14577_irqs[] = {
+ /* INT1 interrupts */
+ { .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, },
+ { .reg_offset = 0, .mask = MAX14577_INT1_ADCLOW_MASK, },
+ { .reg_offset = 0, .mask = MAX14577_INT1_ADCERR_MASK, },
+ /* INT2 interrupts */
+ { .reg_offset = 1, .mask = MAX14577_INT2_CHGTYP_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_CHGDETRUN_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_DCDTMR_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_DBCHG_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_VBVOLT_MASK, },
+ /* INT3 interrupts */
+ { .reg_offset = 2, .mask = MAX14577_INT3_EOC_MASK, },
+ { .reg_offset = 2, .mask = MAX14577_INT3_CGMBC_MASK, },
+ { .reg_offset = 2, .mask = MAX14577_INT3_OVP_MASK, },
+ { .reg_offset = 2, .mask = MAX14577_INT3_MBCCHGERR_MASK, },
+};
+
+static const struct regmap_irq_chip max14577_irq_chip = {
+ .name = "max14577",
+ .status_base = MAX14577_REG_INT1,
+ .mask_base = MAX14577_REG_INTMASK1,
+ .mask_invert = true,
+ .num_regs = 3,
+ .irqs = max14577_irqs,
+ .num_irqs = ARRAY_SIZE(max14577_irqs),
+};
+
+static const struct regmap_irq max77836_muic_irqs[] = {
+ /* INT1 interrupts */
+ { .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, },
+ { .reg_offset = 0, .mask = MAX14577_INT1_ADCLOW_MASK, },
+ { .reg_offset = 0, .mask = MAX14577_INT1_ADCERR_MASK, },
+ { .reg_offset = 0, .mask = MAX77836_INT1_ADC1K_MASK, },
+ /* INT2 interrupts */
+ { .reg_offset = 1, .mask = MAX14577_INT2_CHGTYP_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_CHGDETRUN_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_DCDTMR_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_DBCHG_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_VBVOLT_MASK, },
+ { .reg_offset = 1, .mask = MAX77836_INT2_VIDRM_MASK, },
+ /* INT3 interrupts */
+ { .reg_offset = 2, .mask = MAX14577_INT3_EOC_MASK, },
+ { .reg_offset = 2, .mask = MAX14577_INT3_CGMBC_MASK, },
+ { .reg_offset = 2, .mask = MAX14577_INT3_OVP_MASK, },
+ { .reg_offset = 2, .mask = MAX14577_INT3_MBCCHGERR_MASK, },
+};
+
+static const struct regmap_irq_chip max77836_muic_irq_chip = {
+ .name = "max77836-muic",
+ .status_base = MAX14577_REG_INT1,
+ .mask_base = MAX14577_REG_INTMASK1,
+ .mask_invert = true,
+ .num_regs = 3,
+ .irqs = max77836_muic_irqs,
+ .num_irqs = ARRAY_SIZE(max77836_muic_irqs),
+};
+
+static const struct regmap_irq max77836_pmic_irqs[] = {
+ { .reg_offset = 0, .mask = MAX77836_TOPSYS_INT_T120C_MASK, },
+ { .reg_offset = 0, .mask = MAX77836_TOPSYS_INT_T140C_MASK, },
+};
+
+static const struct regmap_irq_chip max77836_pmic_irq_chip = {
+ .name = "max77836-pmic",
+ .status_base = MAX77836_PMIC_REG_TOPSYS_INT,
+ .mask_base = MAX77836_PMIC_REG_TOPSYS_INT_MASK,
+ .mask_invert = false,
+ .num_regs = 1,
+ .irqs = max77836_pmic_irqs,
+ .num_irqs = ARRAY_SIZE(max77836_pmic_irqs),
+};
+
+static void max14577_print_dev_type(struct max14577 *max14577)
+{
+ u8 reg_data, vendor_id, device_id;
+ int ret;
+
+ ret = max14577_read_reg(max14577->regmap, MAX14577_REG_DEVICEID,
+ &reg_data);
+ if (ret) {
+ dev_err(max14577->dev,
+ "Failed to read DEVICEID register: %d\n", ret);
+ return;
+ }
+
+ vendor_id = ((reg_data & DEVID_VENDORID_MASK) >>
+ DEVID_VENDORID_SHIFT);
+ device_id = ((reg_data & DEVID_DEVICEID_MASK) >>
+ DEVID_DEVICEID_SHIFT);
+
+ dev_info(max14577->dev, "Device type: %u (ID: 0x%x, vendor: 0x%x)\n",
+ max14577->dev_type, device_id, vendor_id);
+}
+
+/*
+ * Max77836 specific initialization code for driver probe.
+ * Adds new I2C dummy device, regmap and regmap IRQ chip.
+ * Unmasks Interrupt Source register.
+ *
+ * On success returns 0.
+ * On failure returns errno and reverts any changes done so far (e.g. remove
+ * I2C dummy device), except masking the INT SRC register.
+ */
+static int max77836_init(struct max14577 *max14577)
+{
+ int ret;
+ u8 intsrc_mask;
+
+ max14577->i2c_pmic = i2c_new_dummy(max14577->i2c->adapter,
+ I2C_ADDR_PMIC);
+ if (!max14577->i2c_pmic) {
+ dev_err(max14577->dev, "Failed to register PMIC I2C device\n");
+ return -ENODEV;
+ }
+ i2c_set_clientdata(max14577->i2c_pmic, max14577);
+
+ max14577->regmap_pmic = devm_regmap_init_i2c(max14577->i2c_pmic,
+ &max77836_pmic_regmap_config);
+ if (IS_ERR(max14577->regmap_pmic)) {
+ ret = PTR_ERR(max14577->regmap_pmic);
+ dev_err(max14577->dev, "Failed to allocate PMIC register map: %d\n",
+ ret);
+ goto err;
+ }
+
+ /* Un-mask MAX77836 Interrupt Source register */
+ ret = max14577_read_reg(max14577->regmap_pmic,
+ MAX77836_PMIC_REG_INTSRC_MASK, &intsrc_mask);
+ if (ret < 0) {
+ dev_err(max14577->dev, "Failed to read PMIC register\n");
+ goto err;
+ }
+
+ intsrc_mask &= ~(MAX77836_INTSRC_MASK_TOP_INT_MASK);
+ intsrc_mask &= ~(MAX77836_INTSRC_MASK_MUIC_CHG_INT_MASK);
+ ret = max14577_write_reg(max14577->regmap_pmic,
+ MAX77836_PMIC_REG_INTSRC_MASK, intsrc_mask);
+ if (ret < 0) {
+ dev_err(max14577->dev, "Failed to write PMIC register\n");
+ goto err;
+ }
+
+ ret = regmap_add_irq_chip(max14577->regmap_pmic, max14577->irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED,
+ 0, &max77836_pmic_irq_chip,
+ &max14577->irq_data_pmic);
+ if (ret != 0) {
+ dev_err(max14577->dev, "Failed to request PMIC IRQ %d: %d\n",
+ max14577->irq, ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ i2c_unregister_device(max14577->i2c_pmic);
+
+ return ret;
+}
+
+/*
+ * Max77836 specific de-initialization code for driver remove.
+ */
+static void max77836_remove(struct max14577 *max14577)
+{
+ regmap_del_irq_chip(max14577->irq, max14577->irq_data_pmic);
+ i2c_unregister_device(max14577->i2c_pmic);
+}
+
+static int max14577_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max14577 *max14577;
+ struct max14577_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct device_node *np = i2c->dev.of_node;
+ int ret = 0;
+ const struct regmap_irq_chip *irq_chip;
+ const struct mfd_cell *mfd_devs;
+ unsigned int mfd_devs_size;
+ int irq_flags;
+
+ if (np) {
+ pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ i2c->dev.platform_data = pdata;
+ }
+
+ if (!pdata) {
+ dev_err(&i2c->dev, "No platform data found.\n");
+ return -EINVAL;
+ }
+
+ max14577 = devm_kzalloc(&i2c->dev, sizeof(*max14577), GFP_KERNEL);
+ if (!max14577)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max14577);
+ max14577->dev = &i2c->dev;
+ max14577->i2c = i2c;
+ max14577->irq = i2c->irq;
+
+ max14577->regmap = devm_regmap_init_i2c(i2c,
+ &max14577_muic_regmap_config);
+ if (IS_ERR(max14577->regmap)) {
+ ret = PTR_ERR(max14577->regmap);
+ dev_err(max14577->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (np) {
+ const struct of_device_id *of_id;
+
+ of_id = of_match_device(max14577_dt_match, &i2c->dev);
+ if (of_id)
+ max14577->dev_type =
+ (enum maxim_device_type)of_id->data;
+ } else {
+ max14577->dev_type = id->driver_data;
+ }
+
+ max14577_print_dev_type(max14577);
+
+ switch (max14577->dev_type) {
+ case MAXIM_DEVICE_TYPE_MAX77836:
+ irq_chip = &max77836_muic_irq_chip;
+ mfd_devs = max77836_devs;
+ mfd_devs_size = ARRAY_SIZE(max77836_devs);
+ irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED;
+ break;
+ case MAXIM_DEVICE_TYPE_MAX14577:
+ default:
+ irq_chip = &max14577_irq_chip;
+ mfd_devs = max14577_devs;
+ mfd_devs_size = ARRAY_SIZE(max14577_devs);
+ irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+ break;
+ }
+
+ ret = regmap_add_irq_chip(max14577->regmap, max14577->irq,
+ irq_flags, 0, irq_chip,
+ &max14577->irq_data);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n",
+ max14577->irq, ret);
+ return ret;
+ }
+
+ /* Max77836 specific initialization code (additional regmap) */
+ if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836) {
+ ret = max77836_init(max14577);
+ if (ret < 0)
+ goto err_max77836;
+ }
+
+ ret = mfd_add_devices(max14577->dev, -1, mfd_devs,
+ mfd_devs_size, NULL, 0,
+ regmap_irq_get_domain(max14577->irq_data));
+ if (ret < 0)
+ goto err_mfd;
+
+ device_init_wakeup(max14577->dev, 1);
+
+ return 0;
+
+err_mfd:
+ if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836)
+ max77836_remove(max14577);
+err_max77836:
+ regmap_del_irq_chip(max14577->irq, max14577->irq_data);
+
+ return ret;
+}
+
+static int max14577_i2c_remove(struct i2c_client *i2c)
+{
+ struct max14577 *max14577 = i2c_get_clientdata(i2c);
+
+ mfd_remove_devices(max14577->dev);
+ regmap_del_irq_chip(max14577->irq, max14577->irq_data);
+ if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836)
+ max77836_remove(max14577);
+
+ return 0;
+}
+
+static const struct i2c_device_id max14577_i2c_id[] = {
+ { "max14577", MAXIM_DEVICE_TYPE_MAX14577, },
+ { "max77836", MAXIM_DEVICE_TYPE_MAX77836, },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max14577_i2c_id);
+
+#ifdef CONFIG_PM_SLEEP
+static int max14577_suspend(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct max14577 *max14577 = i2c_get_clientdata(i2c);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(max14577->irq);
+ /*
+ * MUIC IRQ must be disabled during suspend because if it happens
+ * while suspended it will be handled before resuming I2C.
+ *
+ * When device is woken up from suspend (e.g. by ADC change),
+ * an interrupt occurs before resuming I2C bus controller.
+ * Interrupt handler tries to read registers but this read
+ * will fail because I2C is still suspended.
+ */
+ disable_irq(max14577->irq);
+
+ return 0;
+}
+
+static int max14577_resume(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct max14577 *max14577 = i2c_get_clientdata(i2c);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(max14577->irq);
+ enable_irq(max14577->irq);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(max14577_pm, max14577_suspend, max14577_resume);
+
+static struct i2c_driver max14577_i2c_driver = {
+ .driver = {
+ .name = "max14577",
+ .owner = THIS_MODULE,
+ .pm = &max14577_pm,
+ .of_match_table = max14577_dt_match,
+ },
+ .probe = max14577_i2c_probe,
+ .remove = max14577_i2c_remove,
+ .id_table = max14577_i2c_id,
+};
+
+static int __init max14577_i2c_init(void)
+{
+ BUILD_BUG_ON(ARRAY_SIZE(max14577_i2c_id) != MAXIM_DEVICE_TYPE_NUM);
+ BUILD_BUG_ON(ARRAY_SIZE(max14577_dt_match) != MAXIM_DEVICE_TYPE_NUM);
+
+ return i2c_add_driver(&max14577_i2c_driver);
+}
+subsys_initcall(max14577_i2c_init);
+
+static void __exit max14577_i2c_exit(void)
+{
+ i2c_del_driver(&max14577_i2c_driver);
+}
+module_exit(max14577_i2c_exit);
+
+MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>");
+MODULE_DESCRIPTION("Maxim 14577/77836 multi-function core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c
index d9e24c849a0..ce869acf27a 100644
--- a/drivers/mfd/max77686.c
+++ b/drivers/mfd/max77686.c
@@ -31,12 +31,14 @@
#include <linux/mfd/max77686.h>
#include <linux/mfd/max77686-private.h>
#include <linux/err.h>
+#include <linux/of.h>
#define I2C_ADDR_RTC (0x0C >> 1)
-static struct mfd_cell max77686_devs[] = {
+static const struct mfd_cell max77686_devs[] = {
{ .name = "max77686-pmic", },
{ .name = "max77686-rtc", },
+ { .name = "max77686-clk", },
};
static struct regmap_config max77686_regmap_config = {
@@ -45,8 +47,8 @@ static struct regmap_config max77686_regmap_config = {
};
#ifdef CONFIG_OF
-static struct of_device_id __devinitdata max77686_pmic_dt_match[] = {
- {.compatible = "maxim,max77686", .data = 0},
+static const struct of_device_id max77686_pmic_dt_match[] = {
+ {.compatible = "maxim,max77686", .data = NULL},
{},
};
@@ -76,7 +78,7 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct max77686_dev *max77686 = NULL;
- struct max77686_platform_data *pdata = i2c->dev.platform_data;
+ struct max77686_platform_data *pdata = dev_get_platdata(&i2c->dev);
unsigned int data;
int ret = 0;
@@ -84,24 +86,15 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
pdata = max77686_i2c_parse_dt_pdata(&i2c->dev);
if (!pdata) {
- ret = -EIO;
dev_err(&i2c->dev, "No platform data found.\n");
- goto err;
+ return -EIO;
}
- max77686 = kzalloc(sizeof(struct max77686_dev), GFP_KERNEL);
+ max77686 = devm_kzalloc(&i2c->dev,
+ sizeof(struct max77686_dev), GFP_KERNEL);
if (max77686 == NULL)
return -ENOMEM;
- max77686->regmap = regmap_init_i2c(i2c, &max77686_regmap_config);
- if (IS_ERR(max77686->regmap)) {
- ret = PTR_ERR(max77686->regmap);
- dev_err(max77686->dev, "Failed to allocate register map: %d\n",
- ret);
- kfree(max77686);
- return ret;
- }
-
i2c_set_clientdata(i2c, max77686);
max77686->dev = &i2c->dev;
max77686->i2c = i2c;
@@ -111,33 +104,38 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
max77686->irq_gpio = pdata->irq_gpio;
max77686->irq = i2c->irq;
+ max77686->regmap = devm_regmap_init_i2c(i2c, &max77686_regmap_config);
+ if (IS_ERR(max77686->regmap)) {
+ ret = PTR_ERR(max77686->regmap);
+ dev_err(max77686->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
if (regmap_read(max77686->regmap,
MAX77686_REG_DEVICE_ID, &data) < 0) {
dev_err(max77686->dev,
"device not found on this channel (this is not an error)\n");
- ret = -ENODEV;
- goto err;
+ return -ENODEV;
} else
dev_info(max77686->dev, "device found\n");
max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
+ if (!max77686->rtc) {
+ dev_err(max77686->dev, "Failed to allocate I2C device for RTC\n");
+ return -ENODEV;
+ }
i2c_set_clientdata(max77686->rtc, max77686);
max77686_irq_init(max77686);
ret = mfd_add_devices(max77686->dev, -1, max77686_devs,
ARRAY_SIZE(max77686_devs), NULL, 0, NULL);
+ if (ret < 0) {
+ mfd_remove_devices(max77686->dev);
+ i2c_unregister_device(max77686->rtc);
+ }
- if (ret < 0)
- goto err_mfd;
-
- return ret;
-
-err_mfd:
- mfd_remove_devices(max77686->dev);
- i2c_unregister_device(max77686->rtc);
-err:
- kfree(max77686);
return ret;
}
@@ -147,7 +145,6 @@ static int max77686_i2c_remove(struct i2c_client *i2c)
mfd_remove_devices(max77686->dev);
i2c_unregister_device(max77686->rtc);
- kfree(max77686);
return 0;
}
diff --git a/drivers/mfd/max77693-irq.c b/drivers/mfd/max77693-irq.c
index 1029d018c73..66b58fe7709 100644
--- a/drivers/mfd/max77693-irq.c
+++ b/drivers/mfd/max77693-irq.c
@@ -128,7 +128,8 @@ static void max77693_irq_sync_unlock(struct irq_data *data)
static const inline struct max77693_irq_data *
irq_to_max77693_irq(struct max77693_dev *max77693, int irq)
{
- return &max77693_irqs[irq];
+ struct irq_data *data = irq_get_irq_data(irq);
+ return &max77693_irqs[data->hwirq];
}
static void max77693_irq_mask(struct irq_data *data)
diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c
index cc5155e2049..7e05428c756 100644
--- a/drivers/mfd/max77693.c
+++ b/drivers/mfd/max77693.c
@@ -28,6 +28,7 @@
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/interrupt.h>
+#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/mutex.h>
#include <linux/mfd/core.h>
@@ -40,7 +41,7 @@
#define I2C_ADDR_MUIC (0x4A >> 1)
#define I2C_ADDR_HAPTIC (0x90 >> 1)
-static struct mfd_cell max77693_devs[] = {
+static const struct mfd_cell max77693_devs[] = {
{ .name = "max77693-pmic", },
{ .name = "max77693-charger", },
{ .name = "max77693-flash", },
@@ -106,11 +107,16 @@ static const struct regmap_config max77693_regmap_config = {
.max_register = MAX77693_PMIC_REG_END,
};
+static const struct regmap_config max77693_regmap_muic_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX77693_MUIC_REG_END,
+};
+
static int max77693_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct max77693_dev *max77693;
- struct max77693_platform_data *pdata = i2c->dev.platform_data;
u8 reg_data;
int ret = 0;
@@ -119,37 +125,41 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
if (max77693 == NULL)
return -ENOMEM;
- max77693->regmap = devm_regmap_init_i2c(i2c, &max77693_regmap_config);
- if (IS_ERR(max77693->regmap)) {
- ret = PTR_ERR(max77693->regmap);
- dev_err(max77693->dev,"failed to allocate register map: %d\n",
- ret);
- goto err_regmap;
- }
-
i2c_set_clientdata(i2c, max77693);
max77693->dev = &i2c->dev;
max77693->i2c = i2c;
max77693->irq = i2c->irq;
max77693->type = id->driver_data;
- if (!pdata)
- goto err_regmap;
-
- max77693->wakeup = pdata->wakeup;
+ max77693->regmap = devm_regmap_init_i2c(i2c, &max77693_regmap_config);
+ if (IS_ERR(max77693->regmap)) {
+ ret = PTR_ERR(max77693->regmap);
+ dev_err(max77693->dev, "failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
- if (max77693_read_reg(max77693->regmap,
- MAX77693_PMIC_REG_PMIC_ID2, &reg_data) < 0) {
+ ret = max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_PMIC_ID2,
+ &reg_data);
+ if (ret < 0) {
dev_err(max77693->dev, "device not found on this channel\n");
- ret = -ENODEV;
- goto err_regmap;
+ return ret;
} else
dev_info(max77693->dev, "device ID: 0x%x\n", reg_data);
max77693->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC);
+ if (!max77693->muic) {
+ dev_err(max77693->dev, "Failed to allocate I2C device for MUIC\n");
+ return -ENODEV;
+ }
i2c_set_clientdata(max77693->muic, max77693);
max77693->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC);
+ if (!max77693->haptic) {
+ dev_err(max77693->dev, "Failed to allocate I2C device for Haptic\n");
+ ret = -ENODEV;
+ goto err_i2c_haptic;
+ }
i2c_set_clientdata(max77693->haptic, max77693);
/*
@@ -158,12 +168,12 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
* before call max77693-muic probe() function.
*/
max77693->regmap_muic = devm_regmap_init_i2c(max77693->muic,
- &max77693_regmap_config);
+ &max77693_regmap_muic_config);
if (IS_ERR(max77693->regmap_muic)) {
ret = PTR_ERR(max77693->regmap_muic);
dev_err(max77693->dev,
"failed to allocate register map: %d\n", ret);
- goto err_regmap;
+ goto err_regmap_muic;
}
ret = max77693_irq_init(max77693);
@@ -177,16 +187,15 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
if (ret < 0)
goto err_mfd;
- device_init_wakeup(max77693->dev, pdata->wakeup);
-
return ret;
err_mfd:
max77693_irq_exit(max77693);
err_irq:
- i2c_unregister_device(max77693->muic);
+err_regmap_muic:
i2c_unregister_device(max77693->haptic);
-err_regmap:
+err_i2c_haptic:
+ i2c_unregister_device(max77693->muic);
return ret;
}
@@ -233,11 +242,19 @@ static const struct dev_pm_ops max77693_pm = {
.resume = max77693_resume,
};
+#ifdef CONFIG_OF
+static const struct of_device_id max77693_dt_match[] = {
+ { .compatible = "maxim,max77693" },
+ {},
+};
+#endif
+
static struct i2c_driver max77693_i2c_driver = {
.driver = {
.name = "max77693",
.owner = THIS_MODULE,
.pm = &max77693_pm,
+ .of_match_table = of_match_ptr(max77693_dt_match),
},
.probe = max77693_i2c_probe,
.remove = max77693_i2c_remove,
diff --git a/drivers/mfd/max8907.c b/drivers/mfd/max8907.c
index 17f2593d82b..232749c8813 100644
--- a/drivers/mfd/max8907.c
+++ b/drivers/mfd/max8907.c
@@ -17,11 +17,12 @@
#include <linux/mfd/core.h>
#include <linux/mfd/max8907.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
-static struct mfd_cell max8907_cells[] = {
+static const struct mfd_cell max8907_cells[] = {
{ .name = "max8907-regulator", },
{ .name = "max8907-rtc", },
};
@@ -183,7 +184,7 @@ static void max8907_power_off(void)
MAX8907_MASK_POWER_OFF, MAX8907_MASK_POWER_OFF);
}
-static __devinit int max8907_i2c_probe(struct i2c_client *i2c,
+static int max8907_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct max8907 *max8907;
@@ -288,7 +289,7 @@ err_alloc_drvdata:
return ret;
}
-static __devexit int max8907_i2c_remove(struct i2c_client *i2c)
+static int max8907_i2c_remove(struct i2c_client *i2c)
{
struct max8907 *max8907 = i2c_get_clientdata(i2c);
@@ -304,7 +305,7 @@ static __devexit int max8907_i2c_remove(struct i2c_client *i2c)
}
#ifdef CONFIG_OF
-static struct of_device_id max8907_of_match[] = {
+static const struct of_device_id max8907_of_match[] = {
{ .compatible = "maxim,max8907" },
{ },
};
diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c
index 9f54c04912f..f3faf0c45dd 100644
--- a/drivers/mfd/max8925-core.c
+++ b/drivers/mfd/max8925-core.c
@@ -14,17 +14,20 @@
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
#include <linux/platform_device.h>
#include <linux/regulator/machine.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max8925.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
-static struct resource bk_resources[] __devinitdata = {
+static struct resource bk_resources[] = {
{ 0x84, 0x84, "mode control", IORESOURCE_REG, },
{ 0x85, 0x85, "control", IORESOURCE_REG, },
};
-static struct mfd_cell bk_devs[] __devinitdata = {
+static struct mfd_cell bk_devs[] = {
{
.name = "max8925-backlight",
.num_resources = ARRAY_SIZE(bk_resources),
@@ -42,7 +45,7 @@ static struct resource touch_resources[] = {
},
};
-static struct mfd_cell touch_devs[] = {
+static const struct mfd_cell touch_devs[] = {
{
.name = "max8925-touch",
.num_resources = 1,
@@ -60,7 +63,7 @@ static struct resource power_supply_resources[] = {
},
};
-static struct mfd_cell power_devs[] = {
+static const struct mfd_cell power_devs[] = {
{
.name = "max8925-power",
.num_resources = 1,
@@ -78,7 +81,7 @@ static struct resource rtc_resources[] = {
},
};
-static struct mfd_cell rtc_devs[] = {
+static const struct mfd_cell rtc_devs[] = {
{
.name = "max8925-rtc",
.num_resources = 1,
@@ -101,7 +104,7 @@ static struct resource onkey_resources[] = {
},
};
-static struct mfd_cell onkey_devs[] = {
+static const struct mfd_cell onkey_devs[] = {
{
.name = "max8925-onkey",
.num_resources = 2,
@@ -110,99 +113,99 @@ static struct mfd_cell onkey_devs[] = {
},
};
-static struct resource sd1_resources[] __devinitdata = {
+static struct resource sd1_resources[] = {
{0x06, 0x06, "sdv", IORESOURCE_REG, },
};
-static struct resource sd2_resources[] __devinitdata = {
+static struct resource sd2_resources[] = {
{0x09, 0x09, "sdv", IORESOURCE_REG, },
};
-static struct resource sd3_resources[] __devinitdata = {
+static struct resource sd3_resources[] = {
{0x0c, 0x0c, "sdv", IORESOURCE_REG, },
};
-static struct resource ldo1_resources[] __devinitdata = {
+static struct resource ldo1_resources[] = {
{0x1a, 0x1a, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo2_resources[] __devinitdata = {
+static struct resource ldo2_resources[] = {
{0x1e, 0x1e, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo3_resources[] __devinitdata = {
+static struct resource ldo3_resources[] = {
{0x22, 0x22, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo4_resources[] __devinitdata = {
+static struct resource ldo4_resources[] = {
{0x26, 0x26, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo5_resources[] __devinitdata = {
+static struct resource ldo5_resources[] = {
{0x2a, 0x2a, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo6_resources[] __devinitdata = {
+static struct resource ldo6_resources[] = {
{0x2e, 0x2e, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo7_resources[] __devinitdata = {
+static struct resource ldo7_resources[] = {
{0x32, 0x32, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo8_resources[] __devinitdata = {
+static struct resource ldo8_resources[] = {
{0x36, 0x36, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo9_resources[] __devinitdata = {
+static struct resource ldo9_resources[] = {
{0x3a, 0x3a, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo10_resources[] __devinitdata = {
+static struct resource ldo10_resources[] = {
{0x3e, 0x3e, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo11_resources[] __devinitdata = {
+static struct resource ldo11_resources[] = {
{0x42, 0x42, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo12_resources[] __devinitdata = {
+static struct resource ldo12_resources[] = {
{0x46, 0x46, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo13_resources[] __devinitdata = {
+static struct resource ldo13_resources[] = {
{0x4a, 0x4a, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo14_resources[] __devinitdata = {
+static struct resource ldo14_resources[] = {
{0x4e, 0x4e, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo15_resources[] __devinitdata = {
+static struct resource ldo15_resources[] = {
{0x52, 0x52, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo16_resources[] __devinitdata = {
+static struct resource ldo16_resources[] = {
{0x12, 0x12, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo17_resources[] __devinitdata = {
+static struct resource ldo17_resources[] = {
{0x16, 0x16, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo18_resources[] __devinitdata = {
+static struct resource ldo18_resources[] = {
{0x74, 0x74, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo19_resources[] __devinitdata = {
+static struct resource ldo19_resources[] = {
{0x5e, 0x5e, "ldov", IORESOURCE_REG, },
};
-static struct resource ldo20_resources[] __devinitdata = {
+static struct resource ldo20_resources[] = {
{0x9e, 0x9e, "ldov", IORESOURCE_REG, },
};
-static struct mfd_cell reg_devs[] __devinitdata = {
+static struct mfd_cell reg_devs[] = {
{
.name = "max8925-regulator",
.id = 0,
@@ -639,17 +642,33 @@ static struct irq_chip max8925_irq_chip = {
.irq_disable = max8925_irq_disable,
};
+static int max8925_irq_domain_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_data(virq, d->host_data);
+ irq_set_chip_and_handler(virq, &max8925_irq_chip, handle_edge_irq);
+ irq_set_nested_thread(virq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(virq, IRQF_VALID);
+#else
+ irq_set_noprobe(virq);
+#endif
+ return 0;
+}
+
+static struct irq_domain_ops max8925_irq_domain_ops = {
+ .map = max8925_irq_domain_map,
+ .xlate = irq_domain_xlate_onetwocell,
+};
+
+
static int max8925_irq_init(struct max8925_chip *chip, int irq,
struct max8925_platform_data *pdata)
{
unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
- int i, ret;
- int __irq;
+ int ret;
+ struct device_node *node = chip->dev->of_node;
- if (!pdata || !pdata->irq_base) {
- dev_warn(chip->dev, "No interrupt support on IRQ base\n");
- return -EINVAL;
- }
/* clear all interrupts */
max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ1);
max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ2);
@@ -667,35 +686,30 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq,
max8925_reg_write(chip->rtc, MAX8925_RTC_IRQ_MASK, 0xff);
mutex_init(&chip->irq_lock);
- chip->core_irq = irq;
- chip->irq_base = pdata->irq_base;
-
- /* register with genirq */
- for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) {
- __irq = i + chip->irq_base;
- irq_set_chip_data(__irq, chip);
- irq_set_chip_and_handler(__irq, &max8925_irq_chip,
- handle_edge_irq);
- irq_set_nested_thread(__irq, 1);
-#ifdef CONFIG_ARM
- set_irq_flags(__irq, IRQF_VALID);
-#else
- irq_set_noprobe(__irq);
-#endif
- }
- if (!irq) {
- dev_warn(chip->dev, "No interrupt support on core IRQ\n");
- goto tsc_irq;
+ chip->irq_base = irq_alloc_descs(-1, 0, MAX8925_NR_IRQS, 0);
+ if (chip->irq_base < 0) {
+ dev_err(chip->dev, "Failed to allocate interrupts, ret:%d\n",
+ chip->irq_base);
+ return -EBUSY;
}
+ irq_domain_add_legacy(node, MAX8925_NR_IRQS, chip->irq_base, 0,
+ &max8925_irq_domain_ops, chip);
+
+ /* request irq handler for pmic main irq*/
+ chip->core_irq = irq;
+ if (!chip->core_irq)
+ return -EBUSY;
ret = request_threaded_irq(irq, NULL, max8925_irq, flags | IRQF_ONESHOT,
"max8925", chip);
if (ret) {
dev_err(chip->dev, "Failed to request core IRQ: %d\n", ret);
chip->core_irq = 0;
+ return -EBUSY;
}
-tsc_irq:
+ /* request irq handler for pmic tsc irq*/
+
/* mask TSC interrupt */
max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0x0f);
@@ -704,7 +718,6 @@ tsc_irq:
return 0;
}
chip->tsc_irq = pdata->tsc_irq;
-
ret = request_threaded_irq(chip->tsc_irq, NULL, max8925_tsc_irq,
flags | IRQF_ONESHOT, "max8925-tsc", chip);
if (ret) {
@@ -714,7 +727,7 @@ tsc_irq:
return 0;
}
-static void __devinit init_regulator(struct max8925_chip *chip,
+static void init_regulator(struct max8925_chip *chip,
struct max8925_platform_data *pdata)
{
int ret;
@@ -821,7 +834,7 @@ static void __devinit init_regulator(struct max8925_chip *chip,
}
}
-int __devinit max8925_device_init(struct max8925_chip *chip,
+int max8925_device_init(struct max8925_chip *chip,
struct max8925_platform_data *pdata)
{
int ret;
@@ -846,7 +859,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
ARRAY_SIZE(rtc_devs),
- &rtc_resources[0], chip->irq_base, NULL);
+ NULL, chip->irq_base, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add rtc subdev\n");
goto out;
@@ -854,7 +867,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
ARRAY_SIZE(onkey_devs),
- &onkey_resources[0], 0, NULL);
+ NULL, chip->irq_base, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add onkey subdev\n");
goto out_dev;
@@ -873,21 +886,19 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
goto out_dev;
}
- if (pdata && pdata->power) {
- ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
- ARRAY_SIZE(power_devs),
- &power_supply_resources[0], 0, NULL);
- if (ret < 0) {
- dev_err(chip->dev, "Failed to add power supply "
- "subdev\n");
- goto out_dev;
- }
+ ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
+ ARRAY_SIZE(power_devs),
+ NULL, 0, NULL);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to add power supply subdev, err = %d\n", ret);
+ goto out_dev;
}
if (pdata && pdata->touch) {
ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
ARRAY_SIZE(touch_devs),
- &touch_resources[0], 0, NULL);
+ NULL, chip->tsc_irq, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add touch subdev\n");
goto out_dev;
@@ -901,7 +912,7 @@ out:
return ret;
}
-void __devexit max8925_device_exit(struct max8925_chip *chip)
+void max8925_device_exit(struct max8925_chip *chip)
{
if (chip->core_irq)
free_irq(chip->core_irq, chip);
diff --git a/drivers/mfd/max8925-i2c.c b/drivers/mfd/max8925-i2c.c
index d9e4b36edee..a83eed5c15c 100644
--- a/drivers/mfd/max8925-i2c.c
+++ b/drivers/mfd/max8925-i2c.c
@@ -135,18 +135,43 @@ static const struct i2c_device_id max8925_id_table[] = {
};
MODULE_DEVICE_TABLE(i2c, max8925_id_table);
-static int __devinit max8925_probe(struct i2c_client *client,
+static int max8925_dt_init(struct device_node *np, struct device *dev,
+ struct max8925_platform_data *pdata)
+{
+ int ret;
+
+ ret = of_property_read_u32(np, "maxim,tsc-irq", &pdata->tsc_irq);
+ if (ret) {
+ dev_err(dev, "Not found maxim,tsc-irq property\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int max8925_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct max8925_platform_data *pdata = client->dev.platform_data;
+ struct max8925_platform_data *pdata = dev_get_platdata(&client->dev);
static struct max8925_chip *chip;
-
- if (!pdata) {
+ struct device_node *node = client->dev.of_node;
+
+ if (node && !pdata) {
+ /* parse DT to get platform data */
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct max8925_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (max8925_dt_init(node, &client->dev, pdata))
+ return -EINVAL;
+ } else if (!pdata) {
pr_info("%s: platform data is missing\n", __func__);
return -EINVAL;
}
- chip = kzalloc(sizeof(struct max8925_chip), GFP_KERNEL);
+ chip = devm_kzalloc(&client->dev,
+ sizeof(struct max8925_chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
chip->i2c = client;
@@ -156,9 +181,18 @@ static int __devinit max8925_probe(struct i2c_client *client,
mutex_init(&chip->io_lock);
chip->rtc = i2c_new_dummy(chip->i2c->adapter, RTC_I2C_ADDR);
+ if (!chip->rtc) {
+ dev_err(chip->dev, "Failed to allocate I2C device for RTC\n");
+ return -ENODEV;
+ }
i2c_set_clientdata(chip->rtc, chip);
chip->adc = i2c_new_dummy(chip->i2c->adapter, ADC_I2C_ADDR);
+ if (!chip->adc) {
+ dev_err(chip->dev, "Failed to allocate I2C device for ADC\n");
+ i2c_unregister_device(chip->rtc);
+ return -ENODEV;
+ }
i2c_set_clientdata(chip->adc, chip);
device_init_wakeup(&client->dev, 1);
@@ -168,14 +202,13 @@ static int __devinit max8925_probe(struct i2c_client *client,
return 0;
}
-static int __devexit max8925_remove(struct i2c_client *client)
+static int max8925_remove(struct i2c_client *client)
{
struct max8925_chip *chip = i2c_get_clientdata(client);
max8925_device_exit(chip);
i2c_unregister_device(chip->adc);
i2c_unregister_device(chip->rtc);
- kfree(chip);
return 0;
}
@@ -203,21 +236,27 @@ static int max8925_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(max8925_pm_ops, max8925_suspend, max8925_resume);
+static const struct of_device_id max8925_dt_ids[] = {
+ { .compatible = "maxim,max8925", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, max8925_dt_ids);
+
static struct i2c_driver max8925_driver = {
.driver = {
.name = "max8925",
.owner = THIS_MODULE,
.pm = &max8925_pm_ops,
+ .of_match_table = max8925_dt_ids,
},
.probe = max8925_probe,
- .remove = __devexit_p(max8925_remove),
+ .remove = max8925_remove,
.id_table = max8925_id_table,
};
static int __init max8925_i2c_init(void)
{
int ret;
-
ret = i2c_add_driver(&max8925_driver);
if (ret != 0)
pr_err("Failed to register MAX8925 I2C driver: %d\n", ret);
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index f123517065e..595364ee178 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -21,8 +21,11 @@
* This driver is based on max8998.c
*/
+#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
#include <linux/module.h>
@@ -37,7 +40,7 @@
#define I2C_ADDR_RTC (0x0C >> 1)
#define I2C_ADDR_HAPTIC (0x90 >> 1)
-static struct mfd_cell max8997_devs[] = {
+static const struct mfd_cell max8997_devs[] = {
{ .name = "max8997-pmic", },
{ .name = "max8997-rtc", },
{ .name = "max8997-battery", },
@@ -47,6 +50,13 @@ static struct mfd_cell max8997_devs[] = {
{ .name = "max8997-led", .id = 2 },
};
+#ifdef CONFIG_OF
+static const struct of_device_id max8997_pmic_dt_match[] = {
+ { .compatible = "maxim,max8997-pmic", .data = (void *)TYPE_MAX8997 },
+ {},
+};
+#endif
+
int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
{
struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
@@ -123,53 +133,120 @@ int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
}
EXPORT_SYMBOL_GPL(max8997_update_reg);
+/*
+ * Only the common platform data elements for max8997 are parsed here from the
+ * device tree. Other sub-modules of max8997 such as pmic, rtc and others have
+ * to parse their own platform data elements from device tree.
+ *
+ * The max8997 platform data structure is instantiated here and the drivers for
+ * the sub-modules need not instantiate another instance while parsing their
+ * platform data.
+ */
+static struct max8997_platform_data *max8997_i2c_parse_dt_pdata(
+ struct device *dev)
+{
+ struct max8997_platform_data *pd;
+
+ pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+ if (!pd) {
+ dev_err(dev, "could not allocate memory for pdata\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ pd->ono = irq_of_parse_and_map(dev->of_node, 1);
+
+ /*
+ * ToDo: the 'wakeup' member in the platform data is more of a linux
+ * specfic information. Hence, there is no binding for that yet and
+ * not parsed here.
+ */
+
+ return pd;
+}
+
+static inline unsigned long max8997_i2c_get_driver_data(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ if (IS_ENABLED(CONFIG_OF) && i2c->dev.of_node) {
+ const struct of_device_id *match;
+ match = of_match_node(max8997_pmic_dt_match, i2c->dev.of_node);
+ return (unsigned long)match->data;
+ }
+ return id->driver_data;
+}
+
static int max8997_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct max8997_dev *max8997;
- struct max8997_platform_data *pdata = i2c->dev.platform_data;
+ struct max8997_platform_data *pdata = dev_get_platdata(&i2c->dev);
int ret = 0;
- max8997 = kzalloc(sizeof(struct max8997_dev), GFP_KERNEL);
+ max8997 = devm_kzalloc(&i2c->dev, sizeof(struct max8997_dev),
+ GFP_KERNEL);
if (max8997 == NULL)
return -ENOMEM;
i2c_set_clientdata(i2c, max8997);
max8997->dev = &i2c->dev;
max8997->i2c = i2c;
- max8997->type = id->driver_data;
+ max8997->type = max8997_i2c_get_driver_data(i2c, id);
max8997->irq = i2c->irq;
+ if (IS_ENABLED(CONFIG_OF) && max8997->dev->of_node) {
+ pdata = max8997_i2c_parse_dt_pdata(max8997->dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ }
+
if (!pdata)
- goto err;
+ return ret;
+ max8997->pdata = pdata;
max8997->ono = pdata->ono;
mutex_init(&max8997->iolock);
max8997->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
+ if (!max8997->rtc) {
+ dev_err(max8997->dev, "Failed to allocate I2C device for RTC\n");
+ return -ENODEV;
+ }
i2c_set_clientdata(max8997->rtc, max8997);
+
max8997->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC);
+ if (!max8997->haptic) {
+ dev_err(max8997->dev, "Failed to allocate I2C device for Haptic\n");
+ ret = -ENODEV;
+ goto err_i2c_haptic;
+ }
i2c_set_clientdata(max8997->haptic, max8997);
+
max8997->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC);
+ if (!max8997->muic) {
+ dev_err(max8997->dev, "Failed to allocate I2C device for MUIC\n");
+ ret = -ENODEV;
+ goto err_i2c_muic;
+ }
i2c_set_clientdata(max8997->muic, max8997);
pm_runtime_set_active(max8997->dev);
max8997_irq_init(max8997);
- mfd_add_devices(max8997->dev, -1, max8997_devs,
+ ret = mfd_add_devices(max8997->dev, -1, max8997_devs,
ARRAY_SIZE(max8997_devs),
NULL, 0, NULL);
+ if (ret < 0) {
+ dev_err(max8997->dev, "failed to add MFD devices %d\n", ret);
+ goto err_mfd;
+ }
/*
* TODO: enable others (flash, muic, rtc, battery, ...) and
* check the return value
*/
- if (ret < 0)
- goto err_mfd;
-
/* MAX8997 has a power button input. */
device_init_wakeup(max8997->dev, pdata->wakeup);
@@ -178,10 +255,10 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
err_mfd:
mfd_remove_devices(max8997->dev);
i2c_unregister_device(max8997->muic);
+err_i2c_muic:
i2c_unregister_device(max8997->haptic);
+err_i2c_haptic:
i2c_unregister_device(max8997->rtc);
-err:
- kfree(max8997);
return ret;
}
@@ -193,7 +270,6 @@ static int max8997_i2c_remove(struct i2c_client *i2c)
i2c_unregister_device(max8997->muic);
i2c_unregister_device(max8997->haptic);
i2c_unregister_device(max8997->rtc);
- kfree(max8997);
return 0;
}
@@ -434,6 +510,7 @@ static struct i2c_driver max8997_i2c_driver = {
.name = "max8997",
.owner = THIS_MODULE,
.pm = &max8997_pm,
+ .of_match_table = of_match_ptr(max8997_pmic_dt_match),
},
.probe = max8997_i2c_probe,
.remove = max8997_i2c_remove,
diff --git a/drivers/mfd/max8998-irq.c b/drivers/mfd/max8998-irq.c
index 5919710dc9e..c469477eb77 100644
--- a/drivers/mfd/max8998-irq.c
+++ b/drivers/mfd/max8998-irq.c
@@ -14,6 +14,7 @@
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/mfd/max8998-private.h>
struct max8998_irq_data {
@@ -99,7 +100,8 @@ static struct max8998_irq_data max8998_irqs[] = {
static inline struct max8998_irq_data *
irq_to_max8998_irq(struct max8998_dev *max8998, int irq)
{
- return &max8998_irqs[irq - max8998->irq_base];
+ struct irq_data *data = irq_get_irq_data(irq);
+ return &max8998_irqs[data->hwirq];
}
static void max8998_irq_lock(struct irq_data *data)
@@ -176,8 +178,14 @@ static irqreturn_t max8998_irq_thread(int irq, void *data)
/* Report */
for (i = 0; i < MAX8998_IRQ_NR; i++) {
- if (irq_reg[max8998_irqs[i].reg - 1] & max8998_irqs[i].mask)
- handle_nested_irq(max8998->irq_base + i);
+ if (irq_reg[max8998_irqs[i].reg - 1] & max8998_irqs[i].mask) {
+ irq = irq_find_mapping(max8998->irq_domain, i);
+ if (WARN_ON(!irq)) {
+ disable_irq_nosync(max8998->irq);
+ return IRQ_NONE;
+ }
+ handle_nested_irq(irq);
+ }
}
return IRQ_HANDLED;
@@ -185,27 +193,40 @@ static irqreturn_t max8998_irq_thread(int irq, void *data)
int max8998_irq_resume(struct max8998_dev *max8998)
{
- if (max8998->irq && max8998->irq_base)
- max8998_irq_thread(max8998->irq_base, max8998);
+ if (max8998->irq && max8998->irq_domain)
+ max8998_irq_thread(max8998->irq, max8998);
+ return 0;
+}
+
+static int max8998_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ struct max8997_dev *max8998 = d->host_data;
+
+ irq_set_chip_data(irq, max8998);
+ irq_set_chip_and_handler(irq, &max8998_irq_chip, handle_edge_irq);
+ irq_set_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
return 0;
}
+static struct irq_domain_ops max8998_irq_domain_ops = {
+ .map = max8998_irq_domain_map,
+};
+
int max8998_irq_init(struct max8998_dev *max8998)
{
int i;
- int cur_irq;
int ret;
+ struct irq_domain *domain;
if (!max8998->irq) {
dev_warn(max8998->dev,
"No interrupt specified, no interrupts\n");
- max8998->irq_base = 0;
- return 0;
- }
-
- if (!max8998->irq_base) {
- dev_err(max8998->dev,
- "No interrupt base specified, no interrupts\n");
return 0;
}
@@ -221,19 +242,13 @@ int max8998_irq_init(struct max8998_dev *max8998)
max8998_write_reg(max8998->i2c, MAX8998_REG_STATUSM1, 0xff);
max8998_write_reg(max8998->i2c, MAX8998_REG_STATUSM2, 0xff);
- /* register with genirq */
- for (i = 0; i < MAX8998_IRQ_NR; i++) {
- cur_irq = i + max8998->irq_base;
- irq_set_chip_data(cur_irq, max8998);
- irq_set_chip_and_handler(cur_irq, &max8998_irq_chip,
- handle_edge_irq);
- irq_set_nested_thread(cur_irq, 1);
-#ifdef CONFIG_ARM
- set_irq_flags(cur_irq, IRQF_VALID);
-#else
- irq_set_noprobe(cur_irq);
-#endif
+ domain = irq_domain_add_simple(NULL, MAX8998_IRQ_NR,
+ max8998->irq_base, &max8998_irq_domain_ops, max8998);
+ if (!domain) {
+ dev_err(max8998->dev, "could not create irq domain\n");
+ return -ENODEV;
}
+ max8998->irq_domain = domain;
ret = request_threaded_irq(max8998->irq, NULL, max8998_irq_thread,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c
index d7218cc9094..a37cb7444b6 100644
--- a/drivers/mfd/max8998.c
+++ b/drivers/mfd/max8998.c
@@ -20,12 +20,15 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/err.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/pm_runtime.h>
#include <linux/mutex.h>
#include <linux/mfd/core.h>
@@ -34,7 +37,7 @@
#define RTC_I2C_ADDR (0x0c >> 1)
-static struct mfd_cell max8998_devs[] = {
+static const struct mfd_cell max8998_devs[] = {
{
.name = "max8998-pmic",
}, {
@@ -44,7 +47,7 @@ static struct mfd_cell max8998_devs[] = {
},
};
-static struct mfd_cell lp3974_devs[] = {
+static const struct mfd_cell lp3974_devs[] = {
{
.name = "lp3974-pmic",
}, {
@@ -128,22 +131,82 @@ int max8998_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
}
EXPORT_SYMBOL(max8998_update_reg);
+#ifdef CONFIG_OF
+static const struct of_device_id max8998_dt_match[] = {
+ { .compatible = "maxim,max8998", .data = (void *)TYPE_MAX8998 },
+ { .compatible = "national,lp3974", .data = (void *)TYPE_LP3974 },
+ { .compatible = "ti,lp3974", .data = (void *)TYPE_LP3974 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, max8998_dt_match);
+#endif
+
+/*
+ * Only the common platform data elements for max8998 are parsed here from the
+ * device tree. Other sub-modules of max8998 such as pmic, rtc and others have
+ * to parse their own platform data elements from device tree.
+ *
+ * The max8998 platform data structure is instantiated here and the drivers for
+ * the sub-modules need not instantiate another instance while parsing their
+ * platform data.
+ */
+static struct max8998_platform_data *max8998_i2c_parse_dt_pdata(
+ struct device *dev)
+{
+ struct max8998_platform_data *pd;
+
+ pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+ if (!pd)
+ return ERR_PTR(-ENOMEM);
+
+ pd->ono = irq_of_parse_and_map(dev->of_node, 1);
+
+ /*
+ * ToDo: the 'wakeup' member in the platform data is more of a linux
+ * specfic information. Hence, there is no binding for that yet and
+ * not parsed here.
+ */
+ return pd;
+}
+
+static inline unsigned long max8998_i2c_get_driver_data(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ if (IS_ENABLED(CONFIG_OF) && i2c->dev.of_node) {
+ const struct of_device_id *match;
+ match = of_match_node(max8998_dt_match, i2c->dev.of_node);
+ return (unsigned long)match->data;
+ }
+
+ return id->driver_data;
+}
+
static int max8998_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct max8998_platform_data *pdata = i2c->dev.platform_data;
+ struct max8998_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct max8998_dev *max8998;
int ret = 0;
- max8998 = kzalloc(sizeof(struct max8998_dev), GFP_KERNEL);
+ max8998 = devm_kzalloc(&i2c->dev, sizeof(struct max8998_dev),
+ GFP_KERNEL);
if (max8998 == NULL)
return -ENOMEM;
+ if (IS_ENABLED(CONFIG_OF) && i2c->dev.of_node) {
+ pdata = max8998_i2c_parse_dt_pdata(&i2c->dev);
+ if (IS_ERR(pdata)) {
+ ret = PTR_ERR(pdata);
+ goto err;
+ }
+ }
+
i2c_set_clientdata(i2c, max8998);
max8998->dev = &i2c->dev;
max8998->i2c = i2c;
max8998->irq = i2c->irq;
- max8998->type = id->driver_data;
+ max8998->type = max8998_i2c_get_driver_data(i2c, id);
+ max8998->pdata = pdata;
if (pdata) {
max8998->ono = pdata->ono;
max8998->irq_base = pdata->irq_base;
@@ -152,13 +215,17 @@ static int max8998_i2c_probe(struct i2c_client *i2c,
mutex_init(&max8998->iolock);
max8998->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
+ if (!max8998->rtc) {
+ dev_err(&i2c->dev, "Failed to allocate I2C device for RTC\n");
+ return -ENODEV;
+ }
i2c_set_clientdata(max8998->rtc, max8998);
max8998_irq_init(max8998);
pm_runtime_set_active(max8998->dev);
- switch (id->driver_data) {
+ switch (max8998->type) {
case TYPE_LP3974:
ret = mfd_add_devices(max8998->dev, -1,
lp3974_devs, ARRAY_SIZE(lp3974_devs),
@@ -184,7 +251,6 @@ err:
mfd_remove_devices(max8998->dev);
max8998_irq_exit(max8998);
i2c_unregister_device(max8998->rtc);
- kfree(max8998);
return ret;
}
@@ -195,7 +261,6 @@ static int max8998_i2c_remove(struct i2c_client *i2c)
mfd_remove_devices(max8998->dev);
max8998_irq_exit(max8998);
i2c_unregister_device(max8998->rtc);
- kfree(max8998);
return 0;
}
@@ -314,6 +379,7 @@ static struct i2c_driver max8998_i2c_driver = {
.name = "max8998",
.owner = THIS_MODULE,
.pm = &max8998_pm,
+ .of_match_table = of_match_ptr(max8998_dt_match),
},
.probe = max8998_i2c_probe,
.remove = max8998_i2c_remove,
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
index 1aba0238f42..acf5dd712eb 100644
--- a/drivers/mfd/mc13xxx-core.c
+++ b/drivers/mfd/mc13xxx-core.c
@@ -119,6 +119,11 @@
#define MC13XXX_REVISION_FAB (0x03 << 11)
#define MC13XXX_REVISION_ICIDCODE (0x3f << 13)
+#define MC34708_REVISION_REVMETAL (0x07 << 0)
+#define MC34708_REVISION_REVFULL (0x07 << 3)
+#define MC34708_REVISION_FIN (0x07 << 6)
+#define MC34708_REVISION_FAB (0x07 << 9)
+
#define MC13XXX_ADC1 44
#define MC13XXX_ADC1_ADEN (1 << 0)
#define MC13XXX_ADC1_RAND (1 << 1)
@@ -153,11 +158,6 @@ int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
{
int ret;
- BUG_ON(!mutex_is_locked(&mc13xxx->lock));
-
- if (offset > MC13XXX_NUMREGS)
- return -EINVAL;
-
ret = regmap_read(mc13xxx->regmap, offset, val);
dev_vdbg(mc13xxx->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
@@ -167,11 +167,9 @@ EXPORT_SYMBOL(mc13xxx_reg_read);
int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val)
{
- BUG_ON(!mutex_is_locked(&mc13xxx->lock));
-
dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x\n", offset, val);
- if (offset > MC13XXX_NUMREGS || val > 0xffffff)
+ if (val >= BIT(24))
return -EINVAL;
return regmap_write(mc13xxx->regmap, offset, val);
@@ -181,7 +179,6 @@ EXPORT_SYMBOL(mc13xxx_reg_write);
int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset,
u32 mask, u32 val)
{
- BUG_ON(!mutex_is_locked(&mc13xxx->lock));
BUG_ON(val & ~mask);
dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x (mask: 0x%06x)\n",
offset, val, mask);
@@ -410,62 +407,52 @@ static irqreturn_t mc13xxx_irq_thread(int irq, void *data)
return IRQ_RETVAL(handled);
}
-static const char *mc13xxx_chipname[] = {
- [MC13XXX_ID_MC13783] = "mc13783",
- [MC13XXX_ID_MC13892] = "mc13892",
-};
-
#define maskval(reg, mask) (((reg) & (mask)) >> __ffs(mask))
-static int mc13xxx_identify(struct mc13xxx *mc13xxx)
+static void mc13xxx_print_revision(struct mc13xxx *mc13xxx, u32 revision)
{
- u32 icid;
- u32 revision;
- int ret;
-
- /*
- * Get the generation ID from register 46, as apparently some older
- * IC revisions only have this info at this location. Newer ICs seem to
- * have both.
- */
- ret = mc13xxx_reg_read(mc13xxx, 46, &icid);
- if (ret)
- return ret;
+ dev_info(mc13xxx->dev, "%s: rev: %d.%d, "
+ "fin: %d, fab: %d, icid: %d/%d\n",
+ mc13xxx->variant->name,
+ maskval(revision, MC13XXX_REVISION_REVFULL),
+ maskval(revision, MC13XXX_REVISION_REVMETAL),
+ maskval(revision, MC13XXX_REVISION_FIN),
+ maskval(revision, MC13XXX_REVISION_FAB),
+ maskval(revision, MC13XXX_REVISION_ICID),
+ maskval(revision, MC13XXX_REVISION_ICIDCODE));
+}
- icid = (icid >> 6) & 0x7;
+static void mc34708_print_revision(struct mc13xxx *mc13xxx, u32 revision)
+{
+ dev_info(mc13xxx->dev, "%s: rev %d.%d, fin: %d, fab: %d\n",
+ mc13xxx->variant->name,
+ maskval(revision, MC34708_REVISION_REVFULL),
+ maskval(revision, MC34708_REVISION_REVMETAL),
+ maskval(revision, MC34708_REVISION_FIN),
+ maskval(revision, MC34708_REVISION_FAB));
+}
- switch (icid) {
- case 2:
- mc13xxx->ictype = MC13XXX_ID_MC13783;
- break;
- case 7:
- mc13xxx->ictype = MC13XXX_ID_MC13892;
- break;
- default:
- mc13xxx->ictype = MC13XXX_ID_INVALID;
- break;
- }
+/* These are only exported for mc13xxx-i2c and mc13xxx-spi */
+struct mc13xxx_variant mc13xxx_variant_mc13783 = {
+ .name = "mc13783",
+ .print_revision = mc13xxx_print_revision,
+};
+EXPORT_SYMBOL_GPL(mc13xxx_variant_mc13783);
- if (mc13xxx->ictype == MC13XXX_ID_MC13783 ||
- mc13xxx->ictype == MC13XXX_ID_MC13892) {
- ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision);
-
- dev_info(mc13xxx->dev, "%s: rev: %d.%d, "
- "fin: %d, fab: %d, icid: %d/%d\n",
- mc13xxx_chipname[mc13xxx->ictype],
- maskval(revision, MC13XXX_REVISION_REVFULL),
- maskval(revision, MC13XXX_REVISION_REVMETAL),
- maskval(revision, MC13XXX_REVISION_FIN),
- maskval(revision, MC13XXX_REVISION_FAB),
- maskval(revision, MC13XXX_REVISION_ICID),
- maskval(revision, MC13XXX_REVISION_ICIDCODE));
- }
+struct mc13xxx_variant mc13xxx_variant_mc13892 = {
+ .name = "mc13892",
+ .print_revision = mc13xxx_print_revision,
+};
+EXPORT_SYMBOL_GPL(mc13xxx_variant_mc13892);
- return (mc13xxx->ictype == MC13XXX_ID_INVALID) ? -ENODEV : 0;
-}
+struct mc13xxx_variant mc13xxx_variant_mc34708 = {
+ .name = "mc34708",
+ .print_revision = mc34708_print_revision,
+};
+EXPORT_SYMBOL_GPL(mc13xxx_variant_mc34708);
static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx)
{
- return mc13xxx_chipname[mc13xxx->ictype];
+ return mc13xxx->variant->name;
}
int mc13xxx_get_flags(struct mc13xxx *mc13xxx)
@@ -649,39 +636,36 @@ static inline int mc13xxx_probe_flags_dt(struct mc13xxx *mc13xxx)
}
#endif
-int mc13xxx_common_init(struct mc13xxx *mc13xxx,
- struct mc13xxx_platform_data *pdata, int irq)
+int mc13xxx_common_init(struct device *dev)
{
+ struct mc13xxx_platform_data *pdata = dev_get_platdata(dev);
+ struct mc13xxx *mc13xxx = dev_get_drvdata(dev);
int ret;
+ u32 revision;
- mc13xxx_lock(mc13xxx);
+ mc13xxx->dev = dev;
- ret = mc13xxx_identify(mc13xxx);
+ ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision);
if (ret)
- goto err_revision;
+ return ret;
+
+ mc13xxx->variant->print_revision(mc13xxx, revision);
/* mask all irqs */
ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK0, 0x00ffffff);
if (ret)
- goto err_mask;
+ return ret;
ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK1, 0x00ffffff);
if (ret)
- goto err_mask;
-
- ret = request_threaded_irq(irq, NULL, mc13xxx_irq_thread,
- IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx);
-
- if (ret) {
-err_mask:
-err_revision:
- mc13xxx_unlock(mc13xxx);
return ret;
- }
- mc13xxx->irq = irq;
+ mutex_init(&mc13xxx->lock);
- mc13xxx_unlock(mc13xxx);
+ ret = request_threaded_irq(mc13xxx->irq, NULL, mc13xxx_irq_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx);
+ if (ret)
+ return ret;
if (mc13xxx_probe_flags_dt(mc13xxx) < 0 && pdata)
mc13xxx->flags = pdata->flags;
@@ -689,17 +673,9 @@ err_revision:
if (mc13xxx->flags & MC13XXX_USE_ADC)
mc13xxx_add_subdevice(mc13xxx, "%s-adc");
- if (mc13xxx->flags & MC13XXX_USE_CODEC)
- mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec",
- pdata->codec, sizeof(*pdata->codec));
-
if (mc13xxx->flags & MC13XXX_USE_RTC)
mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
- if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN)
- mc13xxx_add_subdevice_pdata(mc13xxx, "%s-ts",
- &pdata->touch, sizeof(pdata->touch));
-
if (pdata) {
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
&pdata->regulators, sizeof(pdata->regulators));
@@ -707,23 +683,37 @@ err_revision:
pdata->leds, sizeof(*pdata->leds));
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-pwrbutton",
pdata->buttons, sizeof(*pdata->buttons));
+ if (mc13xxx->flags & MC13XXX_USE_CODEC)
+ mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec",
+ pdata->codec, sizeof(*pdata->codec));
+ if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN)
+ mc13xxx_add_subdevice_pdata(mc13xxx, "%s-ts",
+ &pdata->touch, sizeof(pdata->touch));
} else {
mc13xxx_add_subdevice(mc13xxx, "%s-regulator");
mc13xxx_add_subdevice(mc13xxx, "%s-led");
mc13xxx_add_subdevice(mc13xxx, "%s-pwrbutton");
+ if (mc13xxx->flags & MC13XXX_USE_CODEC)
+ mc13xxx_add_subdevice(mc13xxx, "%s-codec");
+ if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN)
+ mc13xxx_add_subdevice(mc13xxx, "%s-ts");
}
return 0;
}
EXPORT_SYMBOL_GPL(mc13xxx_common_init);
-void mc13xxx_common_cleanup(struct mc13xxx *mc13xxx)
+int mc13xxx_common_exit(struct device *dev)
{
+ struct mc13xxx *mc13xxx = dev_get_drvdata(dev);
+
free_irq(mc13xxx->irq, mc13xxx);
+ mfd_remove_devices(dev);
+ mutex_destroy(&mc13xxx->lock);
- mfd_remove_devices(mc13xxx->dev);
+ return 0;
}
-EXPORT_SYMBOL_GPL(mc13xxx_common_cleanup);
+EXPORT_SYMBOL_GPL(mc13xxx_common_exit);
MODULE_DESCRIPTION("Core driver for Freescale MC13XXX PMIC");
MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
diff --git a/drivers/mfd/mc13xxx-i2c.c b/drivers/mfd/mc13xxx-i2c.c
index 9d18dde3cd2..ae3addb153a 100644
--- a/drivers/mfd/mc13xxx-i2c.c
+++ b/drivers/mfd/mc13xxx-i2c.c
@@ -10,7 +10,6 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/mutex.h>
#include <linux/mfd/core.h>
#include <linux/mfd/mc13xxx.h>
#include <linux/of.h>
@@ -24,7 +23,10 @@
static const struct i2c_device_id mc13xxx_i2c_device_id[] = {
{
.name = "mc13892",
- .driver_data = MC13XXX_ID_MC13892,
+ .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc13892,
+ }, {
+ .name = "mc34708",
+ .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc34708,
}, {
/* sentinel */
}
@@ -34,7 +36,10 @@ MODULE_DEVICE_TABLE(i2c, mc13xxx_i2c_device_id);
static const struct of_device_id mc13xxx_dt_ids[] = {
{
.compatible = "fsl,mc13892",
- .data = (void *) &mc13xxx_i2c_device_id[0],
+ .data = &mc13xxx_variant_mc13892,
+ }, {
+ .compatible = "fsl,mc34708",
+ .data = &mc13xxx_variant_mc34708,
}, {
/* sentinel */
}
@@ -54,7 +59,6 @@ static int mc13xxx_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct mc13xxx *mc13xxx;
- struct mc13xxx_platform_data *pdata = dev_get_platdata(&client->dev);
int ret;
mc13xxx = devm_kzalloc(&client->dev, sizeof(*mc13xxx), GFP_KERNEL);
@@ -63,35 +67,30 @@ static int mc13xxx_i2c_probe(struct i2c_client *client,
dev_set_drvdata(&client->dev, mc13xxx);
- mc13xxx->dev = &client->dev;
- mutex_init(&mc13xxx->lock);
+ mc13xxx->irq = client->irq;
mc13xxx->regmap = devm_regmap_init_i2c(client,
&mc13xxx_regmap_i2c_config);
if (IS_ERR(mc13xxx->regmap)) {
ret = PTR_ERR(mc13xxx->regmap);
- dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n",
- ret);
- dev_set_drvdata(&client->dev, NULL);
+ dev_err(&client->dev, "Failed to initialize regmap: %d\n", ret);
return ret;
}
- ret = mc13xxx_common_init(mc13xxx, pdata, client->irq);
-
- if (ret == 0 && (id->driver_data != mc13xxx->ictype))
- dev_warn(mc13xxx->dev,
- "device id doesn't match auto detection!\n");
+ if (client->dev.of_node) {
+ const struct of_device_id *of_id =
+ of_match_device(mc13xxx_dt_ids, &client->dev);
+ mc13xxx->variant = of_id->data;
+ } else {
+ mc13xxx->variant = (void *)id->driver_data;
+ }
- return ret;
+ return mc13xxx_common_init(&client->dev);
}
-static int __devexit mc13xxx_i2c_remove(struct i2c_client *client)
+static int mc13xxx_i2c_remove(struct i2c_client *client)
{
- struct mc13xxx *mc13xxx = dev_get_drvdata(&client->dev);
-
- mc13xxx_common_cleanup(mc13xxx);
-
- return 0;
+ return mc13xxx_common_exit(&client->dev);
}
static struct i2c_driver mc13xxx_i2c_driver = {
@@ -102,7 +101,7 @@ static struct i2c_driver mc13xxx_i2c_driver = {
.of_match_table = mc13xxx_dt_ids,
},
.probe = mc13xxx_i2c_probe,
- .remove = __devexit_p(mc13xxx_i2c_remove),
+ .remove = mc13xxx_i2c_remove,
};
static int __init mc13xxx_i2c_init(void)
diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c
index 0bdb43a0aff..702925e242c 100644
--- a/drivers/mfd/mc13xxx-spi.c
+++ b/drivers/mfd/mc13xxx-spi.c
@@ -13,7 +13,6 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
#include <linux/mfd/mc13xxx.h>
@@ -28,10 +27,13 @@
static const struct spi_device_id mc13xxx_device_id[] = {
{
.name = "mc13783",
- .driver_data = MC13XXX_ID_MC13783,
+ .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc13783,
}, {
.name = "mc13892",
- .driver_data = MC13XXX_ID_MC13892,
+ .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc13892,
+ }, {
+ .name = "mc34708",
+ .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc34708,
}, {
/* sentinel */
}
@@ -39,8 +41,9 @@ static const struct spi_device_id mc13xxx_device_id[] = {
MODULE_DEVICE_TABLE(spi, mc13xxx_device_id);
static const struct of_device_id mc13xxx_dt_ids[] = {
- { .compatible = "fsl,mc13783", .data = (void *) MC13XXX_ID_MC13783, },
- { .compatible = "fsl,mc13892", .data = (void *) MC13XXX_ID_MC13892, },
+ { .compatible = "fsl,mc13783", .data = &mc13xxx_variant_mc13783, },
+ { .compatible = "fsl,mc13892", .data = &mc13xxx_variant_mc13892, },
+ { .compatible = "fsl,mc34708", .data = &mc13xxx_variant_mc34708, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids);
@@ -90,10 +93,15 @@ static int mc13xxx_spi_write(void *context, const void *data, size_t count)
{
struct device *dev = context;
struct spi_device *spi = to_spi_device(dev);
+ const char *reg = data;
if (count != 4)
return -ENOTSUPP;
+ /* include errata fix for spi audio problems */
+ if (*reg == MC13783_AUDIO_CODEC || *reg == MC13783_AUDIO_DAC)
+ spi_write(spi, data, count);
+
return spi_write(spi, data, count);
}
@@ -120,7 +128,6 @@ static struct regmap_bus regmap_mc13xxx_bus = {
static int mc13xxx_spi_probe(struct spi_device *spi)
{
struct mc13xxx *mc13xxx;
- struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);
int ret;
mc13xxx = devm_kzalloc(&spi->dev, sizeof(*mc13xxx), GFP_KERNEL);
@@ -128,44 +135,42 @@ static int mc13xxx_spi_probe(struct spi_device *spi)
return -ENOMEM;
dev_set_drvdata(&spi->dev, mc13xxx);
+
spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
- mc13xxx->dev = &spi->dev;
- mutex_init(&mc13xxx->lock);
+ mc13xxx->irq = spi->irq;
+
+ spi->max_speed_hz = spi->max_speed_hz ? : 26000000;
+ ret = spi_setup(spi);
+ if (ret)
+ return ret;
mc13xxx->regmap = devm_regmap_init(&spi->dev, &regmap_mc13xxx_bus,
&spi->dev,
&mc13xxx_regmap_spi_config);
if (IS_ERR(mc13xxx->regmap)) {
ret = PTR_ERR(mc13xxx->regmap);
- dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n",
- ret);
- dev_set_drvdata(&spi->dev, NULL);
+ dev_err(&spi->dev, "Failed to initialize regmap: %d\n", ret);
return ret;
}
- ret = mc13xxx_common_init(mc13xxx, pdata, spi->irq);
+ if (spi->dev.of_node) {
+ const struct of_device_id *of_id =
+ of_match_device(mc13xxx_dt_ids, &spi->dev);
- if (ret) {
- dev_set_drvdata(&spi->dev, NULL);
+ mc13xxx->variant = of_id->data;
} else {
- const struct spi_device_id *devid =
- spi_get_device_id(spi);
- if (!devid || devid->driver_data != mc13xxx->ictype)
- dev_warn(mc13xxx->dev,
- "device id doesn't match auto detection!\n");
+ const struct spi_device_id *id_entry = spi_get_device_id(spi);
+
+ mc13xxx->variant = (void *)id_entry->driver_data;
}
- return ret;
+ return mc13xxx_common_init(&spi->dev);
}
-static int __devexit mc13xxx_spi_remove(struct spi_device *spi)
+static int mc13xxx_spi_remove(struct spi_device *spi)
{
- struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev);
-
- mc13xxx_common_cleanup(mc13xxx);
-
- return 0;
+ return mc13xxx_common_exit(&spi->dev);
}
static struct spi_driver mc13xxx_spi_driver = {
@@ -176,7 +181,7 @@ static struct spi_driver mc13xxx_spi_driver = {
.of_match_table = mc13xxx_dt_ids,
},
.probe = mc13xxx_spi_probe,
- .remove = __devexit_p(mc13xxx_spi_remove),
+ .remove = mc13xxx_spi_remove,
};
static int __init mc13xxx_init(void)
diff --git a/drivers/mfd/mc13xxx.h b/drivers/mfd/mc13xxx.h
index bbba06feea0..ae7f1659f5d 100644
--- a/drivers/mfd/mc13xxx.h
+++ b/drivers/mfd/mc13xxx.h
@@ -13,19 +13,25 @@
#include <linux/regmap.h>
#include <linux/mfd/mc13xxx.h>
-enum mc13xxx_id {
- MC13XXX_ID_MC13783,
- MC13XXX_ID_MC13892,
- MC13XXX_ID_INVALID,
+#define MC13XXX_NUMREGS 0x3f
+
+struct mc13xxx;
+
+struct mc13xxx_variant {
+ const char *name;
+ void (*print_revision)(struct mc13xxx *mc13xxx, u32 revision);
};
-#define MC13XXX_NUMREGS 0x3f
+extern struct mc13xxx_variant
+ mc13xxx_variant_mc13783,
+ mc13xxx_variant_mc13892,
+ mc13xxx_variant_mc34708;
struct mc13xxx {
struct regmap *regmap;
struct device *dev;
- enum mc13xxx_id ictype;
+ const struct mc13xxx_variant *variant;
struct mutex lock;
int irq;
@@ -37,9 +43,7 @@ struct mc13xxx {
int adcflags;
};
-int mc13xxx_common_init(struct mc13xxx *mc13xxx,
- struct mc13xxx_platform_data *pdata, int irq);
-
-void mc13xxx_common_cleanup(struct mc13xxx *mc13xxx);
+int mc13xxx_common_init(struct device *dev);
+int mc13xxx_common_exit(struct device *dev);
#endif /* __DRIVERS_MFD_MC13XXX_H */
diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c
index f99d6299ec2..29d76986b40 100644
--- a/drivers/mfd/mcp-sa11x0.c
+++ b/drivers/mfd/mcp-sa11x0.c
@@ -12,7 +12,6 @@
* MCP read/write timeouts from Jordi Colomer, rehacked by rmk.
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/kernel.h>
@@ -156,7 +155,7 @@ static struct mcp_ops mcp_sa11x0 = {
static int mcp_sa11x0_probe(struct platform_device *dev)
{
- struct mcp_plat_data *data = dev->dev.platform_data;
+ struct mcp_plat_data *data = dev_get_platdata(&dev->dev);
struct resource *mem0, *mem1;
struct mcp_sa11x0 *m;
struct mcp *mcp;
@@ -225,8 +224,6 @@ static int mcp_sa11x0_probe(struct platform_device *dev)
if (ret == 0)
return 0;
- platform_set_drvdata(dev, NULL);
-
err_ioremap:
iounmap(m->base1);
iounmap(m->base0);
@@ -252,7 +249,6 @@ static int mcp_sa11x0_remove(struct platform_device *dev)
mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1);
- platform_set_drvdata(dev, NULL);
mcp_host_del(mcp);
iounmap(m->base1);
iounmap(m->base0);
diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c
index 55d58998141..5e2667afe2b 100644
--- a/drivers/mfd/menelaus.c
+++ b/drivers/mfd/menelaus.c
@@ -41,11 +41,11 @@
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/slab.h>
+#include <linux/mfd/menelaus.h>
#include <asm/mach/irq.h>
#include <asm/gpio.h>
-#include <plat/menelaus.h>
#define DRIVER_NAME "menelaus"
@@ -442,7 +442,7 @@ void menelaus_unregister_mmc_callback(void)
menelaus_remove_irq_work(MENELAUS_MMC_S2D1_IRQ);
the_menelaus->mmc_callback = NULL;
- the_menelaus->mmc_callback_data = 0;
+ the_menelaus->mmc_callback_data = NULL;
}
EXPORT_SYMBOL(menelaus_unregister_mmc_callback);
@@ -466,7 +466,7 @@ static int menelaus_set_voltage(const struct menelaus_vtg *vtg, int mV,
struct i2c_client *c = the_menelaus->client;
mutex_lock(&the_menelaus->lock);
- if (vtg == 0)
+ if (!vtg)
goto set_voltage;
ret = menelaus_read_reg(vtg->vtg_reg);
@@ -1189,7 +1189,7 @@ static int menelaus_probe(struct i2c_client *client,
int rev = 0, val;
int err = 0;
struct menelaus_platform_data *menelaus_pdata =
- client->dev.platform_data;
+ dev_get_platdata(&client->dev);
if (the_menelaus) {
dev_dbg(&client->dev, "only one %s for now\n",
@@ -1197,7 +1197,7 @@ static int menelaus_probe(struct i2c_client *client,
return -ENODEV;
}
- menelaus = kzalloc(sizeof *menelaus, GFP_KERNEL);
+ menelaus = devm_kzalloc(&client->dev, sizeof(*menelaus), GFP_KERNEL);
if (!menelaus)
return -ENOMEM;
@@ -1210,8 +1210,7 @@ static int menelaus_probe(struct i2c_client *client,
rev = menelaus_read_reg(MENELAUS_REV);
if (rev < 0) {
pr_err(DRIVER_NAME ": device not found");
- err = -ENODEV;
- goto fail1;
+ return -ENODEV;
}
/* Ack and disable all Menelaus interrupts */
@@ -1231,7 +1230,7 @@ static int menelaus_probe(struct i2c_client *client,
if (err) {
dev_dbg(&client->dev, "can't get IRQ %d, err %d\n",
client->irq, err);
- goto fail1;
+ return err;
}
}
@@ -1242,7 +1241,7 @@ static int menelaus_probe(struct i2c_client *client,
val = menelaus_read_reg(MENELAUS_VCORE_CTRL1);
if (val < 0)
- goto fail2;
+ goto fail;
if (val & (1 << 7))
menelaus->vcore_hw_mode = 1;
else
@@ -1251,17 +1250,15 @@ static int menelaus_probe(struct i2c_client *client,
if (menelaus_pdata != NULL && menelaus_pdata->late_init != NULL) {
err = menelaus_pdata->late_init(&client->dev);
if (err < 0)
- goto fail2;
+ goto fail;
}
menelaus_rtc_init(menelaus);
return 0;
-fail2:
+fail:
free_irq(client->irq, menelaus);
flush_work(&menelaus->work);
-fail1:
- kfree(menelaus);
return err;
}
@@ -1271,7 +1268,6 @@ static int __exit menelaus_remove(struct i2c_client *client)
free_irq(client->irq, menelaus);
flush_work(&menelaus->work);
- kfree(menelaus);
the_menelaus = NULL;
return 0;
}
@@ -1291,29 +1287,8 @@ static struct i2c_driver menelaus_i2c_driver = {
.id_table = menelaus_id,
};
-static int __init menelaus_init(void)
-{
- int res;
-
- res = i2c_add_driver(&menelaus_i2c_driver);
- if (res < 0) {
- pr_err(DRIVER_NAME ": driver registration failed\n");
- return res;
- }
-
- return 0;
-}
-
-static void __exit menelaus_exit(void)
-{
- i2c_del_driver(&menelaus_i2c_driver);
-
- /* FIXME: Shutdown menelaus parts that can be shut down */
-}
+module_i2c_driver(menelaus_i2c_driver);
MODULE_AUTHOR("Texas Instruments, Inc. (and others)");
MODULE_DESCRIPTION("I2C interface for Menelaus.");
MODULE_LICENSE("GPL");
-
-module_init(menelaus_init);
-module_exit(menelaus_exit);
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index f8b77711ad2..892d343193a 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -20,6 +20,11 @@
#include <linux/module.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+static struct device_type mfd_dev_type = {
+ .name = "mfd_device",
+};
int mfd_cell_enable(struct platform_device *pdev)
{
@@ -59,7 +64,8 @@ int mfd_cell_disable(struct platform_device *pdev)
EXPORT_SYMBOL(mfd_cell_disable);
static int mfd_platform_add_cell(struct platform_device *pdev,
- const struct mfd_cell *cell)
+ const struct mfd_cell *cell,
+ atomic_t *usage_count)
{
if (!cell)
return 0;
@@ -68,11 +74,12 @@ static int mfd_platform_add_cell(struct platform_device *pdev,
if (!pdev->mfd_cell)
return -ENOMEM;
+ pdev->mfd_cell->usage_count = usage_count;
return 0;
}
static int mfd_add_device(struct device *parent, int id,
- const struct mfd_cell *cell,
+ const struct mfd_cell *cell, atomic_t *usage_count,
struct resource *mem_base,
int irq_base, struct irq_domain *domain)
{
@@ -91,6 +98,16 @@ static int mfd_add_device(struct device *parent, int id,
goto fail_device;
pdev->dev.parent = parent;
+ pdev->dev.type = &mfd_dev_type;
+ pdev->dev.dma_mask = parent->dma_mask;
+ pdev->dev.dma_parms = parent->dma_parms;
+
+ ret = regulator_bulk_register_supply_alias(
+ &pdev->dev, cell->parent_supplies,
+ parent, cell->parent_supplies,
+ cell->num_parent_supplies);
+ if (ret < 0)
+ goto fail_res;
if (parent->of_node && cell->of_compatible) {
for_each_child_of_node(parent->of_node, np) {
@@ -105,12 +122,12 @@ static int mfd_add_device(struct device *parent, int id,
ret = platform_device_add_data(pdev,
cell->platform_data, cell->pdata_size);
if (ret)
- goto fail_res;
+ goto fail_alias;
}
- ret = mfd_platform_add_cell(pdev, cell);
+ ret = mfd_platform_add_cell(pdev, cell, usage_count);
if (ret)
- goto fail_res;
+ goto fail_alias;
for (r = 0; r < cell->num_resources; r++) {
res[r].name = cell->resources[r].name;
@@ -145,17 +162,17 @@ static int mfd_add_device(struct device *parent, int id,
if (!cell->ignore_resource_conflicts) {
ret = acpi_check_resource_conflict(&res[r]);
if (ret)
- goto fail_res;
+ goto fail_alias;
}
}
ret = platform_device_add_resources(pdev, res, cell->num_resources);
if (ret)
- goto fail_res;
+ goto fail_alias;
ret = platform_device_add(pdev);
if (ret)
- goto fail_res;
+ goto fail_alias;
if (cell->pm_runtime_no_callbacks)
pm_runtime_no_callbacks(&pdev->dev);
@@ -164,6 +181,10 @@ static int mfd_add_device(struct device *parent, int id,
return 0;
+fail_alias:
+ regulator_bulk_unregister_supply_alias(&pdev->dev,
+ cell->parent_supplies,
+ cell->num_parent_supplies);
fail_res:
kfree(res);
fail_device:
@@ -173,12 +194,12 @@ fail_alloc:
}
int mfd_add_devices(struct device *parent, int id,
- struct mfd_cell *cells, int n_devs,
+ const struct mfd_cell *cells, int n_devs,
struct resource *mem_base,
int irq_base, struct irq_domain *domain)
{
int i;
- int ret = 0;
+ int ret;
atomic_t *cnts;
/* initialize reference counting for all cells */
@@ -188,26 +209,38 @@ int mfd_add_devices(struct device *parent, int id,
for (i = 0; i < n_devs; i++) {
atomic_set(&cnts[i], 0);
- cells[i].usage_count = &cnts[i];
- ret = mfd_add_device(parent, id, cells + i, mem_base,
+ ret = mfd_add_device(parent, id, cells + i, cnts + i, mem_base,
irq_base, domain);
if (ret)
- break;
+ goto fail;
}
- if (ret)
- mfd_remove_devices(parent);
+ return 0;
+fail:
+ if (i)
+ mfd_remove_devices(parent);
+ else
+ kfree(cnts);
return ret;
}
EXPORT_SYMBOL(mfd_add_devices);
static int mfd_remove_devices_fn(struct device *dev, void *c)
{
- struct platform_device *pdev = to_platform_device(dev);
- const struct mfd_cell *cell = mfd_get_cell(pdev);
+ struct platform_device *pdev;
+ const struct mfd_cell *cell;
atomic_t **usage_count = c;
+ if (dev->type != &mfd_dev_type)
+ return 0;
+
+ pdev = to_platform_device(dev);
+ cell = mfd_get_cell(pdev);
+
+ regulator_bulk_unregister_supply_alias(dev, cell->parent_supplies,
+ cell->num_parent_supplies);
+
/* find the base address of usage_count pointers (for freeing) */
if (!*usage_count || (cell->usage_count < *usage_count))
*usage_count = cell->usage_count;
@@ -246,8 +279,8 @@ int mfd_clone_cell(const char *cell, const char **clones, size_t n_clones)
for (i = 0; i < n_clones; i++) {
cell_entry.name = clones[i];
/* don't give up if a single call fails; just report error */
- if (mfd_add_device(pdev->dev.parent, -1, &cell_entry, NULL, 0,
- NULL))
+ if (mfd_add_device(pdev->dev.parent, -1, &cell_entry,
+ cell_entry.usage_count, NULL, 0, NULL))
dev_err(dev, "failed to create platform device '%s'\n",
clones[i]);
}
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 23cec57c02b..b48d80c367f 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -1,8 +1,9 @@
/**
* omap-usb-host.c - The USBHS core driver for OMAP EHCI & OHCI
*
- * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2011-2013 Texas Instruments Incorporated - http://www.ti.com
* Author: Keshava Munegowda <keshava_mgowda@ti.com>
+ * Author: Roger Quadros <rogerq@ti.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 of
@@ -23,11 +24,15 @@
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
-#include <linux/spinlock.h>
#include <linux/gpio.h>
-#include <plat/cpu.h>
-#include <plat/usb.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/usb-omap.h>
#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/err.h>
+
+#include "omap-usb.h"
#define USBHS_DRIVER_NAME "usbhs_omap"
#define OMAP_EHCI_DEVICE "ehci-omap"
@@ -89,51 +94,96 @@
struct usbhs_hcd_omap {
+ int nports;
+ struct clk **utmi_clk;
+ struct clk **hsic60m_clk;
+ struct clk **hsic480m_clk;
+
struct clk *xclk60mhsp1_ck;
struct clk *xclk60mhsp2_ck;
- struct clk *utmi_p1_fck;
- struct clk *usbhost_p1_fck;
- struct clk *utmi_p2_fck;
- struct clk *usbhost_p2_fck;
+ struct clk *utmi_p1_gfclk;
+ struct clk *utmi_p2_gfclk;
struct clk *init_60m_fclk;
struct clk *ehci_logic_fck;
void __iomem *uhh_base;
- struct usbhs_omap_platform_data platdata;
+ struct usbhs_omap_platform_data *pdata;
u32 usbhs_rev;
- spinlock_t lock;
};
/*-------------------------------------------------------------------------*/
-const char usbhs_driver_name[] = USBHS_DRIVER_NAME;
+static const char usbhs_driver_name[] = USBHS_DRIVER_NAME;
static u64 usbhs_dmamask = DMA_BIT_MASK(32);
/*-------------------------------------------------------------------------*/
static inline void usbhs_write(void __iomem *base, u32 reg, u32 val)
{
- __raw_writel(val, base + reg);
+ writel_relaxed(val, base + reg);
}
static inline u32 usbhs_read(void __iomem *base, u32 reg)
{
- return __raw_readl(base + reg);
+ return readl_relaxed(base + reg);
}
static inline void usbhs_writeb(void __iomem *base, u8 reg, u8 val)
{
- __raw_writeb(val, base + reg);
+ writeb_relaxed(val, base + reg);
}
static inline u8 usbhs_readb(void __iomem *base, u8 reg)
{
- return __raw_readb(base + reg);
+ return readb_relaxed(base + reg);
}
/*-------------------------------------------------------------------------*/
+/**
+ * Map 'enum usbhs_omap_port_mode' found in <linux/platform_data/usb-omap.h>
+ * to the device tree binding portN-mode found in
+ * 'Documentation/devicetree/bindings/mfd/omap-usb-host.txt'
+ */
+static const char * const port_modes[] = {
+ [OMAP_USBHS_PORT_MODE_UNUSED] = "",
+ [OMAP_EHCI_PORT_MODE_PHY] = "ehci-phy",
+ [OMAP_EHCI_PORT_MODE_TLL] = "ehci-tll",
+ [OMAP_EHCI_PORT_MODE_HSIC] = "ehci-hsic",
+ [OMAP_OHCI_PORT_MODE_PHY_6PIN_DATSE0] = "ohci-phy-6pin-datse0",
+ [OMAP_OHCI_PORT_MODE_PHY_6PIN_DPDM] = "ohci-phy-6pin-dpdm",
+ [OMAP_OHCI_PORT_MODE_PHY_3PIN_DATSE0] = "ohci-phy-3pin-datse0",
+ [OMAP_OHCI_PORT_MODE_PHY_4PIN_DPDM] = "ohci-phy-4pin-dpdm",
+ [OMAP_OHCI_PORT_MODE_TLL_6PIN_DATSE0] = "ohci-tll-6pin-datse0",
+ [OMAP_OHCI_PORT_MODE_TLL_6PIN_DPDM] = "ohci-tll-6pin-dpdm",
+ [OMAP_OHCI_PORT_MODE_TLL_3PIN_DATSE0] = "ohci-tll-3pin-datse0",
+ [OMAP_OHCI_PORT_MODE_TLL_4PIN_DPDM] = "ohci-tll-4pin-dpdm",
+ [OMAP_OHCI_PORT_MODE_TLL_2PIN_DATSE0] = "ohci-tll-2pin-datse0",
+ [OMAP_OHCI_PORT_MODE_TLL_2PIN_DPDM] = "ohci-tll-2pin-dpdm",
+};
+
+/**
+ * omap_usbhs_get_dt_port_mode - Get the 'enum usbhs_omap_port_mode'
+ * from the port mode string.
+ * @mode: The port mode string, usually obtained from device tree.
+ *
+ * The function returns the 'enum usbhs_omap_port_mode' that matches the
+ * provided port mode string as per the port_modes table.
+ * If no match is found it returns -ENODEV
+ */
+static const int omap_usbhs_get_dt_port_mode(const char *mode)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(port_modes); i++) {
+ if (!strcmp(mode, port_modes[i]))
+ return i;
+ }
+
+ return -ENODEV;
+}
+
static struct platform_device *omap_usbhs_alloc_child(const char *name,
struct resource *res, int num_resources, void *pdata,
size_t pdata_size, struct device *dev)
@@ -182,19 +232,13 @@ err_end:
static int omap_usbhs_alloc_children(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct usbhs_hcd_omap *omap;
- struct ehci_hcd_omap_platform_data *ehci_data;
- struct ohci_hcd_omap_platform_data *ohci_data;
+ struct usbhs_omap_platform_data *pdata = dev_get_platdata(dev);
struct platform_device *ehci;
struct platform_device *ohci;
struct resource *res;
struct resource resources[2];
int ret;
- omap = platform_get_drvdata(pdev);
- ehci_data = omap->platdata.ehci_data;
- ohci_data = omap->platdata.ohci_data;
-
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ehci");
if (!res) {
dev_err(dev, "EHCI get resource IORESOURCE_MEM failed\n");
@@ -211,8 +255,8 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev)
}
resources[1] = *res;
- ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, ehci_data,
- sizeof(*ehci_data), dev);
+ ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, pdata,
+ sizeof(*pdata), dev);
if (!ehci) {
dev_err(dev, "omap_usbhs_alloc_child failed\n");
@@ -236,8 +280,8 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev)
}
resources[1] = *res;
- ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, ohci_data,
- sizeof(*ohci_data), dev);
+ ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, pdata,
+ sizeof(*pdata), dev);
if (!ohci) {
dev_err(dev, "omap_usbhs_alloc_child failed\n");
ret = -ENOMEM;
@@ -276,93 +320,168 @@ static bool is_ohci_port(enum usbhs_omap_port_mode pmode)
static int usbhs_runtime_resume(struct device *dev)
{
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
- struct usbhs_omap_platform_data *pdata = &omap->platdata;
- unsigned long flags;
+ struct usbhs_omap_platform_data *pdata = omap->pdata;
+ int i, r;
dev_dbg(dev, "usbhs_runtime_resume\n");
- if (!pdata) {
- dev_dbg(dev, "missing platform_data\n");
- return -ENODEV;
+ omap_tll_enable(pdata);
+
+ if (!IS_ERR(omap->ehci_logic_fck))
+ clk_prepare_enable(omap->ehci_logic_fck);
+
+ for (i = 0; i < omap->nports; i++) {
+ switch (pdata->port_mode[i]) {
+ case OMAP_EHCI_PORT_MODE_HSIC:
+ if (!IS_ERR(omap->hsic60m_clk[i])) {
+ r = clk_prepare_enable(omap->hsic60m_clk[i]);
+ if (r) {
+ dev_err(dev,
+ "Can't enable port %d hsic60m clk:%d\n",
+ i, r);
+ }
+ }
+
+ if (!IS_ERR(omap->hsic480m_clk[i])) {
+ r = clk_prepare_enable(omap->hsic480m_clk[i]);
+ if (r) {
+ dev_err(dev,
+ "Can't enable port %d hsic480m clk:%d\n",
+ i, r);
+ }
+ }
+ /* Fall through as HSIC mode needs utmi_clk */
+
+ case OMAP_EHCI_PORT_MODE_TLL:
+ if (!IS_ERR(omap->utmi_clk[i])) {
+ r = clk_prepare_enable(omap->utmi_clk[i]);
+ if (r) {
+ dev_err(dev,
+ "Can't enable port %d clk : %d\n",
+ i, r);
+ }
+ }
+ break;
+ default:
+ break;
+ }
}
- omap_tll_enable();
- spin_lock_irqsave(&omap->lock, flags);
+ return 0;
+}
- if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck))
- clk_enable(omap->ehci_logic_fck);
+static int usbhs_runtime_suspend(struct device *dev)
+{
+ struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
+ struct usbhs_omap_platform_data *pdata = omap->pdata;
+ int i;
- if (is_ehci_tll_mode(pdata->port_mode[0]))
- clk_enable(omap->usbhost_p1_fck);
- if (is_ehci_tll_mode(pdata->port_mode[1]))
- clk_enable(omap->usbhost_p2_fck);
+ dev_dbg(dev, "usbhs_runtime_suspend\n");
- clk_enable(omap->utmi_p1_fck);
- clk_enable(omap->utmi_p2_fck);
+ for (i = 0; i < omap->nports; i++) {
+ switch (pdata->port_mode[i]) {
+ case OMAP_EHCI_PORT_MODE_HSIC:
+ if (!IS_ERR(omap->hsic60m_clk[i]))
+ clk_disable_unprepare(omap->hsic60m_clk[i]);
- spin_unlock_irqrestore(&omap->lock, flags);
+ if (!IS_ERR(omap->hsic480m_clk[i]))
+ clk_disable_unprepare(omap->hsic480m_clk[i]);
+ /* Fall through as utmi_clks were used in HSIC mode */
+
+ case OMAP_EHCI_PORT_MODE_TLL:
+ if (!IS_ERR(omap->utmi_clk[i]))
+ clk_disable_unprepare(omap->utmi_clk[i]);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!IS_ERR(omap->ehci_logic_fck))
+ clk_disable_unprepare(omap->ehci_logic_fck);
+
+ omap_tll_disable(pdata);
return 0;
}
-static int usbhs_runtime_suspend(struct device *dev)
+static unsigned omap_usbhs_rev1_hostconfig(struct usbhs_hcd_omap *omap,
+ unsigned reg)
{
- struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
- struct usbhs_omap_platform_data *pdata = &omap->platdata;
- unsigned long flags;
+ struct usbhs_omap_platform_data *pdata = omap->pdata;
+ int i;
- dev_dbg(dev, "usbhs_runtime_suspend\n");
+ for (i = 0; i < omap->nports; i++) {
+ switch (pdata->port_mode[i]) {
+ case OMAP_USBHS_PORT_MODE_UNUSED:
+ reg &= ~(OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS << i);
+ break;
+ case OMAP_EHCI_PORT_MODE_PHY:
+ if (pdata->single_ulpi_bypass)
+ break;
- if (!pdata) {
- dev_dbg(dev, "missing platform_data\n");
- return -ENODEV;
- }
+ if (i == 0)
+ reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
+ else
+ reg &= ~(OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS
+ << (i-1));
+ break;
+ default:
+ if (pdata->single_ulpi_bypass)
+ break;
- spin_lock_irqsave(&omap->lock, flags);
+ if (i == 0)
+ reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
+ else
+ reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS
+ << (i-1);
+ break;
+ }
+ }
- if (is_ehci_tll_mode(pdata->port_mode[0]))
- clk_disable(omap->usbhost_p1_fck);
- if (is_ehci_tll_mode(pdata->port_mode[1]))
- clk_disable(omap->usbhost_p2_fck);
+ if (pdata->single_ulpi_bypass) {
+ /* bypass ULPI only if none of the ports use PHY mode */
+ reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
- clk_disable(omap->utmi_p2_fck);
- clk_disable(omap->utmi_p1_fck);
+ for (i = 0; i < omap->nports; i++) {
+ if (is_ehci_phy_mode(pdata->port_mode[i])) {
+ reg &= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
+ break;
+ }
+ }
+ }
- if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck))
- clk_disable(omap->ehci_logic_fck);
+ return reg;
+}
- spin_unlock_irqrestore(&omap->lock, flags);
- omap_tll_disable();
+static unsigned omap_usbhs_rev2_hostconfig(struct usbhs_hcd_omap *omap,
+ unsigned reg)
+{
+ struct usbhs_omap_platform_data *pdata = omap->pdata;
+ int i;
+
+ for (i = 0; i < omap->nports; i++) {
+ /* Clear port mode fields for PHY mode */
+ reg &= ~(OMAP4_P1_MODE_CLEAR << 2 * i);
+
+ if (is_ehci_tll_mode(pdata->port_mode[i]) ||
+ (is_ohci_port(pdata->port_mode[i])))
+ reg |= OMAP4_P1_MODE_TLL << 2 * i;
+ else if (is_ehci_hsic_mode(pdata->port_mode[i]))
+ reg |= OMAP4_P1_MODE_HSIC << 2 * i;
+ }
- return 0;
+ return reg;
}
static void omap_usbhs_init(struct device *dev)
{
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
- struct usbhs_omap_platform_data *pdata = &omap->platdata;
- unsigned long flags;
unsigned reg;
dev_dbg(dev, "starting TI HSUSB Controller\n");
- if (pdata->ehci_data->phy_reset) {
- if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
- gpio_request_one(pdata->ehci_data->reset_gpio_port[0],
- GPIOF_OUT_INIT_LOW, "USB1 PHY reset");
-
- if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
- gpio_request_one(pdata->ehci_data->reset_gpio_port[1],
- GPIOF_OUT_INIT_LOW, "USB2 PHY reset");
-
- /* Hold the PHY in RESET for enough time till DIR is high */
- udelay(10);
- }
-
pm_runtime_get_sync(dev);
- spin_lock_irqsave(&omap->lock, flags);
- omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION);
- dev_dbg(dev, "OMAP UHH_REVISION 0x%x\n", omap->usbhs_rev);
reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG);
/* setup ULPI bypass and burst configurations */
@@ -372,303 +491,378 @@ static void omap_usbhs_init(struct device *dev)
reg |= OMAP4_UHH_HOSTCONFIG_APP_START_CLK;
reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN;
- if (is_omap_usbhs_rev1(omap)) {
- if (pdata->port_mode[0] == OMAP_USBHS_PORT_MODE_UNUSED)
- reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS;
- if (pdata->port_mode[1] == OMAP_USBHS_PORT_MODE_UNUSED)
- reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS;
- if (pdata->port_mode[2] == OMAP_USBHS_PORT_MODE_UNUSED)
- reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS;
-
- /* Bypass the TLL module for PHY mode operation */
- if (cpu_is_omap3430() && (omap_rev() <= OMAP3430_REV_ES2_1)) {
- dev_dbg(dev, "OMAP3 ES version <= ES2.1\n");
- if (is_ehci_phy_mode(pdata->port_mode[0]) ||
- is_ehci_phy_mode(pdata->port_mode[1]) ||
- is_ehci_phy_mode(pdata->port_mode[2]))
- reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
- else
- reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
- } else {
- dev_dbg(dev, "OMAP3 ES version > ES2.1\n");
- if (is_ehci_phy_mode(pdata->port_mode[0]))
- reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
- else
- reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
- if (is_ehci_phy_mode(pdata->port_mode[1]))
- reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
- else
- reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
- if (is_ehci_phy_mode(pdata->port_mode[2]))
- reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
- else
- reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
- }
- } else if (is_omap_usbhs_rev2(omap)) {
- /* Clear port mode fields for PHY mode*/
- reg &= ~OMAP4_P1_MODE_CLEAR;
- reg &= ~OMAP4_P2_MODE_CLEAR;
+ switch (omap->usbhs_rev) {
+ case OMAP_USBHS_REV1:
+ reg = omap_usbhs_rev1_hostconfig(omap, reg);
+ break;
- if (is_ehci_tll_mode(pdata->port_mode[0]) ||
- (is_ohci_port(pdata->port_mode[0])))
- reg |= OMAP4_P1_MODE_TLL;
- else if (is_ehci_hsic_mode(pdata->port_mode[0]))
- reg |= OMAP4_P1_MODE_HSIC;
+ case OMAP_USBHS_REV2:
+ reg = omap_usbhs_rev2_hostconfig(omap, reg);
+ break;
- if (is_ehci_tll_mode(pdata->port_mode[1]) ||
- (is_ohci_port(pdata->port_mode[1])))
- reg |= OMAP4_P2_MODE_TLL;
- else if (is_ehci_hsic_mode(pdata->port_mode[1]))
- reg |= OMAP4_P2_MODE_HSIC;
+ default: /* newer revisions */
+ reg = omap_usbhs_rev2_hostconfig(omap, reg);
+ break;
}
usbhs_write(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg);
dev_dbg(dev, "UHH setup done, uhh_hostconfig=%x\n", reg);
- spin_unlock_irqrestore(&omap->lock, flags);
-
pm_runtime_put_sync(dev);
- if (pdata->ehci_data->phy_reset) {
- /* Hold the PHY in RESET for enough time till
- * PHY is settled and ready
- */
- udelay(10);
+}
- if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
- gpio_set_value_cansleep
- (pdata->ehci_data->reset_gpio_port[0], 1);
+static int usbhs_omap_get_dt_pdata(struct device *dev,
+ struct usbhs_omap_platform_data *pdata)
+{
+ int ret, i;
+ struct device_node *node = dev->of_node;
- if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
- gpio_set_value_cansleep
- (pdata->ehci_data->reset_gpio_port[1], 1);
+ ret = of_property_read_u32(node, "num-ports", &pdata->nports);
+ if (ret)
+ pdata->nports = 0;
+
+ if (pdata->nports > OMAP3_HS_USB_PORTS) {
+ dev_warn(dev, "Too many num_ports <%d> in device tree. Max %d\n",
+ pdata->nports, OMAP3_HS_USB_PORTS);
+ return -ENODEV;
}
-}
-static void omap_usbhs_deinit(struct device *dev)
-{
- struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
- struct usbhs_omap_platform_data *pdata = &omap->platdata;
+ /* get port modes */
+ for (i = 0; i < OMAP3_HS_USB_PORTS; i++) {
+ char prop[11];
+ const char *mode;
+
+ pdata->port_mode[i] = OMAP_USBHS_PORT_MODE_UNUSED;
- if (pdata->ehci_data->phy_reset) {
- if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
- gpio_free(pdata->ehci_data->reset_gpio_port[0]);
+ snprintf(prop, sizeof(prop), "port%d-mode", i + 1);
+ ret = of_property_read_string(node, prop, &mode);
+ if (ret < 0)
+ continue;
+
+ ret = omap_usbhs_get_dt_port_mode(mode);
+ if (ret < 0) {
+ dev_warn(dev, "Invalid port%d-mode \"%s\" in device tree\n",
+ i, mode);
+ return -ENODEV;
+ }
- if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
- gpio_free(pdata->ehci_data->reset_gpio_port[1]);
+ dev_dbg(dev, "port%d-mode: %s -> %d\n", i, mode, ret);
+ pdata->port_mode[i] = ret;
}
+
+ /* get flags */
+ pdata->single_ulpi_bypass = of_property_read_bool(node,
+ "single-ulpi-bypass");
+
+ return 0;
}
+static const struct of_device_id usbhs_child_match_table[] = {
+ { .compatible = "ti,omap-ehci", },
+ { .compatible = "ti,omap-ohci", },
+ { }
+};
/**
* usbhs_omap_probe - initialize TI-based HCDs
*
* Allocates basic resources for this USB host controller.
*/
-static int __devinit usbhs_omap_probe(struct platform_device *pdev)
+static int usbhs_omap_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct usbhs_omap_platform_data *pdata = dev->platform_data;
+ struct usbhs_omap_platform_data *pdata = dev_get_platdata(dev);
struct usbhs_hcd_omap *omap;
struct resource *res;
int ret = 0;
int i;
+ bool need_logic_fck;
+
+ if (dev->of_node) {
+ /* For DT boot we populate platform data from OF node */
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ ret = usbhs_omap_get_dt_pdata(dev, pdata);
+ if (ret)
+ return ret;
+
+ dev->platform_data = pdata;
+ }
if (!pdata) {
dev_err(dev, "Missing platform data\n");
- ret = -ENOMEM;
- goto end_probe;
+ return -ENODEV;
+ }
+
+ if (pdata->nports > OMAP3_HS_USB_PORTS) {
+ dev_info(dev, "Too many num_ports <%d> in platform_data. Max %d\n",
+ pdata->nports, OMAP3_HS_USB_PORTS);
+ return -ENODEV;
}
- omap = kzalloc(sizeof(*omap), GFP_KERNEL);
+ omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL);
if (!omap) {
dev_err(dev, "Memory allocation failed\n");
- ret = -ENOMEM;
- goto end_probe;
+ return -ENOMEM;
}
- spin_lock_init(&omap->lock);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ omap->uhh_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(omap->uhh_base))
+ return PTR_ERR(omap->uhh_base);
- for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
- omap->platdata.port_mode[i] = pdata->port_mode[i];
+ omap->pdata = pdata;
- omap->platdata.ehci_data = pdata->ehci_data;
- omap->platdata.ohci_data = pdata->ohci_data;
+ /* Initialize the TLL subsystem */
+ omap_tll_init(pdata);
pm_runtime_enable(dev);
+ platform_set_drvdata(pdev, omap);
+ pm_runtime_get_sync(dev);
+
+ omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION);
+
+ /* we need to call runtime suspend before we update omap->nports
+ * to prevent unbalanced clk_disable()
+ */
+ pm_runtime_put_sync(dev);
- for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
- if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) ||
- is_ehci_hsic_mode(i)) {
- omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck");
+ /*
+ * If platform data contains nports then use that
+ * else make out number of ports from USBHS revision
+ */
+ if (pdata->nports) {
+ omap->nports = pdata->nports;
+ } else {
+ switch (omap->usbhs_rev) {
+ case OMAP_USBHS_REV1:
+ omap->nports = 3;
+ break;
+ case OMAP_USBHS_REV2:
+ omap->nports = 2;
+ break;
+ default:
+ omap->nports = OMAP3_HS_USB_PORTS;
+ dev_dbg(dev,
+ "USB HOST Rev:0x%d not recognized, assuming %d ports\n",
+ omap->usbhs_rev, omap->nports);
+ break;
+ }
+ pdata->nports = omap->nports;
+ }
+
+ i = sizeof(struct clk *) * omap->nports;
+ omap->utmi_clk = devm_kzalloc(dev, i, GFP_KERNEL);
+ omap->hsic480m_clk = devm_kzalloc(dev, i, GFP_KERNEL);
+ omap->hsic60m_clk = devm_kzalloc(dev, i, GFP_KERNEL);
+
+ if (!omap->utmi_clk || !omap->hsic480m_clk || !omap->hsic60m_clk) {
+ dev_err(dev, "Memory allocation failed\n");
+ ret = -ENOMEM;
+ goto err_mem;
+ }
+
+ /* Set all clocks as invalid to begin with */
+ omap->ehci_logic_fck = ERR_PTR(-ENODEV);
+ omap->init_60m_fclk = ERR_PTR(-ENODEV);
+ omap->utmi_p1_gfclk = ERR_PTR(-ENODEV);
+ omap->utmi_p2_gfclk = ERR_PTR(-ENODEV);
+ omap->xclk60mhsp1_ck = ERR_PTR(-ENODEV);
+ omap->xclk60mhsp2_ck = ERR_PTR(-ENODEV);
+
+ for (i = 0; i < omap->nports; i++) {
+ omap->utmi_clk[i] = ERR_PTR(-ENODEV);
+ omap->hsic480m_clk[i] = ERR_PTR(-ENODEV);
+ omap->hsic60m_clk[i] = ERR_PTR(-ENODEV);
+ }
+
+ /* for OMAP3 i.e. USBHS REV1 */
+ if (omap->usbhs_rev == OMAP_USBHS_REV1) {
+ need_logic_fck = false;
+ for (i = 0; i < omap->nports; i++) {
+ if (is_ehci_phy_mode(pdata->port_mode[i]) ||
+ is_ehci_tll_mode(pdata->port_mode[i]) ||
+ is_ehci_hsic_mode(pdata->port_mode[i]))
+
+ need_logic_fck |= true;
+ }
+
+ if (need_logic_fck) {
+ omap->ehci_logic_fck = devm_clk_get(dev,
+ "usbhost_120m_fck");
if (IS_ERR(omap->ehci_logic_fck)) {
ret = PTR_ERR(omap->ehci_logic_fck);
- dev_warn(dev, "ehci_logic_fck failed:%d\n",
- ret);
+ dev_err(dev, "usbhost_120m_fck failed:%d\n",
+ ret);
+ goto err_mem;
}
- break;
}
-
- omap->utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk");
- if (IS_ERR(omap->utmi_p1_fck)) {
- ret = PTR_ERR(omap->utmi_p1_fck);
- dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
- goto err_end;
+ goto initialize;
}
- omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck");
- if (IS_ERR(omap->xclk60mhsp1_ck)) {
- ret = PTR_ERR(omap->xclk60mhsp1_ck);
- dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret);
- goto err_utmi_p1_fck;
+ /* for OMAP4+ i.e. USBHS REV2+ */
+ omap->utmi_p1_gfclk = devm_clk_get(dev, "utmi_p1_gfclk");
+ if (IS_ERR(omap->utmi_p1_gfclk)) {
+ ret = PTR_ERR(omap->utmi_p1_gfclk);
+ dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
+ goto err_mem;
}
- omap->utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk");
- if (IS_ERR(omap->utmi_p2_fck)) {
- ret = PTR_ERR(omap->utmi_p2_fck);
+ omap->utmi_p2_gfclk = devm_clk_get(dev, "utmi_p2_gfclk");
+ if (IS_ERR(omap->utmi_p2_gfclk)) {
+ ret = PTR_ERR(omap->utmi_p2_gfclk);
dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
- goto err_xclk60mhsp1_ck;
+ goto err_mem;
+ }
+
+ omap->xclk60mhsp1_ck = devm_clk_get(dev, "refclk_60m_ext_p1");
+ if (IS_ERR(omap->xclk60mhsp1_ck)) {
+ ret = PTR_ERR(omap->xclk60mhsp1_ck);
+ dev_err(dev, "refclk_60m_ext_p1 failed error:%d\n", ret);
+ goto err_mem;
}
- omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck");
+ omap->xclk60mhsp2_ck = devm_clk_get(dev, "refclk_60m_ext_p2");
if (IS_ERR(omap->xclk60mhsp2_ck)) {
ret = PTR_ERR(omap->xclk60mhsp2_ck);
- dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret);
- goto err_utmi_p2_fck;
+ dev_err(dev, "refclk_60m_ext_p2 failed error:%d\n", ret);
+ goto err_mem;
}
- omap->usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk");
- if (IS_ERR(omap->usbhost_p1_fck)) {
- ret = PTR_ERR(omap->usbhost_p1_fck);
- dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret);
- goto err_xclk60mhsp2_ck;
+ omap->init_60m_fclk = devm_clk_get(dev, "refclk_60m_int");
+ if (IS_ERR(omap->init_60m_fclk)) {
+ ret = PTR_ERR(omap->init_60m_fclk);
+ dev_err(dev, "refclk_60m_int failed error:%d\n", ret);
+ goto err_mem;
}
- omap->usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk");
- if (IS_ERR(omap->usbhost_p2_fck)) {
- ret = PTR_ERR(omap->usbhost_p2_fck);
- dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret);
- goto err_usbhost_p1_fck;
- }
+ for (i = 0; i < omap->nports; i++) {
+ char clkname[30];
- omap->init_60m_fclk = clk_get(dev, "init_60m_fclk");
- if (IS_ERR(omap->init_60m_fclk)) {
- ret = PTR_ERR(omap->init_60m_fclk);
- dev_err(dev, "init_60m_fclk failed error:%d\n", ret);
- goto err_usbhost_p2_fck;
+ /* clock names are indexed from 1*/
+ snprintf(clkname, sizeof(clkname),
+ "usb_host_hs_utmi_p%d_clk", i + 1);
+
+ /* If a clock is not found we won't bail out as not all
+ * platforms have all clocks and we can function without
+ * them
+ */
+ omap->utmi_clk[i] = devm_clk_get(dev, clkname);
+ if (IS_ERR(omap->utmi_clk[i])) {
+ ret = PTR_ERR(omap->utmi_clk[i]);
+ dev_err(dev, "Failed to get clock : %s : %d\n",
+ clkname, ret);
+ goto err_mem;
+ }
+
+ snprintf(clkname, sizeof(clkname),
+ "usb_host_hs_hsic480m_p%d_clk", i + 1);
+ omap->hsic480m_clk[i] = devm_clk_get(dev, clkname);
+ if (IS_ERR(omap->hsic480m_clk[i])) {
+ ret = PTR_ERR(omap->hsic480m_clk[i]);
+ dev_err(dev, "Failed to get clock : %s : %d\n",
+ clkname, ret);
+ goto err_mem;
+ }
+
+ snprintf(clkname, sizeof(clkname),
+ "usb_host_hs_hsic60m_p%d_clk", i + 1);
+ omap->hsic60m_clk[i] = devm_clk_get(dev, clkname);
+ if (IS_ERR(omap->hsic60m_clk[i])) {
+ ret = PTR_ERR(omap->hsic60m_clk[i]);
+ dev_err(dev, "Failed to get clock : %s : %d\n",
+ clkname, ret);
+ goto err_mem;
+ }
}
if (is_ehci_phy_mode(pdata->port_mode[0])) {
- /* for OMAP3 , the clk set paretn fails */
- ret = clk_set_parent(omap->utmi_p1_fck,
+ ret = clk_set_parent(omap->utmi_p1_gfclk,
omap->xclk60mhsp1_ck);
- if (ret != 0)
- dev_err(dev, "xclk60mhsp1_ck set parent"
- "failed error:%d\n", ret);
+ if (ret != 0) {
+ dev_err(dev, "xclk60mhsp1_ck set parent failed: %d\n",
+ ret);
+ goto err_mem;
+ }
} else if (is_ehci_tll_mode(pdata->port_mode[0])) {
- ret = clk_set_parent(omap->utmi_p1_fck,
+ ret = clk_set_parent(omap->utmi_p1_gfclk,
omap->init_60m_fclk);
- if (ret != 0)
- dev_err(dev, "init_60m_fclk set parent"
- "failed error:%d\n", ret);
+ if (ret != 0) {
+ dev_err(dev, "P0 init_60m_fclk set parent failed: %d\n",
+ ret);
+ goto err_mem;
+ }
}
if (is_ehci_phy_mode(pdata->port_mode[1])) {
- ret = clk_set_parent(omap->utmi_p2_fck,
+ ret = clk_set_parent(omap->utmi_p2_gfclk,
omap->xclk60mhsp2_ck);
- if (ret != 0)
- dev_err(dev, "xclk60mhsp2_ck set parent"
- "failed error:%d\n", ret);
+ if (ret != 0) {
+ dev_err(dev, "xclk60mhsp2_ck set parent failed: %d\n",
+ ret);
+ goto err_mem;
+ }
} else if (is_ehci_tll_mode(pdata->port_mode[1])) {
- ret = clk_set_parent(omap->utmi_p2_fck,
+ ret = clk_set_parent(omap->utmi_p2_gfclk,
omap->init_60m_fclk);
- if (ret != 0)
- dev_err(dev, "init_60m_fclk set parent"
- "failed error:%d\n", ret);
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh");
- if (!res) {
- dev_err(dev, "UHH EHCI get resource failed\n");
- ret = -ENODEV;
- goto err_init_60m_fclk;
- }
-
- omap->uhh_base = ioremap(res->start, resource_size(res));
- if (!omap->uhh_base) {
- dev_err(dev, "UHH ioremap failed\n");
- ret = -ENOMEM;
- goto err_init_60m_fclk;
+ if (ret != 0) {
+ dev_err(dev, "P1 init_60m_fclk set parent failed: %d\n",
+ ret);
+ goto err_mem;
+ }
}
- platform_set_drvdata(pdev, omap);
-
+initialize:
omap_usbhs_init(dev);
- ret = omap_usbhs_alloc_children(pdev);
- if (ret) {
- dev_err(dev, "omap_usbhs_alloc_children failed\n");
- goto err_alloc;
- }
-
- goto end_probe;
-
-err_alloc:
- omap_usbhs_deinit(&pdev->dev);
- iounmap(omap->uhh_base);
-
-err_init_60m_fclk:
- clk_put(omap->init_60m_fclk);
-err_usbhost_p2_fck:
- clk_put(omap->usbhost_p2_fck);
+ if (dev->of_node) {
+ ret = of_platform_populate(dev->of_node,
+ usbhs_child_match_table, NULL, dev);
-err_usbhost_p1_fck:
- clk_put(omap->usbhost_p1_fck);
-
-err_xclk60mhsp2_ck:
- clk_put(omap->xclk60mhsp2_ck);
-
-err_utmi_p2_fck:
- clk_put(omap->utmi_p2_fck);
+ if (ret) {
+ dev_err(dev, "Failed to create DT children: %d\n", ret);
+ goto err_mem;
+ }
-err_xclk60mhsp1_ck:
- clk_put(omap->xclk60mhsp1_ck);
+ } else {
+ ret = omap_usbhs_alloc_children(pdev);
+ if (ret) {
+ dev_err(dev, "omap_usbhs_alloc_children failed: %d\n",
+ ret);
+ goto err_mem;
+ }
+ }
-err_utmi_p1_fck:
- clk_put(omap->utmi_p1_fck);
+ return 0;
-err_end:
- clk_put(omap->ehci_logic_fck);
+err_mem:
pm_runtime_disable(dev);
- kfree(omap);
-end_probe:
return ret;
}
+static int usbhs_omap_remove_child(struct device *dev, void *data)
+{
+ dev_info(dev, "unregistering\n");
+ platform_device_unregister(to_platform_device(dev));
+ return 0;
+}
+
/**
* usbhs_omap_remove - shutdown processing for UHH & TLL HCDs
* @pdev: USB Host Controller being removed
*
* Reverses the effect of usbhs_omap_probe().
*/
-static int __devexit usbhs_omap_remove(struct platform_device *pdev)
+static int usbhs_omap_remove(struct platform_device *pdev)
{
- struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev);
-
- omap_usbhs_deinit(&pdev->dev);
- iounmap(omap->uhh_base);
- clk_put(omap->init_60m_fclk);
- clk_put(omap->usbhost_p2_fck);
- clk_put(omap->usbhost_p1_fck);
- clk_put(omap->xclk60mhsp2_ck);
- clk_put(omap->utmi_p2_fck);
- clk_put(omap->xclk60mhsp1_ck);
- clk_put(omap->utmi_p1_fck);
- clk_put(omap->ehci_logic_fck);
pm_runtime_disable(&pdev->dev);
- kfree(omap);
+ /* remove children */
+ device_for_each_child(&pdev->dev, NULL, usbhs_omap_remove_child);
return 0;
}
@@ -677,16 +871,26 @@ static const struct dev_pm_ops usbhsomap_dev_pm_ops = {
.runtime_resume = usbhs_runtime_resume,
};
+static const struct of_device_id usbhs_omap_dt_ids[] = {
+ { .compatible = "ti,usbhs-host" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, usbhs_omap_dt_ids);
+
+
static struct platform_driver usbhs_omap_driver = {
.driver = {
.name = (char *)usbhs_driver_name,
.owner = THIS_MODULE,
.pm = &usbhsomap_dev_pm_ops,
+ .of_match_table = usbhs_omap_dt_ids,
},
- .remove = __exit_p(usbhs_omap_remove),
+ .remove = usbhs_omap_remove,
};
MODULE_AUTHOR("Keshava Munegowda <keshava_mgowda@ti.com>");
+MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");
MODULE_ALIAS("platform:" USBHS_DRIVER_NAME);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("usb host common core driver for omap EHCI and OHCI");
diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index 4b7757b8430..532eacab6b4 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -1,8 +1,9 @@
/**
* omap-usb-tll.c - The USB TLL driver for OMAP EHCI & OHCI
*
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2012-2013 Texas Instruments Incorporated - http://www.ti.com
* Author: Keshava Munegowda <keshava_mgowda@ti.com>
+ * Author: Roger Quadros <rogerq@ti.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 of
@@ -25,8 +26,9 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/err.h>
-#include <plat/usb.h>
#include <linux/pm_runtime.h>
+#include <linux/platform_data/usb-omap.h>
+#include <linux/of.h>
#define USBTLL_DRIVER_NAME "usbhs_tll"
@@ -54,10 +56,13 @@
#define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num)
#define OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT 24
+#define OMAP_TLL_CHANNEL_CONF_DRVVBUS (1 << 16)
+#define OMAP_TLL_CHANNEL_CONF_CHRGVBUS (1 << 15)
#define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11)
#define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10)
#define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9)
#define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8)
+#define OMAP_TLL_CHANNEL_CONF_MODE_TRANSPARENT_UTMI (2 << 1)
#define OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS (1 << 1)
#define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0)
@@ -92,42 +97,46 @@
#define OMAP_USBTLL_REV1 0x00000015 /* OMAP3 */
#define OMAP_USBTLL_REV2 0x00000018 /* OMAP 3630 */
#define OMAP_USBTLL_REV3 0x00000004 /* OMAP4 */
+#define OMAP_USBTLL_REV4 0x00000006 /* OMAP5 */
#define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL)
+/* only PHY and UNUSED modes don't need TLL */
+#define omap_usb_mode_needs_tll(x) ((x) != OMAP_USBHS_PORT_MODE_UNUSED &&\
+ (x) != OMAP_EHCI_PORT_MODE_PHY)
+
struct usbtll_omap {
- struct clk *usbtll_p1_fck;
- struct clk *usbtll_p2_fck;
- struct usbtll_omap_platform_data platdata;
- /* secure the register updates */
- spinlock_t lock;
+ int nch; /* num. of channels */
+ struct clk **ch_clk;
+ void __iomem *base;
};
/*-------------------------------------------------------------------------*/
-const char usbtll_driver_name[] = USBTLL_DRIVER_NAME;
-struct platform_device *tll_pdev;
+static const char usbtll_driver_name[] = USBTLL_DRIVER_NAME;
+static struct device *tll_dev;
+static DEFINE_SPINLOCK(tll_lock); /* serialize access to tll_dev */
/*-------------------------------------------------------------------------*/
static inline void usbtll_write(void __iomem *base, u32 reg, u32 val)
{
- __raw_writel(val, base + reg);
+ writel_relaxed(val, base + reg);
}
static inline u32 usbtll_read(void __iomem *base, u32 reg)
{
- return __raw_readl(base + reg);
+ return readl_relaxed(base + reg);
}
static inline void usbtll_writeb(void __iomem *base, u8 reg, u8 val)
{
- __raw_writeb(val, base + reg);
+ writeb_relaxed(val, base + reg);
}
static inline u8 usbtll_readb(void __iomem *base, u8 reg)
{
- return __raw_readb(base + reg);
+ return readb_relaxed(base + reg);
}
/*-------------------------------------------------------------------------*/
@@ -200,87 +209,143 @@ static unsigned ohci_omap3_fslsmode(enum usbhs_omap_port_mode mode)
*
* Allocates basic resources for this USB host controller.
*/
-static int __devinit usbtll_omap_probe(struct platform_device *pdev)
+static int usbtll_omap_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct usbtll_omap_platform_data *pdata = dev->platform_data;
- void __iomem *base;
struct resource *res;
struct usbtll_omap *tll;
- unsigned reg;
- unsigned long flags;
int ret = 0;
- int i, ver, count;
+ int i, ver;
dev_dbg(dev, "starting TI HSUSB TLL Controller\n");
- tll = kzalloc(sizeof(struct usbtll_omap), GFP_KERNEL);
+ tll = devm_kzalloc(dev, sizeof(struct usbtll_omap), GFP_KERNEL);
if (!tll) {
dev_err(dev, "Memory allocation failed\n");
- ret = -ENOMEM;
- goto end;
- }
-
- spin_lock_init(&tll->lock);
-
- for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
- tll->platdata.port_mode[i] = pdata->port_mode[i];
-
- tll->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk");
- if (IS_ERR(tll->usbtll_p1_fck)) {
- ret = PTR_ERR(tll->usbtll_p1_fck);
- dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret);
- goto err_tll;
- }
-
- tll->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk");
- if (IS_ERR(tll->usbtll_p2_fck)) {
- ret = PTR_ERR(tll->usbtll_p2_fck);
- dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret);
- goto err_usbtll_p1_fck;
+ return -ENOMEM;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "usb tll get resource failed\n");
- ret = -ENODEV;
- goto err_usbtll_p2_fck;
- }
-
- base = ioremap(res->start, resource_size(res));
- if (!base) {
- dev_err(dev, "TLL ioremap failed\n");
- ret = -ENOMEM;
- goto err_usbtll_p2_fck;
- }
+ tll->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(tll->base))
+ return PTR_ERR(tll->base);
platform_set_drvdata(pdev, tll);
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
- spin_lock_irqsave(&tll->lock, flags);
-
- ver = usbtll_read(base, OMAP_USBTLL_REVISION);
+ ver = usbtll_read(tll->base, OMAP_USBTLL_REVISION);
switch (ver) {
case OMAP_USBTLL_REV1:
- case OMAP_USBTLL_REV2:
- count = OMAP_TLL_CHANNEL_COUNT;
+ case OMAP_USBTLL_REV4:
+ tll->nch = OMAP_TLL_CHANNEL_COUNT;
break;
+ case OMAP_USBTLL_REV2:
case OMAP_USBTLL_REV3:
- count = OMAP_REV2_TLL_CHANNEL_COUNT;
+ tll->nch = OMAP_REV2_TLL_CHANNEL_COUNT;
break;
default:
- dev_err(dev, "TLL version failed\n");
- ret = -ENODEV;
- goto err_ioremap;
+ tll->nch = OMAP_TLL_CHANNEL_COUNT;
+ dev_dbg(dev,
+ "USB TLL Rev : 0x%x not recognized, assuming %d channels\n",
+ ver, tll->nch);
+ break;
+ }
+
+ tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk *) * tll->nch,
+ GFP_KERNEL);
+ if (!tll->ch_clk) {
+ ret = -ENOMEM;
+ dev_err(dev, "Couldn't allocate memory for channel clocks\n");
+ goto err_clk_alloc;
}
- if (is_ehci_tll_mode(pdata->port_mode[0]) ||
- is_ehci_tll_mode(pdata->port_mode[1]) ||
- is_ehci_tll_mode(pdata->port_mode[2]) ||
- is_ohci_port(pdata->port_mode[0]) ||
- is_ohci_port(pdata->port_mode[1]) ||
- is_ohci_port(pdata->port_mode[2])) {
+ for (i = 0; i < tll->nch; i++) {
+ char clkname[] = "usb_tll_hs_usb_chx_clk";
+
+ snprintf(clkname, sizeof(clkname),
+ "usb_tll_hs_usb_ch%d_clk", i);
+ tll->ch_clk[i] = clk_get(dev, clkname);
+
+ if (IS_ERR(tll->ch_clk[i]))
+ dev_dbg(dev, "can't get clock : %s\n", clkname);
+ }
+
+ pm_runtime_put_sync(dev);
+ /* only after this can omap_tll_enable/disable work */
+ spin_lock(&tll_lock);
+ tll_dev = dev;
+ spin_unlock(&tll_lock);
+
+ return 0;
+
+err_clk_alloc:
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+
+ return ret;
+}
+
+/**
+ * usbtll_omap_remove - shutdown processing for UHH & TLL HCDs
+ * @pdev: USB Host Controller being removed
+ *
+ * Reverses the effect of usbtll_omap_probe().
+ */
+static int usbtll_omap_remove(struct platform_device *pdev)
+{
+ struct usbtll_omap *tll = platform_get_drvdata(pdev);
+ int i;
+
+ spin_lock(&tll_lock);
+ tll_dev = NULL;
+ spin_unlock(&tll_lock);
+
+ for (i = 0; i < tll->nch; i++)
+ if (!IS_ERR(tll->ch_clk[i]))
+ clk_put(tll->ch_clk[i]);
+
+ pm_runtime_disable(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id usbtll_omap_dt_ids[] = {
+ { .compatible = "ti,usbhs-tll" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, usbtll_omap_dt_ids);
+
+static struct platform_driver usbtll_omap_driver = {
+ .driver = {
+ .name = (char *)usbtll_driver_name,
+ .owner = THIS_MODULE,
+ .of_match_table = usbtll_omap_dt_ids,
+ },
+ .probe = usbtll_omap_probe,
+ .remove = usbtll_omap_remove,
+};
+
+int omap_tll_init(struct usbhs_omap_platform_data *pdata)
+{
+ int i;
+ bool needs_tll;
+ unsigned reg;
+ struct usbtll_omap *tll;
+
+ if (!tll_dev)
+ return -ENODEV;
+
+ pm_runtime_get_sync(tll_dev);
+
+ spin_lock(&tll_lock);
+ tll = dev_get_drvdata(tll_dev);
+ needs_tll = false;
+ for (i = 0; i < tll->nch; i++)
+ needs_tll |= omap_usb_mode_needs_tll(pdata->port_mode[i]);
+
+ if (needs_tll) {
+ void __iomem *base = tll->base;
/* Program Common TLL register */
reg = usbtll_read(base, OMAP_TLL_SHARED_CONF);
@@ -292,7 +357,7 @@ static int __devinit usbtll_omap_probe(struct platform_device *pdev)
usbtll_write(base, OMAP_TLL_SHARED_CONF, reg);
/* Enable channels now */
- for (i = 0; i < count; i++) {
+ for (i = 0; i < tll->nch; i++) {
reg = usbtll_read(base, OMAP_TLL_CHANNEL_CONF(i));
if (is_ohci_port(pdata->port_mode[i])) {
@@ -308,6 +373,15 @@ static int __devinit usbtll_omap_probe(struct platform_device *pdev)
reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE
| OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF
| OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE);
+ } else if (pdata->port_mode[i] ==
+ OMAP_EHCI_PORT_MODE_HSIC) {
+ /*
+ * HSIC Mode requires UTMI port configurations
+ */
+ reg |= OMAP_TLL_CHANNEL_CONF_DRVVBUS
+ | OMAP_TLL_CHANNEL_CONF_CHRGVBUS
+ | OMAP_TLL_CHANNEL_CONF_MODE_TRANSPARENT_UTMI
+ | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF;
} else {
continue;
}
@@ -320,134 +394,74 @@ static int __devinit usbtll_omap_probe(struct platform_device *pdev)
}
}
-err_ioremap:
- spin_unlock_irqrestore(&tll->lock, flags);
- iounmap(base);
- pm_runtime_put_sync(dev);
- tll_pdev = pdev;
- if (!ret)
- goto end;
- pm_runtime_disable(dev);
-
-err_usbtll_p2_fck:
- clk_put(tll->usbtll_p2_fck);
-
-err_usbtll_p1_fck:
- clk_put(tll->usbtll_p1_fck);
-
-err_tll:
- kfree(tll);
+ spin_unlock(&tll_lock);
+ pm_runtime_put_sync(tll_dev);
-end:
- return ret;
-}
-
-/**
- * usbtll_omap_remove - shutdown processing for UHH & TLL HCDs
- * @pdev: USB Host Controller being removed
- *
- * Reverses the effect of usbtll_omap_probe().
- */
-static int __devexit usbtll_omap_remove(struct platform_device *pdev)
-{
- struct usbtll_omap *tll = platform_get_drvdata(pdev);
-
- clk_put(tll->usbtll_p2_fck);
- clk_put(tll->usbtll_p1_fck);
- pm_runtime_disable(&pdev->dev);
- kfree(tll);
return 0;
}
+EXPORT_SYMBOL_GPL(omap_tll_init);
-static int usbtll_runtime_resume(struct device *dev)
+int omap_tll_enable(struct usbhs_omap_platform_data *pdata)
{
- struct usbtll_omap *tll = dev_get_drvdata(dev);
- struct usbtll_omap_platform_data *pdata = &tll->platdata;
- unsigned long flags;
+ int i;
+ struct usbtll_omap *tll;
- dev_dbg(dev, "usbtll_runtime_resume\n");
+ if (!tll_dev)
+ return -ENODEV;
- if (!pdata) {
- dev_dbg(dev, "missing platform_data\n");
- return -ENODEV;
- }
+ pm_runtime_get_sync(tll_dev);
- spin_lock_irqsave(&tll->lock, flags);
+ spin_lock(&tll_lock);
+ tll = dev_get_drvdata(tll_dev);
- if (is_ehci_tll_mode(pdata->port_mode[0]))
- clk_enable(tll->usbtll_p1_fck);
+ for (i = 0; i < tll->nch; i++) {
+ if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {
+ int r;
- if (is_ehci_tll_mode(pdata->port_mode[1]))
- clk_enable(tll->usbtll_p2_fck);
+ if (IS_ERR(tll->ch_clk[i]))
+ continue;
- spin_unlock_irqrestore(&tll->lock, flags);
+ r = clk_prepare_enable(tll->ch_clk[i]);
+ if (r) {
+ dev_err(tll_dev,
+ "Error enabling ch %d clock: %d\n", i, r);
+ }
+ }
+ }
+
+ spin_unlock(&tll_lock);
return 0;
}
+EXPORT_SYMBOL_GPL(omap_tll_enable);
-static int usbtll_runtime_suspend(struct device *dev)
+int omap_tll_disable(struct usbhs_omap_platform_data *pdata)
{
- struct usbtll_omap *tll = dev_get_drvdata(dev);
- struct usbtll_omap_platform_data *pdata = &tll->platdata;
- unsigned long flags;
-
- dev_dbg(dev, "usbtll_runtime_suspend\n");
-
- if (!pdata) {
- dev_dbg(dev, "missing platform_data\n");
- return -ENODEV;
- }
+ int i;
+ struct usbtll_omap *tll;
- spin_lock_irqsave(&tll->lock, flags);
+ if (!tll_dev)
+ return -ENODEV;
- if (is_ehci_tll_mode(pdata->port_mode[0]))
- clk_disable(tll->usbtll_p1_fck);
+ spin_lock(&tll_lock);
+ tll = dev_get_drvdata(tll_dev);
- if (is_ehci_tll_mode(pdata->port_mode[1]))
- clk_disable(tll->usbtll_p2_fck);
+ for (i = 0; i < tll->nch; i++) {
+ if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {
+ if (!IS_ERR(tll->ch_clk[i]))
+ clk_disable_unprepare(tll->ch_clk[i]);
+ }
+ }
- spin_unlock_irqrestore(&tll->lock, flags);
+ spin_unlock(&tll_lock);
+ pm_runtime_put_sync(tll_dev);
return 0;
}
-
-static const struct dev_pm_ops usbtllomap_dev_pm_ops = {
- SET_RUNTIME_PM_OPS(usbtll_runtime_suspend,
- usbtll_runtime_resume,
- NULL)
-};
-
-static struct platform_driver usbtll_omap_driver = {
- .driver = {
- .name = (char *)usbtll_driver_name,
- .owner = THIS_MODULE,
- .pm = &usbtllomap_dev_pm_ops,
- },
- .probe = usbtll_omap_probe,
- .remove = __devexit_p(usbtll_omap_remove),
-};
-
-int omap_tll_enable(void)
-{
- if (!tll_pdev) {
- pr_err("missing omap usbhs tll platform_data\n");
- return -ENODEV;
- }
- return pm_runtime_get_sync(&tll_pdev->dev);
-}
-EXPORT_SYMBOL_GPL(omap_tll_enable);
-
-int omap_tll_disable(void)
-{
- if (!tll_pdev) {
- pr_err("missing omap usbhs tll platform_data\n");
- return -ENODEV;
- }
- return pm_runtime_put_sync(&tll_pdev->dev);
-}
EXPORT_SYMBOL_GPL(omap_tll_disable);
MODULE_AUTHOR("Keshava Munegowda <keshava_mgowda@ti.com>");
+MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");
MODULE_ALIAS("platform:" USBHS_DRIVER_NAME);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("usb tll driver for TI OMAP EHCI and OHCI controllers");
diff --git a/drivers/mfd/omap-usb.h b/drivers/mfd/omap-usb.h
new file mode 100644
index 00000000000..2a508b6aeac
--- /dev/null
+++ b/drivers/mfd/omap-usb.h
@@ -0,0 +1,3 @@
+extern int omap_tll_init(struct usbhs_omap_platform_data *pdata);
+extern int omap_tll_enable(struct usbhs_omap_platform_data *pdata);
+extern int omap_tll_disable(struct usbhs_omap_platform_data *pdata);
diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c
index 4f8d6e6b19a..d280d789e55 100644
--- a/drivers/mfd/palmas.c
+++ b/drivers/mfd/palmas.c
@@ -23,67 +23,52 @@
#include <linux/err.h>
#include <linux/mfd/core.h>
#include <linux/mfd/palmas.h>
-#include <linux/of_platform.h>
-
-enum palmas_ids {
- PALMAS_PMIC_ID,
- PALMAS_GPIO_ID,
- PALMAS_LEDS_ID,
- PALMAS_WDT_ID,
- PALMAS_RTC_ID,
- PALMAS_PWRBUTTON_ID,
- PALMAS_GPADC_ID,
- PALMAS_RESOURCE_ID,
- PALMAS_CLK_ID,
- PALMAS_PWM_ID,
- PALMAS_USB_ID,
+#include <linux/of_device.h>
+
+#define PALMAS_EXT_REQ (PALMAS_EXT_CONTROL_ENABLE1 | \
+ PALMAS_EXT_CONTROL_ENABLE2 | \
+ PALMAS_EXT_CONTROL_NSLEEP)
+
+struct palmas_sleep_requestor_info {
+ int id;
+ int reg_offset;
+ int bit_pos;
};
-static const struct mfd_cell palmas_children[] = {
- {
- .name = "palmas-pmic",
- .id = PALMAS_PMIC_ID,
- },
- {
- .name = "palmas-gpio",
- .id = PALMAS_GPIO_ID,
- },
- {
- .name = "palmas-leds",
- .id = PALMAS_LEDS_ID,
- },
- {
- .name = "palmas-wdt",
- .id = PALMAS_WDT_ID,
- },
- {
- .name = "palmas-rtc",
- .id = PALMAS_RTC_ID,
- },
- {
- .name = "palmas-pwrbutton",
- .id = PALMAS_PWRBUTTON_ID,
- },
- {
- .name = "palmas-gpadc",
- .id = PALMAS_GPADC_ID,
- },
- {
- .name = "palmas-resource",
- .id = PALMAS_RESOURCE_ID,
- },
- {
- .name = "palmas-clk",
- .id = PALMAS_CLK_ID,
- },
- {
- .name = "palmas-pwm",
- .id = PALMAS_PWM_ID,
- },
- {
- .name = "palmas-usb",
- .id = PALMAS_USB_ID,
+#define EXTERNAL_REQUESTOR(_id, _offset, _pos) \
+ [PALMAS_EXTERNAL_REQSTR_ID_##_id] = { \
+ .id = PALMAS_EXTERNAL_REQSTR_ID_##_id, \
+ .reg_offset = _offset, \
+ .bit_pos = _pos, \
}
+
+static struct palmas_sleep_requestor_info sleep_req_info[] = {
+ EXTERNAL_REQUESTOR(REGEN1, 0, 0),
+ EXTERNAL_REQUESTOR(REGEN2, 0, 1),
+ EXTERNAL_REQUESTOR(SYSEN1, 0, 2),
+ EXTERNAL_REQUESTOR(SYSEN2, 0, 3),
+ EXTERNAL_REQUESTOR(CLK32KG, 0, 4),
+ EXTERNAL_REQUESTOR(CLK32KGAUDIO, 0, 5),
+ EXTERNAL_REQUESTOR(REGEN3, 0, 6),
+ EXTERNAL_REQUESTOR(SMPS12, 1, 0),
+ EXTERNAL_REQUESTOR(SMPS3, 1, 1),
+ EXTERNAL_REQUESTOR(SMPS45, 1, 2),
+ EXTERNAL_REQUESTOR(SMPS6, 1, 3),
+ EXTERNAL_REQUESTOR(SMPS7, 1, 4),
+ EXTERNAL_REQUESTOR(SMPS8, 1, 5),
+ EXTERNAL_REQUESTOR(SMPS9, 1, 6),
+ EXTERNAL_REQUESTOR(SMPS10, 1, 7),
+ EXTERNAL_REQUESTOR(LDO1, 2, 0),
+ EXTERNAL_REQUESTOR(LDO2, 2, 1),
+ EXTERNAL_REQUESTOR(LDO3, 2, 2),
+ EXTERNAL_REQUESTOR(LDO4, 2, 3),
+ EXTERNAL_REQUESTOR(LDO5, 2, 4),
+ EXTERNAL_REQUESTOR(LDO6, 2, 5),
+ EXTERNAL_REQUESTOR(LDO7, 2, 6),
+ EXTERNAL_REQUESTOR(LDO8, 2, 7),
+ EXTERNAL_REQUESTOR(LDO9, 3, 0),
+ EXTERNAL_REQUESTOR(LDOLN, 3, 1),
+ EXTERNAL_REQUESTOR(LDOUSB, 3, 2),
};
static const struct regmap_config palmas_regmap_config[PALMAS_NUM_CLIENTS] = {
@@ -247,44 +232,154 @@ static struct regmap_irq_chip palmas_irq_chip = {
PALMAS_INT1_MASK),
};
-static void __devinit palmas_dt_to_pdata(struct device_node *node,
+int palmas_ext_control_req_config(struct palmas *palmas,
+ enum palmas_external_requestor_id id, int ext_ctrl, bool enable)
+{
+ int preq_mask_bit = 0;
+ int reg_add = 0;
+ int bit_pos;
+ int ret;
+
+ if (!(ext_ctrl & PALMAS_EXT_REQ))
+ return 0;
+
+ if (id >= PALMAS_EXTERNAL_REQSTR_ID_MAX)
+ return 0;
+
+ if (ext_ctrl & PALMAS_EXT_CONTROL_NSLEEP) {
+ reg_add = PALMAS_NSLEEP_RES_ASSIGN;
+ preq_mask_bit = 0;
+ } else if (ext_ctrl & PALMAS_EXT_CONTROL_ENABLE1) {
+ reg_add = PALMAS_ENABLE1_RES_ASSIGN;
+ preq_mask_bit = 1;
+ } else if (ext_ctrl & PALMAS_EXT_CONTROL_ENABLE2) {
+ reg_add = PALMAS_ENABLE2_RES_ASSIGN;
+ preq_mask_bit = 2;
+ }
+
+ bit_pos = sleep_req_info[id].bit_pos;
+ reg_add += sleep_req_info[id].reg_offset;
+ if (enable)
+ ret = palmas_update_bits(palmas, PALMAS_RESOURCE_BASE,
+ reg_add, BIT(bit_pos), BIT(bit_pos));
+ else
+ ret = palmas_update_bits(palmas, PALMAS_RESOURCE_BASE,
+ reg_add, BIT(bit_pos), 0);
+ if (ret < 0) {
+ dev_err(palmas->dev, "Resource reg 0x%02x update failed %d\n",
+ reg_add, ret);
+ return ret;
+ }
+
+ /* Unmask the PREQ */
+ ret = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE,
+ PALMAS_POWER_CTRL, BIT(preq_mask_bit), 0);
+ if (ret < 0) {
+ dev_err(palmas->dev, "POWER_CTRL register update failed %d\n",
+ ret);
+ return ret;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(palmas_ext_control_req_config);
+
+static int palmas_set_pdata_irq_flag(struct i2c_client *i2c,
struct palmas_platform_data *pdata)
{
+ struct irq_data *irq_data = irq_get_irq_data(i2c->irq);
+ if (!irq_data) {
+ dev_err(&i2c->dev, "Invalid IRQ: %d\n", i2c->irq);
+ return -EINVAL;
+ }
+
+ pdata->irq_flags = irqd_get_trigger_type(irq_data);
+ dev_info(&i2c->dev, "Irq flag is 0x%08x\n", pdata->irq_flags);
+ return 0;
+}
+
+static void palmas_dt_to_pdata(struct i2c_client *i2c,
+ struct palmas_platform_data *pdata)
+{
+ struct device_node *node = i2c->dev.of_node;
int ret;
u32 prop;
- ret = of_property_read_u32(node, "ti,mux_pad1", &prop);
+ ret = of_property_read_u32(node, "ti,mux-pad1", &prop);
if (!ret) {
pdata->mux_from_pdata = 1;
pdata->pad1 = prop;
}
- ret = of_property_read_u32(node, "ti,mux_pad2", &prop);
+ ret = of_property_read_u32(node, "ti,mux-pad2", &prop);
if (!ret) {
pdata->mux_from_pdata = 1;
pdata->pad2 = prop;
}
/* The default for this register is all masked */
- ret = of_property_read_u32(node, "ti,power_ctrl", &prop);
+ ret = of_property_read_u32(node, "ti,power-ctrl", &prop);
if (!ret)
pdata->power_ctrl = prop;
else
pdata->power_ctrl = PALMAS_POWER_CTRL_NSLEEP_MASK |
PALMAS_POWER_CTRL_ENABLE1_MASK |
PALMAS_POWER_CTRL_ENABLE2_MASK;
+ if (i2c->irq)
+ palmas_set_pdata_irq_flag(i2c, pdata);
+
+ pdata->pm_off = of_property_read_bool(node,
+ "ti,system-power-controller");
+}
+
+static struct palmas *palmas_dev;
+static void palmas_power_off(void)
+{
+ unsigned int addr;
+ int ret, slave;
+
+ if (!palmas_dev)
+ return;
+
+ slave = PALMAS_BASE_TO_SLAVE(PALMAS_PMU_CONTROL_BASE);
+ addr = PALMAS_BASE_TO_REG(PALMAS_PMU_CONTROL_BASE, PALMAS_DEV_CTRL);
+
+ ret = regmap_update_bits(
+ palmas_dev->regmap[slave],
+ addr,
+ PALMAS_DEV_CTRL_DEV_ON,
+ 0);
+
+ if (ret)
+ pr_err("%s: Unable to write to DEV_CTRL_DEV_ON: %d\n",
+ __func__, ret);
}
-static int __devinit palmas_i2c_probe(struct i2c_client *i2c,
+static unsigned int palmas_features = PALMAS_PMIC_FEATURE_SMPS10_BOOST;
+static unsigned int tps659038_features;
+
+static const struct of_device_id of_palmas_match_tbl[] = {
+ {
+ .compatible = "ti,palmas",
+ .data = &palmas_features,
+ },
+ {
+ .compatible = "ti,tps659038",
+ .data = &tps659038_features,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
+
+static int palmas_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct palmas *palmas;
struct palmas_platform_data *pdata;
struct device_node *node = i2c->dev.of_node;
int ret = 0, i;
- unsigned int reg, addr;
+ unsigned int reg, addr, *features;
int slave;
- struct mfd_cell *children;
+ const struct of_device_id *match;
pdata = dev_get_platdata(&i2c->dev);
@@ -294,7 +389,7 @@ static int __devinit palmas_i2c_probe(struct i2c_client *i2c,
if (!pdata)
return -ENOMEM;
- palmas_dt_to_pdata(node, pdata);
+ palmas_dt_to_pdata(i2c, pdata);
}
if (!pdata)
@@ -306,9 +401,16 @@ static int __devinit palmas_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, palmas);
palmas->dev = &i2c->dev;
- palmas->id = id->driver_data;
palmas->irq = i2c->irq;
+ match = of_match_device(of_palmas_match_tbl, &i2c->dev);
+
+ if (!match)
+ return -ENODATA;
+
+ features = (unsigned int *)match->data;
+ palmas->features = *features;
+
for (i = 0; i < PALMAS_NUM_CLIENTS; i++) {
if (i == 0)
palmas->i2c_clients[i] = i2c;
@@ -320,8 +422,9 @@ static int __devinit palmas_i2c_probe(struct i2c_client *i2c,
dev_err(palmas->dev,
"can't attach client %d\n", i);
ret = -ENOMEM;
- goto err;
+ goto err_i2c;
}
+ palmas->i2c_clients[i]->dev.of_node = of_node_get(node);
}
palmas->regmap[i] = devm_regmap_init_i2c(palmas->i2c_clients[i],
&palmas_regmap_config[i]);
@@ -330,10 +433,28 @@ static int __devinit palmas_i2c_probe(struct i2c_client *i2c,
dev_err(palmas->dev,
"Failed to allocate regmap %d, err: %d\n",
i, ret);
- goto err;
+ goto err_i2c;
}
}
+ if (!palmas->irq) {
+ dev_warn(palmas->dev, "IRQ missing: skipping irq request\n");
+ goto no_irq;
+ }
+
+ /* Change interrupt line output polarity */
+ if (pdata->irq_flags & IRQ_TYPE_LEVEL_HIGH)
+ reg = PALMAS_POLARITY_CTRL_INT_POLARITY;
+ else
+ reg = 0;
+ ret = palmas_update_bits(palmas, PALMAS_PU_PD_OD_BASE,
+ PALMAS_POLARITY_CTRL, PALMAS_POLARITY_CTRL_INT_POLARITY,
+ reg);
+ if (ret < 0) {
+ dev_err(palmas->dev, "POLARITY_CTRL updat failed: %d\n", ret);
+ goto err_i2c;
+ }
+
/* Change IRQ into clear on read mode for efficiency */
slave = PALMAS_BASE_TO_SLAVE(PALMAS_INTERRUPT_BASE);
addr = PALMAS_BASE_TO_REG(PALMAS_INTERRUPT_BASE, PALMAS_INT_CTRL);
@@ -342,11 +463,12 @@ static int __devinit palmas_i2c_probe(struct i2c_client *i2c,
regmap_write(palmas->regmap[slave], addr, reg);
ret = regmap_add_irq_chip(palmas->regmap[slave], palmas->irq,
- IRQF_ONESHOT | IRQF_TRIGGER_LOW, 0, &palmas_irq_chip,
+ IRQF_ONESHOT | pdata->irq_flags, 0, &palmas_irq_chip,
&palmas->irq_data);
if (ret < 0)
- goto err;
+ goto err_i2c;
+no_irq:
slave = PALMAS_BASE_TO_SLAVE(PALMAS_PU_PD_OD_BASE);
addr = PALMAS_BASE_TO_REG(PALMAS_PU_PD_OD_BASE,
PALMAS_PRIMARY_SECONDARY_PAD1);
@@ -425,61 +547,43 @@ static int __devinit palmas_i2c_probe(struct i2c_client *i2c,
*/
if (node) {
ret = of_platform_populate(node, NULL, NULL, &i2c->dev);
- if (ret < 0)
+ if (ret < 0) {
goto err_irq;
- else
- return ret;
- }
-
- children = kmemdup(palmas_children, sizeof(palmas_children),
- GFP_KERNEL);
- if (!children) {
- ret = -ENOMEM;
- goto err_irq;
+ } else if (pdata->pm_off && !pm_power_off) {
+ palmas_dev = palmas;
+ pm_power_off = palmas_power_off;
+ }
}
- children[PALMAS_PMIC_ID].platform_data = pdata->pmic_pdata;
- children[PALMAS_PMIC_ID].pdata_size = sizeof(*pdata->pmic_pdata);
-
- children[PALMAS_GPADC_ID].platform_data = pdata->gpadc_pdata;
- children[PALMAS_GPADC_ID].pdata_size = sizeof(*pdata->gpadc_pdata);
-
- children[PALMAS_RESOURCE_ID].platform_data = pdata->resource_pdata;
- children[PALMAS_RESOURCE_ID].pdata_size =
- sizeof(*pdata->resource_pdata);
-
- children[PALMAS_USB_ID].platform_data = pdata->usb_pdata;
- children[PALMAS_USB_ID].pdata_size = sizeof(*pdata->usb_pdata);
-
- children[PALMAS_CLK_ID].platform_data = pdata->clk_pdata;
- children[PALMAS_CLK_ID].pdata_size = sizeof(*pdata->clk_pdata);
-
- ret = mfd_add_devices(palmas->dev, -1,
- children, ARRAY_SIZE(palmas_children),
- NULL, regmap_irq_chip_get_base(palmas->irq_data),
- NULL);
- kfree(children);
-
- if (ret < 0)
- goto err_devices;
-
return ret;
-err_devices:
- mfd_remove_devices(palmas->dev);
err_irq:
regmap_del_irq_chip(palmas->irq, palmas->irq_data);
-err:
+err_i2c:
+ for (i = 1; i < PALMAS_NUM_CLIENTS; i++) {
+ if (palmas->i2c_clients[i])
+ i2c_unregister_device(palmas->i2c_clients[i]);
+ }
return ret;
}
static int palmas_i2c_remove(struct i2c_client *i2c)
{
struct palmas *palmas = i2c_get_clientdata(i2c);
+ int i;
- mfd_remove_devices(palmas->dev);
regmap_del_irq_chip(palmas->irq, palmas->irq_data);
+ for (i = 1; i < PALMAS_NUM_CLIENTS; i++) {
+ if (palmas->i2c_clients[i])
+ i2c_unregister_device(palmas->i2c_clients[i]);
+ }
+
+ if (palmas == palmas_dev) {
+ pm_power_off = NULL;
+ palmas_dev = NULL;
+ }
+
return 0;
}
@@ -492,11 +596,6 @@ static const struct i2c_device_id palmas_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, palmas_i2c_id);
-static struct of_device_id __devinitdata of_palmas_match_tbl[] = {
- { .compatible = "ti,palmas", },
- { /* end */ }
-};
-
static struct i2c_driver palmas_i2c_driver = {
.driver = {
.name = "palmas",
diff --git a/drivers/mfd/pcf50633-adc.c b/drivers/mfd/pcf50633-adc.c
index 3927c17e417..c1984b0d1b6 100644
--- a/drivers/mfd/pcf50633-adc.c
+++ b/drivers/mfd/pcf50633-adc.c
@@ -19,7 +19,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/completion.h>
@@ -199,11 +198,11 @@ static void pcf50633_adc_irq(int irq, void *data)
kfree(req);
}
-static int __devinit pcf50633_adc_probe(struct platform_device *pdev)
+static int pcf50633_adc_probe(struct platform_device *pdev)
{
struct pcf50633_adc *adc;
- adc = kzalloc(sizeof(*adc), GFP_KERNEL);
+ adc = devm_kzalloc(&pdev->dev, sizeof(*adc), GFP_KERNEL);
if (!adc)
return -ENOMEM;
@@ -218,7 +217,7 @@ static int __devinit pcf50633_adc_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit pcf50633_adc_remove(struct platform_device *pdev)
+static int pcf50633_adc_remove(struct platform_device *pdev)
{
struct pcf50633_adc *adc = platform_get_drvdata(pdev);
int i, head;
@@ -236,7 +235,6 @@ static int __devexit pcf50633_adc_remove(struct platform_device *pdev)
kfree(adc->queue[i]);
mutex_unlock(&adc->queue_mutex);
- kfree(adc);
return 0;
}
@@ -246,7 +244,7 @@ static struct platform_driver pcf50633_adc_driver = {
.name = "pcf50633-adc",
},
.probe = pcf50633_adc_probe,
- .remove = __devexit_p(pcf50633_adc_remove),
+ .remove = pcf50633_adc_remove,
};
module_platform_driver(pcf50633_adc_driver);
diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
index 45ce1fb5a54..41ab5e34d2a 100644
--- a/drivers/mfd/pcf50633-core.c
+++ b/drivers/mfd/pcf50633-core.c
@@ -191,11 +191,11 @@ static struct regmap_config pcf50633_regmap_config = {
.val_bits = 8,
};
-static int __devinit pcf50633_probe(struct i2c_client *client,
+static int pcf50633_probe(struct i2c_client *client,
const struct i2c_device_id *ids)
{
struct pcf50633 *pcf;
- struct pcf50633_platform_data *pdata = client->dev.platform_data;
+ struct pcf50633_platform_data *pdata = dev_get_platdata(&client->dev);
int i, ret;
int version, variant;
@@ -208,6 +208,8 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
if (!pcf)
return -ENOMEM;
+ i2c_set_clientdata(client, pcf);
+ pcf->dev = &client->dev;
pcf->pdata = pdata;
mutex_init(&pcf->lock);
@@ -219,9 +221,6 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
return ret;
}
- i2c_set_clientdata(client, pcf);
- pcf->dev = &client->dev;
-
version = pcf50633_reg_read(pcf, 0);
variant = pcf50633_reg_read(pcf, 1);
if (version < 0 || variant < 0) {
@@ -246,7 +245,7 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
for (i = 0; i < PCF50633_NUM_REGULATORS; i++) {
struct platform_device *pdev;
- pdev = platform_device_alloc("pcf50633-regltr", i);
+ pdev = platform_device_alloc("pcf50633-regulator", i);
if (!pdev) {
dev_err(pcf->dev, "Cannot create regulator %d\n", i);
continue;
@@ -275,7 +274,7 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
return 0;
}
-static int __devexit pcf50633_remove(struct i2c_client *client)
+static int pcf50633_remove(struct i2c_client *client)
{
struct pcf50633 *pcf = i2c_get_clientdata(client);
int i;
@@ -308,7 +307,7 @@ static struct i2c_driver pcf50633_driver = {
},
.id_table = pcf50633_id_table,
.probe = pcf50633_probe,
- .remove = __devexit_p(pcf50633_remove),
+ .remove = pcf50633_remove,
};
static int __init pcf50633_init(void)
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index e873b15753d..95951380354 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -14,183 +14,372 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>
-#include <linux/msm_ssbi.h>
+#include <linux/ssbi.h>
+#include <linux/regmap.h>
+#include <linux/of_platform.h>
#include <linux/mfd/core.h>
-#include <linux/mfd/pm8xxx/pm8921.h>
-#include <linux/mfd/pm8xxx/core.h>
+
+#define SSBI_REG_ADDR_IRQ_BASE 0x1BB
+
+#define SSBI_REG_ADDR_IRQ_ROOT (SSBI_REG_ADDR_IRQ_BASE + 0)
+#define SSBI_REG_ADDR_IRQ_M_STATUS1 (SSBI_REG_ADDR_IRQ_BASE + 1)
+#define SSBI_REG_ADDR_IRQ_M_STATUS2 (SSBI_REG_ADDR_IRQ_BASE + 2)
+#define SSBI_REG_ADDR_IRQ_M_STATUS3 (SSBI_REG_ADDR_IRQ_BASE + 3)
+#define SSBI_REG_ADDR_IRQ_M_STATUS4 (SSBI_REG_ADDR_IRQ_BASE + 4)
+#define SSBI_REG_ADDR_IRQ_BLK_SEL (SSBI_REG_ADDR_IRQ_BASE + 5)
+#define SSBI_REG_ADDR_IRQ_IT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 6)
+#define SSBI_REG_ADDR_IRQ_CONFIG (SSBI_REG_ADDR_IRQ_BASE + 7)
+#define SSBI_REG_ADDR_IRQ_RT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 8)
+
+#define PM_IRQF_LVL_SEL 0x01 /* level select */
+#define PM_IRQF_MASK_FE 0x02 /* mask falling edge */
+#define PM_IRQF_MASK_RE 0x04 /* mask rising edge */
+#define PM_IRQF_CLR 0x08 /* clear interrupt */
+#define PM_IRQF_BITS_MASK 0x70
+#define PM_IRQF_BITS_SHIFT 4
+#define PM_IRQF_WRITE 0x80
+
+#define PM_IRQF_MASK_ALL (PM_IRQF_MASK_FE | \
+ PM_IRQF_MASK_RE)
#define REG_HWREV 0x002 /* PMIC4 revision */
#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
-struct pm8921 {
- struct device *dev;
- struct pm_irq_chip *irq_chip;
+#define PM8921_NR_IRQS 256
+
+struct pm_irq_chip {
+ struct regmap *regmap;
+ spinlock_t pm_irq_lock;
+ struct irq_domain *irqdomain;
+ unsigned int num_irqs;
+ unsigned int num_blocks;
+ unsigned int num_masters;
+ u8 config[0];
};
-static int pm8921_readb(const struct device *dev, u16 addr, u8 *val)
+static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp,
+ unsigned int *ip)
{
- const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
- const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+ int rc;
+
+ spin_lock(&chip->pm_irq_lock);
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+ if (rc) {
+ pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
+ goto bail;
+ }
- return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+ rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
+ if (rc)
+ pr_err("Failed Reading Status rc=%d\n", rc);
+bail:
+ spin_unlock(&chip->pm_irq_lock);
+ return rc;
}
-static int pm8921_writeb(const struct device *dev, u16 addr, u8 val)
+static int
+pm8xxx_config_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int cp)
{
- const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
- const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+ int rc;
+
+ spin_lock(&chip->pm_irq_lock);
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+ if (rc) {
+ pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
+ goto bail;
+ }
- return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+ cp |= PM_IRQF_WRITE;
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_CONFIG, cp);
+ if (rc)
+ pr_err("Failed Configuring IRQ rc=%d\n", rc);
+bail:
+ spin_unlock(&chip->pm_irq_lock);
+ return rc;
}
-static int pm8921_read_buf(const struct device *dev, u16 addr, u8 *buf,
- int cnt)
+static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
{
- const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
- const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+ int pmirq, irq, i, ret = 0;
+ unsigned int bits;
+
+ ret = pm8xxx_read_block_irq(chip, block, &bits);
+ if (ret) {
+ pr_err("Failed reading %d block ret=%d", block, ret);
+ return ret;
+ }
+ if (!bits) {
+ pr_err("block bit set in master but no irqs: %d", block);
+ return 0;
+ }
- return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+ /* Check IRQ bits */
+ for (i = 0; i < 8; i++) {
+ if (bits & (1 << i)) {
+ pmirq = block * 8 + i;
+ irq = irq_find_mapping(chip->irqdomain, pmirq);
+ generic_handle_irq(irq);
+ }
+ }
+ return 0;
}
-static int pm8921_write_buf(const struct device *dev, u16 addr, u8 *buf,
- int cnt)
+static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
{
- const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
- const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+ unsigned int blockbits;
+ int block_number, i, ret = 0;
+
+ ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_M_STATUS1 + master,
+ &blockbits);
+ if (ret) {
+ pr_err("Failed to read master %d ret=%d\n", master, ret);
+ return ret;
+ }
+ if (!blockbits) {
+ pr_err("master bit set in root but no blocks: %d", master);
+ return 0;
+ }
- return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+ for (i = 0; i < 8; i++)
+ if (blockbits & (1 << i)) {
+ block_number = master * 8 + i; /* block # */
+ ret |= pm8xxx_irq_block_handler(chip, block_number);
+ }
+ return ret;
}
-static int pm8921_read_irq_stat(const struct device *dev, int irq)
+static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
{
- const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
- const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+ struct pm_irq_chip *chip = irq_desc_get_handler_data(desc);
+ struct irq_chip *irq_chip = irq_desc_get_chip(desc);
+ unsigned int root;
+ int i, ret, masters = 0;
+
+ chained_irq_enter(irq_chip, desc);
+
+ ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_ROOT, &root);
+ if (ret) {
+ pr_err("Can't read root status ret=%d\n", ret);
+ return;
+ }
+
+ /* on pm8xxx series masters start from bit 1 of the root */
+ masters = root >> 1;
- return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+ /* Read allowed masters for blocks. */
+ for (i = 0; i < chip->num_masters; i++)
+ if (masters & (1 << i))
+ pm8xxx_irq_master_handler(chip, i);
+
+ chained_irq_exit(irq_chip, desc);
}
-static struct pm8xxx_drvdata pm8921_drvdata = {
- .pmic_readb = pm8921_readb,
- .pmic_writeb = pm8921_writeb,
- .pmic_read_buf = pm8921_read_buf,
- .pmic_write_buf = pm8921_write_buf,
- .pmic_read_irq_stat = pm8921_read_irq_stat,
-};
+static void pm8xxx_irq_mask_ack(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ irq_bit = pmirq % 8;
-static int __devinit pm8921_add_subdevices(const struct pm8921_platform_data
- *pdata,
- struct pm8921 *pmic,
- u32 rev)
+ config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
+ pm8xxx_config_irq(chip, block, config);
+}
+
+static void pm8xxx_irq_unmask(struct irq_data *d)
{
- int ret = 0, irq_base = 0;
- struct pm_irq_chip *irq_chip;
-
- if (pdata->irq_pdata) {
- pdata->irq_pdata->irq_cdata.nirqs = PM8921_NR_IRQS;
- pdata->irq_pdata->irq_cdata.rev = rev;
- irq_base = pdata->irq_pdata->irq_base;
- irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
-
- if (IS_ERR(irq_chip)) {
- pr_err("Failed to init interrupts ret=%ld\n",
- PTR_ERR(irq_chip));
- return PTR_ERR(irq_chip);
- }
- pmic->irq_chip = irq_chip;
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ irq_bit = pmirq % 8;
+
+ config = chip->config[pmirq];
+ pm8xxx_config_irq(chip, block, config);
+}
+
+static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ irq_bit = pmirq % 8;
+
+ chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
+ | PM_IRQF_MASK_ALL;
+ if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+ if (flow_type & IRQF_TRIGGER_RISING)
+ chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
+ if (flow_type & IRQF_TRIGGER_FALLING)
+ chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
+ } else {
+ chip->config[pmirq] |= PM_IRQF_LVL_SEL;
+
+ if (flow_type & IRQF_TRIGGER_HIGH)
+ chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
+ else
+ chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
}
- return ret;
+
+ config = chip->config[pmirq] | PM_IRQF_CLR;
+ return pm8xxx_config_irq(chip, block, config);
+}
+
+static struct irq_chip pm8xxx_irq_chip = {
+ .name = "pm8xxx",
+ .irq_mask_ack = pm8xxx_irq_mask_ack,
+ .irq_unmask = pm8xxx_irq_unmask,
+ .irq_set_type = pm8xxx_irq_set_type,
+ .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
+};
+
+static int pm8xxx_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct pm_irq_chip *chip = d->host_data;
+
+ irq_set_chip_and_handler(irq, &pm8xxx_irq_chip, handle_level_irq);
+ irq_set_chip_data(irq, chip);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+ return 0;
}
-static int __devinit pm8921_probe(struct platform_device *pdev)
+static const struct irq_domain_ops pm8xxx_irq_domain_ops = {
+ .xlate = irq_domain_xlate_twocell,
+ .map = pm8xxx_irq_domain_map,
+};
+
+static const struct regmap_config ssbi_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = 0x3ff,
+ .fast_io = true,
+ .reg_read = ssbi_reg_read,
+ .reg_write = ssbi_reg_write
+};
+
+static const struct of_device_id pm8921_id_table[] = {
+ { .compatible = "qcom,pm8058", },
+ { .compatible = "qcom,pm8921", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pm8921_id_table);
+
+static int pm8921_probe(struct platform_device *pdev)
{
- const struct pm8921_platform_data *pdata = pdev->dev.platform_data;
- struct pm8921 *pmic;
- int rc;
- u8 val;
+ struct regmap *regmap;
+ int irq, rc;
+ unsigned int val;
u32 rev;
+ struct pm_irq_chip *chip;
+ unsigned int nirqs = PM8921_NR_IRQS;
- if (!pdata) {
- pr_err("missing platform data\n");
- return -EINVAL;
- }
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
- pmic = kzalloc(sizeof(struct pm8921), GFP_KERNEL);
- if (!pmic) {
- pr_err("Cannot alloc pm8921 struct\n");
- return -ENOMEM;
- }
+ regmap = devm_regmap_init(&pdev->dev, NULL, pdev->dev.parent,
+ &ssbi_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
/* Read PMIC chip revision */
- rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
+ rc = regmap_read(regmap, REG_HWREV, &val);
if (rc) {
pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);
- goto err_read_rev;
+ return rc;
}
pr_info("PMIC revision 1: %02X\n", val);
rev = val;
/* Read PMIC chip revision 2 */
- rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
+ rc = regmap_read(regmap, REG_HWREV_2, &val);
if (rc) {
pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",
REG_HWREV_2, rc);
- goto err_read_rev;
+ return rc;
}
pr_info("PMIC revision 2: %02X\n", val);
rev |= val << BITS_PER_BYTE;
- pmic->dev = &pdev->dev;
- pm8921_drvdata.pm_chip_data = pmic;
- platform_set_drvdata(pdev, &pm8921_drvdata);
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip) +
+ sizeof(chip->config[0]) * nirqs,
+ GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, chip);
+ chip->regmap = regmap;
+ chip->num_irqs = nirqs;
+ chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
+ chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
+ spin_lock_init(&chip->pm_irq_lock);
+
+ chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node, nirqs,
+ &pm8xxx_irq_domain_ops,
+ chip);
+ if (!chip->irqdomain)
+ return -ENODEV;
- rc = pm8921_add_subdevices(pdata, pmic, rev);
+ irq_set_handler_data(irq, chip);
+ irq_set_chained_handler(irq, pm8xxx_irq_handler);
+ irq_set_irq_wake(irq, 1);
+
+ rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
if (rc) {
- pr_err("Cannot add subdevices rc=%d\n", rc);
- goto err;
+ irq_set_chained_handler(irq, NULL);
+ irq_set_handler_data(irq, NULL);
+ irq_domain_remove(chip->irqdomain);
}
- /* gpio might not work if no irq device is found */
- WARN_ON(pmic->irq_chip == NULL);
+ return rc;
+}
+static int pm8921_remove_child(struct device *dev, void *unused)
+{
+ platform_device_unregister(to_platform_device(dev));
return 0;
-
-err:
- mfd_remove_devices(pmic->dev);
- platform_set_drvdata(pdev, NULL);
-err_read_rev:
- kfree(pmic);
- return rc;
}
-static int __devexit pm8921_remove(struct platform_device *pdev)
+static int pm8921_remove(struct platform_device *pdev)
{
- struct pm8xxx_drvdata *drvdata;
- struct pm8921 *pmic = NULL;
-
- drvdata = platform_get_drvdata(pdev);
- if (drvdata)
- pmic = drvdata->pm_chip_data;
- if (pmic)
- mfd_remove_devices(pmic->dev);
- if (pmic->irq_chip) {
- pm8xxx_irq_exit(pmic->irq_chip);
- pmic->irq_chip = NULL;
- }
- platform_set_drvdata(pdev, NULL);
- kfree(pmic);
+ int irq = platform_get_irq(pdev, 0);
+ struct pm_irq_chip *chip = platform_get_drvdata(pdev);
+
+ device_for_each_child(&pdev->dev, NULL, pm8921_remove_child);
+ irq_set_chained_handler(irq, NULL);
+ irq_set_handler_data(irq, NULL);
+ irq_domain_remove(chip->irqdomain);
return 0;
}
static struct platform_driver pm8921_driver = {
.probe = pm8921_probe,
- .remove = __devexit_p(pm8921_remove),
+ .remove = pm8921_remove,
.driver = {
.name = "pm8921-core",
.owner = THIS_MODULE,
+ .of_match_table = pm8921_id_table,
},
};
diff --git a/drivers/mfd/pm8xxx-irq.c b/drivers/mfd/pm8xxx-irq.c
deleted file mode 100644
index d452dd01308..00000000000
--- a/drivers/mfd/pm8xxx-irq.c
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- */
-
-#define pr_fmt(fmt) "%s: " fmt, __func__
-
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/kernel.h>
-#include <linux/mfd/pm8xxx/core.h>
-#include <linux/mfd/pm8xxx/irq.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-/* PMIC8xxx IRQ */
-
-#define SSBI_REG_ADDR_IRQ_BASE 0x1BB
-
-#define SSBI_REG_ADDR_IRQ_ROOT (SSBI_REG_ADDR_IRQ_BASE + 0)
-#define SSBI_REG_ADDR_IRQ_M_STATUS1 (SSBI_REG_ADDR_IRQ_BASE + 1)
-#define SSBI_REG_ADDR_IRQ_M_STATUS2 (SSBI_REG_ADDR_IRQ_BASE + 2)
-#define SSBI_REG_ADDR_IRQ_M_STATUS3 (SSBI_REG_ADDR_IRQ_BASE + 3)
-#define SSBI_REG_ADDR_IRQ_M_STATUS4 (SSBI_REG_ADDR_IRQ_BASE + 4)
-#define SSBI_REG_ADDR_IRQ_BLK_SEL (SSBI_REG_ADDR_IRQ_BASE + 5)
-#define SSBI_REG_ADDR_IRQ_IT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 6)
-#define SSBI_REG_ADDR_IRQ_CONFIG (SSBI_REG_ADDR_IRQ_BASE + 7)
-#define SSBI_REG_ADDR_IRQ_RT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 8)
-
-#define PM_IRQF_LVL_SEL 0x01 /* level select */
-#define PM_IRQF_MASK_FE 0x02 /* mask falling edge */
-#define PM_IRQF_MASK_RE 0x04 /* mask rising edge */
-#define PM_IRQF_CLR 0x08 /* clear interrupt */
-#define PM_IRQF_BITS_MASK 0x70
-#define PM_IRQF_BITS_SHIFT 4
-#define PM_IRQF_WRITE 0x80
-
-#define PM_IRQF_MASK_ALL (PM_IRQF_MASK_FE | \
- PM_IRQF_MASK_RE)
-
-struct pm_irq_chip {
- struct device *dev;
- spinlock_t pm_irq_lock;
- unsigned int devirq;
- unsigned int irq_base;
- unsigned int num_irqs;
- unsigned int num_blocks;
- unsigned int num_masters;
- u8 config[0];
-};
-
-static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp)
-{
- return pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp);
-}
-
-static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp)
-{
- return pm8xxx_readb(chip->dev,
- SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp);
-}
-
-static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip)
-{
- int rc;
-
- spin_lock(&chip->pm_irq_lock);
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
- if (rc) {
- pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
- goto bail;
- }
-
- rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
- if (rc)
- pr_err("Failed Reading Status rc=%d\n", rc);
-bail:
- spin_unlock(&chip->pm_irq_lock);
- return rc;
-}
-
-static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
-{
- int rc;
-
- spin_lock(&chip->pm_irq_lock);
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
- if (rc) {
- pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
- goto bail;
- }
-
- cp |= PM_IRQF_WRITE;
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp);
- if (rc)
- pr_err("Failed Configuring IRQ rc=%d\n", rc);
-bail:
- spin_unlock(&chip->pm_irq_lock);
- return rc;
-}
-
-static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
-{
- int pmirq, irq, i, ret = 0;
- u8 bits;
-
- ret = pm8xxx_read_block_irq(chip, block, &bits);
- if (ret) {
- pr_err("Failed reading %d block ret=%d", block, ret);
- return ret;
- }
- if (!bits) {
- pr_err("block bit set in master but no irqs: %d", block);
- return 0;
- }
-
- /* Check IRQ bits */
- for (i = 0; i < 8; i++) {
- if (bits & (1 << i)) {
- pmirq = block * 8 + i;
- irq = pmirq + chip->irq_base;
- generic_handle_irq(irq);
- }
- }
- return 0;
-}
-
-static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
-{
- u8 blockbits;
- int block_number, i, ret = 0;
-
- ret = pm8xxx_read_master_irq(chip, master, &blockbits);
- if (ret) {
- pr_err("Failed to read master %d ret=%d\n", master, ret);
- return ret;
- }
- if (!blockbits) {
- pr_err("master bit set in root but no blocks: %d", master);
- return 0;
- }
-
- for (i = 0; i < 8; i++)
- if (blockbits & (1 << i)) {
- block_number = master * 8 + i; /* block # */
- ret |= pm8xxx_irq_block_handler(chip, block_number);
- }
- return ret;
-}
-
-static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
-{
- struct pm_irq_chip *chip = irq_desc_get_handler_data(desc);
- struct irq_chip *irq_chip = irq_desc_get_chip(desc);
- u8 root;
- int i, ret, masters = 0;
-
- ret = pm8xxx_read_root_irq(chip, &root);
- if (ret) {
- pr_err("Can't read root status ret=%d\n", ret);
- return;
- }
-
- /* on pm8xxx series masters start from bit 1 of the root */
- masters = root >> 1;
-
- /* Read allowed masters for blocks. */
- for (i = 0; i < chip->num_masters; i++)
- if (masters & (1 << i))
- pm8xxx_irq_master_handler(chip, i);
-
- irq_chip->irq_ack(&desc->irq_data);
-}
-
-static void pm8xxx_irq_mask_ack(struct irq_data *d)
-{
- struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
- u8 block, config;
-
- block = pmirq / 8;
- master = block / 8;
- irq_bit = pmirq % 8;
-
- config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
- pm8xxx_config_irq(chip, block, config);
-}
-
-static void pm8xxx_irq_unmask(struct irq_data *d)
-{
- struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
- u8 block, config;
-
- block = pmirq / 8;
- master = block / 8;
- irq_bit = pmirq % 8;
-
- config = chip->config[pmirq];
- pm8xxx_config_irq(chip, block, config);
-}
-
-static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
-{
- struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
- u8 block, config;
-
- block = pmirq / 8;
- master = block / 8;
- irq_bit = pmirq % 8;
-
- chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
- | PM_IRQF_MASK_ALL;
- if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
- if (flow_type & IRQF_TRIGGER_RISING)
- chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
- if (flow_type & IRQF_TRIGGER_FALLING)
- chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
- } else {
- chip->config[pmirq] |= PM_IRQF_LVL_SEL;
-
- if (flow_type & IRQF_TRIGGER_HIGH)
- chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
- else
- chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
- }
-
- config = chip->config[pmirq] | PM_IRQF_CLR;
- return pm8xxx_config_irq(chip, block, config);
-}
-
-static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on)
-{
- return 0;
-}
-
-static struct irq_chip pm8xxx_irq_chip = {
- .name = "pm8xxx",
- .irq_mask_ack = pm8xxx_irq_mask_ack,
- .irq_unmask = pm8xxx_irq_unmask,
- .irq_set_type = pm8xxx_irq_set_type,
- .irq_set_wake = pm8xxx_irq_set_wake,
- .flags = IRQCHIP_MASK_ON_SUSPEND,
-};
-
-/**
- * pm8xxx_get_irq_stat - get the status of the irq line
- * @chip: pointer to identify a pmic irq controller
- * @irq: the irq number
- *
- * The pm8xxx gpio and mpp rely on the interrupt block to read
- * the values on their pins. This function is to facilitate reading
- * the status of a gpio or an mpp line. The caller has to convert the
- * gpio number to irq number.
- *
- * RETURNS:
- * an int indicating the value read on that line
- */
-int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
-{
- int pmirq, rc;
- u8 block, bits, bit;
- unsigned long flags;
-
- if (chip == NULL || irq < chip->irq_base ||
- irq >= chip->irq_base + chip->num_irqs)
- return -EINVAL;
-
- pmirq = irq - chip->irq_base;
-
- block = pmirq / 8;
- bit = pmirq % 8;
-
- spin_lock_irqsave(&chip->pm_irq_lock, flags);
-
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
- if (rc) {
- pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
- irq, pmirq, block, rc);
- goto bail_out;
- }
-
- rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
- if (rc) {
- pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
- irq, pmirq, block, rc);
- goto bail_out;
- }
-
- rc = (bits & (1 << bit)) ? 1 : 0;
-
-bail_out:
- spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(pm8xxx_get_irq_stat);
-
-struct pm_irq_chip * __devinit pm8xxx_irq_init(struct device *dev,
- const struct pm8xxx_irq_platform_data *pdata)
-{
- struct pm_irq_chip *chip;
- int devirq, rc;
- unsigned int pmirq;
-
- if (!pdata) {
- pr_err("No platform data\n");
- return ERR_PTR(-EINVAL);
- }
-
- devirq = pdata->devirq;
- if (devirq < 0) {
- pr_err("missing devirq\n");
- rc = devirq;
- return ERR_PTR(-EINVAL);
- }
-
- chip = kzalloc(sizeof(struct pm_irq_chip)
- + sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL);
- if (!chip) {
- pr_err("Cannot alloc pm_irq_chip struct\n");
- return ERR_PTR(-EINVAL);
- }
-
- chip->dev = dev;
- chip->devirq = devirq;
- chip->irq_base = pdata->irq_base;
- chip->num_irqs = pdata->irq_cdata.nirqs;
- chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
- chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
- spin_lock_init(&chip->pm_irq_lock);
-
- for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) {
- irq_set_chip_and_handler(chip->irq_base + pmirq,
- &pm8xxx_irq_chip,
- handle_level_irq);
- irq_set_chip_data(chip->irq_base + pmirq, chip);
-#ifdef CONFIG_ARM
- set_irq_flags(chip->irq_base + pmirq, IRQF_VALID);
-#else
- irq_set_noprobe(chip->irq_base + pmirq);
-#endif
- }
-
- irq_set_irq_type(devirq, pdata->irq_trigger_flag);
- irq_set_handler_data(devirq, chip);
- irq_set_chained_handler(devirq, pm8xxx_irq_handler);
- set_irq_wake(devirq, 1);
-
- return chip;
-}
-
-int __devexit pm8xxx_irq_exit(struct pm_irq_chip *chip)
-{
- irq_set_chained_handler(chip->devirq, NULL);
- kfree(chip);
- return 0;
-}
diff --git a/drivers/mfd/rc5t583-irq.c b/drivers/mfd/rc5t583-irq.c
index fe00cdd6f83..bb850202027 100644
--- a/drivers/mfd/rc5t583-irq.c
+++ b/drivers/mfd/rc5t583-irq.c
@@ -22,7 +22,6 @@
*/
#include <linux/interrupt.h>
#include <linux/irq.h>
-#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/mfd/rc5t583.h>
@@ -345,7 +344,7 @@ int rc5t583_irq_init(struct rc5t583 *rc5t583, int irq, int irq_base)
mutex_init(&rc5t583->irq_lock);
/* Initailize all int register to 0 */
- for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++) {
+ for (i = 0; i < RC5T583_MAX_INTERRUPT_EN_REGS; i++) {
ret = rc5t583_write(rc5t583->dev, irq_en_add[i],
rc5t583->irq_en_reg[i]);
if (ret < 0)
diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c
index f1a024ecdb1..df276ad9f40 100644
--- a/drivers/mfd/rc5t583.c
+++ b/drivers/mfd/rc5t583.c
@@ -74,7 +74,7 @@ static struct deepsleep_control_data deepsleep_data[] = {
#define EXT_PWR_REQ \
(RC5T583_EXT_PWRREQ1_CONTROL | RC5T583_EXT_PWRREQ2_CONTROL)
-static struct mfd_cell rc5t583_subdevs[] = {
+static const struct mfd_cell rc5t583_subdevs[] = {
{.name = "rc5t583-gpio",},
{.name = "rc5t583-regulator",},
{.name = "rc5t583-rtc", },
@@ -246,11 +246,11 @@ static const struct regmap_config rc5t583_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c,
+static int rc5t583_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct rc5t583 *rc5t583;
- struct rc5t583_platform_data *pdata = i2c->dev.platform_data;
+ struct rc5t583_platform_data *pdata = dev_get_platdata(&i2c->dev);
int ret;
bool irq_init_success = false;
@@ -303,7 +303,7 @@ err_add_devs:
return ret;
}
-static int __devexit rc5t583_i2c_remove(struct i2c_client *i2c)
+static int rc5t583_i2c_remove(struct i2c_client *i2c)
{
struct rc5t583 *rc5t583 = i2c_get_clientdata(i2c);
@@ -325,7 +325,7 @@ static struct i2c_driver rc5t583_i2c_driver = {
.owner = THIS_MODULE,
},
.probe = rc5t583_i2c_probe,
- .remove = __devexit_p(rc5t583_i2c_remove),
+ .remove = rc5t583_i2c_remove,
.id_table = rc5t583_i2c_id,
};
diff --git a/drivers/mfd/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c
index fbabc3cbe35..6575585f1d1 100644
--- a/drivers/mfd/rdc321x-southbridge.c
+++ b/drivers/mfd/rdc321x-southbridge.c
@@ -19,7 +19,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
@@ -39,7 +38,7 @@ static struct resource rdc321x_wdt_resource[] = {
};
static struct rdc321x_gpio_pdata rdc321x_gpio_pdata = {
- .max_gpios = RDC321X_MAX_GPIO,
+ .max_gpios = RDC321X_NUM_GPIO,
};
static struct resource rdc321x_gpio_resources[] = {
@@ -56,7 +55,7 @@ static struct resource rdc321x_gpio_resources[] = {
}
};
-static struct mfd_cell rdc321x_sb_cells[] = {
+static const struct mfd_cell rdc321x_sb_cells[] = {
{
.name = "rdc321x-wdt",
.resources = rdc321x_wdt_resource,
@@ -72,7 +71,7 @@ static struct mfd_cell rdc321x_sb_cells[] = {
},
};
-static int __devinit rdc321x_sb_probe(struct pci_dev *pdev,
+static int rdc321x_sb_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
int err;
@@ -91,12 +90,12 @@ static int __devinit rdc321x_sb_probe(struct pci_dev *pdev,
NULL, 0, NULL);
}
-static void __devexit rdc321x_sb_remove(struct pci_dev *pdev)
+static void rdc321x_sb_remove(struct pci_dev *pdev)
{
mfd_remove_devices(&pdev->dev);
}
-static DEFINE_PCI_DEVICE_TABLE(rdc321x_sb_table) = {
+static const struct pci_device_id rdc321x_sb_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_RDC, PCI_DEVICE_ID_RDC_R6030) },
{}
};
@@ -106,7 +105,7 @@ static struct pci_driver rdc321x_sb_driver = {
.name = "RDC321x Southbridge",
.id_table = rdc321x_sb_table,
.probe = rdc321x_sb_probe,
- .remove = __devexit_p(rdc321x_sb_remove),
+ .remove = rdc321x_sb_remove,
};
module_pci_driver(rdc321x_sb_driver);
diff --git a/drivers/mfd/retu-mfd.c b/drivers/mfd/retu-mfd.c
new file mode 100644
index 00000000000..663f8a37aa6
--- /dev/null
+++ b/drivers/mfd/retu-mfd.c
@@ -0,0 +1,327 @@
+/*
+ * Retu/Tahvo MFD driver
+ *
+ * Copyright (C) 2004, 2005 Nokia Corporation
+ *
+ * Based on code written by Juha Yrjölä, David Weinehall and Mikko Ylinen.
+ * Rewritten by Aaro Koskinen.
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/retu.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+
+/* Registers */
+#define RETU_REG_ASICR 0x00 /* ASIC ID and revision */
+#define RETU_REG_ASICR_VILMA (1 << 7) /* Bit indicating Vilma */
+#define RETU_REG_IDR 0x01 /* Interrupt ID */
+#define RETU_REG_IMR 0x02 /* Interrupt mask (Retu) */
+#define TAHVO_REG_IMR 0x03 /* Interrupt mask (Tahvo) */
+
+/* Interrupt sources */
+#define RETU_INT_PWR 0 /* Power button */
+
+struct retu_dev {
+ struct regmap *regmap;
+ struct device *dev;
+ struct mutex mutex;
+ struct regmap_irq_chip_data *irq_data;
+};
+
+static struct resource retu_pwrbutton_res[] = {
+ {
+ .name = "retu-pwrbutton",
+ .start = RETU_INT_PWR,
+ .end = RETU_INT_PWR,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static const struct mfd_cell retu_devs[] = {
+ {
+ .name = "retu-wdt"
+ },
+ {
+ .name = "retu-pwrbutton",
+ .resources = retu_pwrbutton_res,
+ .num_resources = ARRAY_SIZE(retu_pwrbutton_res),
+ }
+};
+
+static struct regmap_irq retu_irqs[] = {
+ [RETU_INT_PWR] = {
+ .mask = 1 << RETU_INT_PWR,
+ }
+};
+
+static struct regmap_irq_chip retu_irq_chip = {
+ .name = "RETU",
+ .irqs = retu_irqs,
+ .num_irqs = ARRAY_SIZE(retu_irqs),
+ .num_regs = 1,
+ .status_base = RETU_REG_IDR,
+ .mask_base = RETU_REG_IMR,
+ .ack_base = RETU_REG_IDR,
+};
+
+/* Retu device registered for the power off. */
+static struct retu_dev *retu_pm_power_off;
+
+static struct resource tahvo_usb_res[] = {
+ {
+ .name = "tahvo-usb",
+ .start = TAHVO_INT_VBUS,
+ .end = TAHVO_INT_VBUS,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static const struct mfd_cell tahvo_devs[] = {
+ {
+ .name = "tahvo-usb",
+ .resources = tahvo_usb_res,
+ .num_resources = ARRAY_SIZE(tahvo_usb_res),
+ },
+};
+
+static struct regmap_irq tahvo_irqs[] = {
+ [TAHVO_INT_VBUS] = {
+ .mask = 1 << TAHVO_INT_VBUS,
+ }
+};
+
+static struct regmap_irq_chip tahvo_irq_chip = {
+ .name = "TAHVO",
+ .irqs = tahvo_irqs,
+ .num_irqs = ARRAY_SIZE(tahvo_irqs),
+ .num_regs = 1,
+ .status_base = RETU_REG_IDR,
+ .mask_base = TAHVO_REG_IMR,
+ .ack_base = RETU_REG_IDR,
+};
+
+static const struct retu_data {
+ char *chip_name;
+ char *companion_name;
+ struct regmap_irq_chip *irq_chip;
+ const struct mfd_cell *children;
+ int nchildren;
+} retu_data[] = {
+ [0] = {
+ .chip_name = "Retu",
+ .companion_name = "Vilma",
+ .irq_chip = &retu_irq_chip,
+ .children = retu_devs,
+ .nchildren = ARRAY_SIZE(retu_devs),
+ },
+ [1] = {
+ .chip_name = "Tahvo",
+ .companion_name = "Betty",
+ .irq_chip = &tahvo_irq_chip,
+ .children = tahvo_devs,
+ .nchildren = ARRAY_SIZE(tahvo_devs),
+ }
+};
+
+int retu_read(struct retu_dev *rdev, u8 reg)
+{
+ int ret;
+ int value;
+
+ mutex_lock(&rdev->mutex);
+ ret = regmap_read(rdev->regmap, reg, &value);
+ mutex_unlock(&rdev->mutex);
+
+ return ret ? ret : value;
+}
+EXPORT_SYMBOL_GPL(retu_read);
+
+int retu_write(struct retu_dev *rdev, u8 reg, u16 data)
+{
+ int ret;
+
+ mutex_lock(&rdev->mutex);
+ ret = regmap_write(rdev->regmap, reg, data);
+ mutex_unlock(&rdev->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(retu_write);
+
+static void retu_power_off(void)
+{
+ struct retu_dev *rdev = retu_pm_power_off;
+ int reg;
+
+ mutex_lock(&retu_pm_power_off->mutex);
+
+ /* Ignore power button state */
+ regmap_read(rdev->regmap, RETU_REG_CC1, &reg);
+ regmap_write(rdev->regmap, RETU_REG_CC1, reg | 2);
+
+ /* Expire watchdog immediately */
+ regmap_write(rdev->regmap, RETU_REG_WATCHDOG, 0);
+
+ /* Wait for poweroff */
+ for (;;)
+ cpu_relax();
+
+ mutex_unlock(&retu_pm_power_off->mutex);
+}
+
+static int retu_regmap_read(void *context, const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ int ret;
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+
+ BUG_ON(reg_size != 1 || val_size != 2);
+
+ ret = i2c_smbus_read_word_data(i2c, *(u8 const *)reg);
+ if (ret < 0)
+ return ret;
+
+ *(u16 *)val = ret;
+ return 0;
+}
+
+static int retu_regmap_write(void *context, const void *data, size_t count)
+{
+ u8 reg;
+ u16 val;
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+
+ BUG_ON(count != sizeof(reg) + sizeof(val));
+ memcpy(&reg, data, sizeof(reg));
+ memcpy(&val, data + sizeof(reg), sizeof(val));
+ return i2c_smbus_write_word_data(i2c, reg, val);
+}
+
+static struct regmap_bus retu_bus = {
+ .read = retu_regmap_read,
+ .write = retu_regmap_write,
+ .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
+};
+
+static struct regmap_config retu_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+};
+
+static int retu_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+ struct retu_data const *rdat;
+ struct retu_dev *rdev;
+ int ret;
+
+ if (i2c->addr > ARRAY_SIZE(retu_data))
+ return -ENODEV;
+ rdat = &retu_data[i2c->addr - 1];
+
+ rdev = devm_kzalloc(&i2c->dev, sizeof(*rdev), GFP_KERNEL);
+ if (rdev == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rdev);
+ rdev->dev = &i2c->dev;
+ mutex_init(&rdev->mutex);
+ rdev->regmap = devm_regmap_init(&i2c->dev, &retu_bus, &i2c->dev,
+ &retu_config);
+ if (IS_ERR(rdev->regmap))
+ return PTR_ERR(rdev->regmap);
+
+ ret = retu_read(rdev, RETU_REG_ASICR);
+ if (ret < 0) {
+ dev_err(rdev->dev, "could not read %s revision: %d\n",
+ rdat->chip_name, ret);
+ return ret;
+ }
+
+ dev_info(rdev->dev, "%s%s%s v%d.%d found\n", rdat->chip_name,
+ (ret & RETU_REG_ASICR_VILMA) ? " & " : "",
+ (ret & RETU_REG_ASICR_VILMA) ? rdat->companion_name : "",
+ (ret >> 4) & 0x7, ret & 0xf);
+
+ /* Mask all interrupts. */
+ ret = retu_write(rdev, rdat->irq_chip->mask_base, 0xffff);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_add_irq_chip(rdev->regmap, i2c->irq, IRQF_ONESHOT, -1,
+ rdat->irq_chip, &rdev->irq_data);
+ if (ret < 0)
+ return ret;
+
+ ret = mfd_add_devices(rdev->dev, -1, rdat->children, rdat->nchildren,
+ NULL, regmap_irq_chip_get_base(rdev->irq_data),
+ NULL);
+ if (ret < 0) {
+ regmap_del_irq_chip(i2c->irq, rdev->irq_data);
+ return ret;
+ }
+
+ if (i2c->addr == 1 && !pm_power_off) {
+ retu_pm_power_off = rdev;
+ pm_power_off = retu_power_off;
+ }
+
+ return 0;
+}
+
+static int retu_remove(struct i2c_client *i2c)
+{
+ struct retu_dev *rdev = i2c_get_clientdata(i2c);
+
+ if (retu_pm_power_off == rdev) {
+ pm_power_off = NULL;
+ retu_pm_power_off = NULL;
+ }
+ mfd_remove_devices(rdev->dev);
+ regmap_del_irq_chip(i2c->irq, rdev->irq_data);
+
+ return 0;
+}
+
+static const struct i2c_device_id retu_id[] = {
+ { "retu-mfd", 0 },
+ { "tahvo-mfd", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, retu_id);
+
+static struct i2c_driver retu_driver = {
+ .driver = {
+ .name = "retu-mfd",
+ .owner = THIS_MODULE,
+ },
+ .probe = retu_probe,
+ .remove = retu_remove,
+ .id_table = retu_id,
+};
+module_i2c_driver(retu_driver);
+
+MODULE_DESCRIPTION("Retu MFD driver");
+MODULE_AUTHOR("Juha Yrjölä");
+MODULE_AUTHOR("David Weinehall");
+MODULE_AUTHOR("Mikko Ylinen");
+MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/rtl8411.c b/drivers/mfd/rtl8411.c
new file mode 100644
index 00000000000..fdd34c883d8
--- /dev/null
+++ b/drivers/mfd/rtl8411.c
@@ -0,0 +1,509 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * 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, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG <wei_wang@realsil.com.cn>
+ * Roger Tseng <rogerable@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/mfd/rtsx_pci.h>
+
+#include "rtsx_pcr.h"
+
+static u8 rtl8411_get_ic_version(struct rtsx_pcr *pcr)
+{
+ u8 val;
+
+ rtsx_pci_read_register(pcr, SYS_VER, &val);
+ return val & 0x0F;
+}
+
+static int rtl8411b_is_qfn48(struct rtsx_pcr *pcr)
+{
+ u8 val = 0;
+
+ rtsx_pci_read_register(pcr, RTL8411B_PACKAGE_MODE, &val);
+
+ if (val & 0x2)
+ return 1;
+ else
+ return 0;
+}
+
+static void rtl8411_fetch_vendor_settings(struct rtsx_pcr *pcr)
+{
+ u32 reg1 = 0;
+ u8 reg3 = 0;
+
+ rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, &reg1);
+ dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg1);
+
+ if (!rtsx_vendor_setting_valid(reg1))
+ return;
+
+ pcr->aspm_en = rtsx_reg_to_aspm(reg1);
+ pcr->sd30_drive_sel_1v8 =
+ map_sd_drive(rtsx_reg_to_sd30_drive_sel_1v8(reg1));
+ pcr->card_drive_sel &= 0x3F;
+ pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg1);
+
+ rtsx_pci_read_config_byte(pcr, PCR_SETTING_REG3, &reg3);
+ dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG3, reg3);
+ pcr->sd30_drive_sel_3v3 = rtl8411_reg_to_sd30_drive_sel_3v3(reg3);
+}
+
+static void rtl8411b_fetch_vendor_settings(struct rtsx_pcr *pcr)
+{
+ u32 reg = 0;
+
+ rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, &reg);
+ dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg);
+
+ if (!rtsx_vendor_setting_valid(reg))
+ return;
+
+ pcr->aspm_en = rtsx_reg_to_aspm(reg);
+ pcr->sd30_drive_sel_1v8 =
+ map_sd_drive(rtsx_reg_to_sd30_drive_sel_1v8(reg));
+ pcr->sd30_drive_sel_3v3 =
+ map_sd_drive(rtl8411b_reg_to_sd30_drive_sel_3v3(reg));
+}
+
+static void rtl8411_force_power_down(struct rtsx_pcr *pcr, u8 pm_state)
+{
+ rtsx_pci_write_register(pcr, FPDCTL, 0x07, 0x07);
+}
+
+static int rtl8411_extra_init_hw(struct rtsx_pcr *pcr)
+{
+ rtsx_pci_init_cmd(pcr);
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DRIVE_SEL,
+ 0xFF, pcr->sd30_drive_sel_3v3);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CD_PAD_CTL,
+ CD_DISABLE_MASK | CD_AUTO_DISABLE, CD_ENABLE);
+
+ return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static int rtl8411b_extra_init_hw(struct rtsx_pcr *pcr)
+{
+ rtsx_pci_init_cmd(pcr);
+
+ if (rtl8411b_is_qfn48(pcr))
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+ CARD_PULL_CTL3, 0xFF, 0xF5);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DRIVE_SEL,
+ 0xFF, pcr->sd30_drive_sel_3v3);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CD_PAD_CTL,
+ CD_DISABLE_MASK | CD_AUTO_DISABLE, CD_ENABLE);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, FUNC_FORCE_CTL,
+ 0x06, 0x00);
+
+ return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static int rtl8411_turn_on_led(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x00);
+}
+
+static int rtl8411_turn_off_led(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x01);
+}
+
+static int rtl8411_enable_auto_blink(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0xFF, 0x0D);
+}
+
+static int rtl8411_disable_auto_blink(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0x08, 0x00);
+}
+
+static int rtl8411_card_power_on(struct rtsx_pcr *pcr, int card)
+{
+ int err;
+
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+ BPP_POWER_MASK, BPP_POWER_5_PERCENT_ON);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_CTL,
+ BPP_LDO_POWB, BPP_LDO_SUSPEND);
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ /* To avoid too large in-rush current */
+ udelay(150);
+
+ err = rtsx_pci_write_register(pcr, CARD_PWR_CTL,
+ BPP_POWER_MASK, BPP_POWER_10_PERCENT_ON);
+ if (err < 0)
+ return err;
+
+ udelay(150);
+
+ err = rtsx_pci_write_register(pcr, CARD_PWR_CTL,
+ BPP_POWER_MASK, BPP_POWER_15_PERCENT_ON);
+ if (err < 0)
+ return err;
+
+ udelay(150);
+
+ err = rtsx_pci_write_register(pcr, CARD_PWR_CTL,
+ BPP_POWER_MASK, BPP_POWER_ON);
+ if (err < 0)
+ return err;
+
+ return rtsx_pci_write_register(pcr, LDO_CTL, BPP_LDO_POWB, BPP_LDO_ON);
+}
+
+static int rtl8411_card_power_off(struct rtsx_pcr *pcr, int card)
+{
+ int err;
+
+ err = rtsx_pci_write_register(pcr, CARD_PWR_CTL,
+ BPP_POWER_MASK, BPP_POWER_OFF);
+ if (err < 0)
+ return err;
+
+ return rtsx_pci_write_register(pcr, LDO_CTL,
+ BPP_LDO_POWB, BPP_LDO_SUSPEND);
+}
+
+static int rtl8411_do_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage,
+ int bpp_tuned18_shift, int bpp_asic_1v8)
+{
+ u8 mask, val;
+ int err;
+
+ mask = (BPP_REG_TUNED18 << bpp_tuned18_shift) | BPP_PAD_MASK;
+ if (voltage == OUTPUT_3V3) {
+ err = rtsx_pci_write_register(pcr,
+ SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_3v3);
+ if (err < 0)
+ return err;
+ val = (BPP_ASIC_3V3 << bpp_tuned18_shift) | BPP_PAD_3V3;
+ } else if (voltage == OUTPUT_1V8) {
+ err = rtsx_pci_write_register(pcr,
+ SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_1v8);
+ if (err < 0)
+ return err;
+ val = (bpp_asic_1v8 << bpp_tuned18_shift) | BPP_PAD_1V8;
+ } else {
+ return -EINVAL;
+ }
+
+ return rtsx_pci_write_register(pcr, LDO_CTL, mask, val);
+}
+
+static int rtl8411_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
+{
+ return rtl8411_do_switch_output_voltage(pcr, voltage,
+ BPP_TUNED18_SHIFT_8411, BPP_ASIC_1V8);
+}
+
+static int rtl8402_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
+{
+ return rtl8411_do_switch_output_voltage(pcr, voltage,
+ BPP_TUNED18_SHIFT_8402, BPP_ASIC_2V0);
+}
+
+static unsigned int rtl8411_cd_deglitch(struct rtsx_pcr *pcr)
+{
+ unsigned int card_exist;
+
+ card_exist = rtsx_pci_readl(pcr, RTSX_BIPR);
+ card_exist &= CARD_EXIST;
+ if (!card_exist) {
+ /* Enable card CD */
+ rtsx_pci_write_register(pcr, CD_PAD_CTL,
+ CD_DISABLE_MASK, CD_ENABLE);
+ /* Enable card interrupt */
+ rtsx_pci_write_register(pcr, EFUSE_CONTENT, 0xe0, 0x00);
+ return 0;
+ }
+
+ if (hweight32(card_exist) > 1) {
+ rtsx_pci_write_register(pcr, CARD_PWR_CTL,
+ BPP_POWER_MASK, BPP_POWER_5_PERCENT_ON);
+ msleep(100);
+
+ card_exist = rtsx_pci_readl(pcr, RTSX_BIPR);
+ if (card_exist & MS_EXIST)
+ card_exist = MS_EXIST;
+ else if (card_exist & SD_EXIST)
+ card_exist = SD_EXIST;
+ else
+ card_exist = 0;
+
+ rtsx_pci_write_register(pcr, CARD_PWR_CTL,
+ BPP_POWER_MASK, BPP_POWER_OFF);
+
+ dev_dbg(&(pcr->pci->dev),
+ "After CD deglitch, card_exist = 0x%x\n",
+ card_exist);
+ }
+
+ if (card_exist & MS_EXIST) {
+ /* Disable SD interrupt */
+ rtsx_pci_write_register(pcr, EFUSE_CONTENT, 0xe0, 0x40);
+ rtsx_pci_write_register(pcr, CD_PAD_CTL,
+ CD_DISABLE_MASK, MS_CD_EN_ONLY);
+ } else if (card_exist & SD_EXIST) {
+ /* Disable MS interrupt */
+ rtsx_pci_write_register(pcr, EFUSE_CONTENT, 0xe0, 0x80);
+ rtsx_pci_write_register(pcr, CD_PAD_CTL,
+ CD_DISABLE_MASK, SD_CD_EN_ONLY);
+ }
+
+ return card_exist;
+}
+
+static int rtl8411_conv_clk_and_div_n(int input, int dir)
+{
+ int output;
+
+ if (dir == CLK_TO_DIV_N)
+ output = input * 4 / 5 - 2;
+ else
+ output = (input + 2) * 5 / 4;
+
+ return output;
+}
+
+static const struct pcr_ops rtl8411_pcr_ops = {
+ .fetch_vendor_settings = rtl8411_fetch_vendor_settings,
+ .extra_init_hw = rtl8411_extra_init_hw,
+ .optimize_phy = NULL,
+ .turn_on_led = rtl8411_turn_on_led,
+ .turn_off_led = rtl8411_turn_off_led,
+ .enable_auto_blink = rtl8411_enable_auto_blink,
+ .disable_auto_blink = rtl8411_disable_auto_blink,
+ .card_power_on = rtl8411_card_power_on,
+ .card_power_off = rtl8411_card_power_off,
+ .switch_output_voltage = rtl8411_switch_output_voltage,
+ .cd_deglitch = rtl8411_cd_deglitch,
+ .conv_clk_and_div_n = rtl8411_conv_clk_and_div_n,
+ .force_power_down = rtl8411_force_power_down,
+};
+
+static const struct pcr_ops rtl8402_pcr_ops = {
+ .fetch_vendor_settings = rtl8411_fetch_vendor_settings,
+ .extra_init_hw = rtl8411_extra_init_hw,
+ .optimize_phy = NULL,
+ .turn_on_led = rtl8411_turn_on_led,
+ .turn_off_led = rtl8411_turn_off_led,
+ .enable_auto_blink = rtl8411_enable_auto_blink,
+ .disable_auto_blink = rtl8411_disable_auto_blink,
+ .card_power_on = rtl8411_card_power_on,
+ .card_power_off = rtl8411_card_power_off,
+ .switch_output_voltage = rtl8402_switch_output_voltage,
+ .cd_deglitch = rtl8411_cd_deglitch,
+ .conv_clk_and_div_n = rtl8411_conv_clk_and_div_n,
+ .force_power_down = rtl8411_force_power_down,
+};
+
+static const struct pcr_ops rtl8411b_pcr_ops = {
+ .fetch_vendor_settings = rtl8411b_fetch_vendor_settings,
+ .extra_init_hw = rtl8411b_extra_init_hw,
+ .optimize_phy = NULL,
+ .turn_on_led = rtl8411_turn_on_led,
+ .turn_off_led = rtl8411_turn_off_led,
+ .enable_auto_blink = rtl8411_enable_auto_blink,
+ .disable_auto_blink = rtl8411_disable_auto_blink,
+ .card_power_on = rtl8411_card_power_on,
+ .card_power_off = rtl8411_card_power_off,
+ .switch_output_voltage = rtl8411_switch_output_voltage,
+ .cd_deglitch = rtl8411_cd_deglitch,
+ .conv_clk_and_div_n = rtl8411_conv_clk_and_div_n,
+ .force_power_down = rtl8411_force_power_down,
+};
+
+/* SD Pull Control Enable:
+ * SD_DAT[3:0] ==> pull up
+ * SD_CD ==> pull up
+ * SD_WP ==> pull up
+ * SD_CMD ==> pull up
+ * SD_CLK ==> pull down
+ */
+static const u32 rtl8411_sd_pull_ctl_enable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL1, 0xAA),
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0xA9),
+ RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09),
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x09),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04),
+ 0,
+};
+
+/* SD Pull Control Disable:
+ * SD_DAT[3:0] ==> pull down
+ * SD_CD ==> pull up
+ * SD_WP ==> pull down
+ * SD_CMD ==> pull down
+ * SD_CLK ==> pull down
+ */
+static const u32 rtl8411_sd_pull_ctl_disable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65),
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0x95),
+ RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09),
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04),
+ 0,
+};
+
+/* MS Pull Control Enable:
+ * MS CD ==> pull up
+ * others ==> pull down
+ */
+static const u32 rtl8411_ms_pull_ctl_enable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65),
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0x95),
+ RTSX_REG_PAIR(CARD_PULL_CTL4, 0x05),
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04),
+ 0,
+};
+
+/* MS Pull Control Disable:
+ * MS CD ==> pull up
+ * others ==> pull down
+ */
+static const u32 rtl8411_ms_pull_ctl_disable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65),
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0x95),
+ RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09),
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04),
+ 0,
+};
+
+static const u32 rtl8411b_qfn64_sd_pull_ctl_enable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL1, 0xAA),
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0x09 | 0xD0),
+ RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09 | 0x50),
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05 | 0x50),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11),
+ 0,
+};
+
+static const u32 rtl8411b_qfn48_sd_pull_ctl_enable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0x69 | 0x90),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x08 | 0x11),
+ 0,
+};
+
+static const u32 rtl8411b_qfn64_sd_pull_ctl_disable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65),
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0x05 | 0xD0),
+ RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09 | 0x50),
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05 | 0x50),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11),
+ 0,
+};
+
+static const u32 rtl8411b_qfn48_sd_pull_ctl_disable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0x65 | 0x90),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11),
+ 0,
+};
+
+static const u32 rtl8411b_qfn64_ms_pull_ctl_enable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65),
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0x05 | 0xD0),
+ RTSX_REG_PAIR(CARD_PULL_CTL4, 0x05 | 0x50),
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05 | 0x50),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11),
+ 0,
+};
+
+static const u32 rtl8411b_qfn48_ms_pull_ctl_enable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0x65 | 0x90),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11),
+ 0,
+};
+
+static const u32 rtl8411b_qfn64_ms_pull_ctl_disable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65),
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0x05 | 0xD0),
+ RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09 | 0x50),
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05 | 0x50),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11),
+ 0,
+};
+
+static const u32 rtl8411b_qfn48_ms_pull_ctl_disable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0x65 | 0x90),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11),
+ 0,
+};
+
+static void rtl8411_init_common_params(struct rtsx_pcr *pcr)
+{
+ pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104;
+ pcr->num_slots = 2;
+ pcr->flags = 0;
+ pcr->card_drive_sel = RTL8411_CARD_DRIVE_DEFAULT;
+ pcr->sd30_drive_sel_1v8 = DRIVER_TYPE_B;
+ pcr->sd30_drive_sel_3v3 = DRIVER_TYPE_D;
+ pcr->aspm_en = ASPM_L1_EN;
+ pcr->tx_initial_phase = SET_CLOCK_PHASE(23, 7, 14);
+ pcr->rx_initial_phase = SET_CLOCK_PHASE(4, 3, 10);
+ pcr->ic_version = rtl8411_get_ic_version(pcr);
+}
+
+void rtl8411_init_params(struct rtsx_pcr *pcr)
+{
+ rtl8411_init_common_params(pcr);
+ pcr->ops = &rtl8411_pcr_ops;
+ set_pull_ctrl_tables(pcr, rtl8411);
+}
+
+void rtl8411b_init_params(struct rtsx_pcr *pcr)
+{
+ rtl8411_init_common_params(pcr);
+ pcr->ops = &rtl8411b_pcr_ops;
+ if (rtl8411b_is_qfn48(pcr))
+ set_pull_ctrl_tables(pcr, rtl8411b_qfn48);
+ else
+ set_pull_ctrl_tables(pcr, rtl8411b_qfn64);
+}
+
+void rtl8402_init_params(struct rtsx_pcr *pcr)
+{
+ rtl8411_init_common_params(pcr);
+ pcr->ops = &rtl8402_pcr_ops;
+ set_pull_ctrl_tables(pcr, rtl8411);
+}
diff --git a/drivers/mfd/rts5209.c b/drivers/mfd/rts5209.c
new file mode 100644
index 00000000000..cb04174a892
--- /dev/null
+++ b/drivers/mfd/rts5209.c
@@ -0,0 +1,281 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * 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, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG <wei_wang@realsil.com.cn>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mfd/rtsx_pci.h>
+
+#include "rtsx_pcr.h"
+
+static u8 rts5209_get_ic_version(struct rtsx_pcr *pcr)
+{
+ u8 val;
+
+ val = rtsx_pci_readb(pcr, 0x1C);
+ return val & 0x0F;
+}
+
+static void rts5209_fetch_vendor_settings(struct rtsx_pcr *pcr)
+{
+ u32 reg;
+
+ rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, &reg);
+ dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg);
+
+ if (rts5209_vendor_setting1_valid(reg)) {
+ if (rts5209_reg_check_ms_pmos(reg))
+ pcr->flags |= PCR_MS_PMOS;
+ pcr->aspm_en = rts5209_reg_to_aspm(reg);
+ }
+
+ rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, &reg);
+ dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg);
+
+ if (rts5209_vendor_setting2_valid(reg)) {
+ pcr->sd30_drive_sel_1v8 =
+ rts5209_reg_to_sd30_drive_sel_1v8(reg);
+ pcr->sd30_drive_sel_3v3 =
+ rts5209_reg_to_sd30_drive_sel_3v3(reg);
+ pcr->card_drive_sel = rts5209_reg_to_card_drive_sel(reg);
+ }
+}
+
+static void rts5209_force_power_down(struct rtsx_pcr *pcr, u8 pm_state)
+{
+ rtsx_pci_write_register(pcr, FPDCTL, 0x07, 0x07);
+}
+
+static int rts5209_extra_init_hw(struct rtsx_pcr *pcr)
+{
+ rtsx_pci_init_cmd(pcr);
+
+ /* Turn off LED */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_GPIO, 0xFF, 0x03);
+ /* Reset ASPM state to default value */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, ASPM_FORCE_CTL, 0x3F, 0);
+ /* Force CLKREQ# PIN to drive 0 to request clock */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0x08, 0x08);
+ /* Configure GPIO as output */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_GPIO_DIR, 0xFF, 0x03);
+ /* Configure driving */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DRIVE_SEL,
+ 0xFF, pcr->sd30_drive_sel_3v3);
+
+ return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static int rts5209_optimize_phy(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_phy_register(pcr, 0x00, 0xB966);
+}
+
+static int rts5209_turn_on_led(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x00);
+}
+
+static int rts5209_turn_off_led(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x01);
+}
+
+static int rts5209_enable_auto_blink(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0xFF, 0x0D);
+}
+
+static int rts5209_disable_auto_blink(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0x08, 0x00);
+}
+
+static int rts5209_card_power_on(struct rtsx_pcr *pcr, int card)
+{
+ int err;
+ u8 pwr_mask, partial_pwr_on, pwr_on;
+
+ pwr_mask = SD_POWER_MASK;
+ partial_pwr_on = SD_PARTIAL_POWER_ON;
+ pwr_on = SD_POWER_ON;
+
+ if ((pcr->flags & PCR_MS_PMOS) && (card == RTSX_MS_CARD)) {
+ pwr_mask = MS_POWER_MASK;
+ partial_pwr_on = MS_PARTIAL_POWER_ON;
+ pwr_on = MS_POWER_ON;
+ }
+
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+ pwr_mask, partial_pwr_on);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, 0x04);
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ /* To avoid too large in-rush current */
+ udelay(150);
+
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, pwr_mask, pwr_on);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, 0x00);
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rts5209_card_power_off(struct rtsx_pcr *pcr, int card)
+{
+ u8 pwr_mask, pwr_off;
+
+ pwr_mask = SD_POWER_MASK;
+ pwr_off = SD_POWER_OFF;
+
+ if ((pcr->flags & PCR_MS_PMOS) && (card == RTSX_MS_CARD)) {
+ pwr_mask = MS_POWER_MASK;
+ pwr_off = MS_POWER_OFF;
+ }
+
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+ pwr_mask | PMOS_STRG_MASK, pwr_off | PMOS_STRG_400mA);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, 0x06);
+ return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static int rts5209_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
+{
+ int err;
+
+ if (voltage == OUTPUT_3V3) {
+ err = rtsx_pci_write_register(pcr,
+ SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_3v3);
+ if (err < 0)
+ return err;
+ err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24);
+ if (err < 0)
+ return err;
+ } else if (voltage == OUTPUT_1V8) {
+ err = rtsx_pci_write_register(pcr,
+ SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_1v8);
+ if (err < 0)
+ return err;
+ err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24);
+ if (err < 0)
+ return err;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct pcr_ops rts5209_pcr_ops = {
+ .fetch_vendor_settings = rts5209_fetch_vendor_settings,
+ .extra_init_hw = rts5209_extra_init_hw,
+ .optimize_phy = rts5209_optimize_phy,
+ .turn_on_led = rts5209_turn_on_led,
+ .turn_off_led = rts5209_turn_off_led,
+ .enable_auto_blink = rts5209_enable_auto_blink,
+ .disable_auto_blink = rts5209_disable_auto_blink,
+ .card_power_on = rts5209_card_power_on,
+ .card_power_off = rts5209_card_power_off,
+ .switch_output_voltage = rts5209_switch_output_voltage,
+ .cd_deglitch = NULL,
+ .conv_clk_and_div_n = NULL,
+ .force_power_down = rts5209_force_power_down,
+};
+
+/* SD Pull Control Enable:
+ * SD_DAT[3:0] ==> pull up
+ * SD_CD ==> pull up
+ * SD_WP ==> pull up
+ * SD_CMD ==> pull up
+ * SD_CLK ==> pull down
+ */
+static const u32 rts5209_sd_pull_ctl_enable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL1, 0xAA),
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9),
+ 0,
+};
+
+/* SD Pull Control Disable:
+ * SD_DAT[3:0] ==> pull down
+ * SD_CD ==> pull up
+ * SD_WP ==> pull down
+ * SD_CMD ==> pull down
+ * SD_CLK ==> pull down
+ */
+static const u32 rts5209_sd_pull_ctl_disable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL1, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5),
+ 0,
+};
+
+/* MS Pull Control Enable:
+ * MS CD ==> pull up
+ * others ==> pull down
+ */
+static const u32 rts5209_ms_pull_ctl_enable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15),
+ 0,
+};
+
+/* MS Pull Control Disable:
+ * MS CD ==> pull up
+ * others ==> pull down
+ */
+static const u32 rts5209_ms_pull_ctl_disable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15),
+ 0,
+};
+
+void rts5209_init_params(struct rtsx_pcr *pcr)
+{
+ pcr->extra_caps = EXTRA_CAPS_SD_SDR50 |
+ EXTRA_CAPS_SD_SDR104 | EXTRA_CAPS_MMC_8BIT;
+ pcr->num_slots = 2;
+ pcr->ops = &rts5209_pcr_ops;
+
+ pcr->flags = 0;
+ pcr->card_drive_sel = RTS5209_CARD_DRIVE_DEFAULT;
+ pcr->sd30_drive_sel_1v8 = DRIVER_TYPE_B;
+ pcr->sd30_drive_sel_3v3 = DRIVER_TYPE_D;
+ pcr->aspm_en = ASPM_L1_EN;
+ pcr->tx_initial_phase = SET_CLOCK_PHASE(27, 27, 16);
+ pcr->rx_initial_phase = SET_CLOCK_PHASE(24, 6, 5);
+
+ pcr->ic_version = rts5209_get_ic_version(pcr);
+ pcr->sd_pull_ctl_enable_tbl = rts5209_sd_pull_ctl_enable_tbl;
+ pcr->sd_pull_ctl_disable_tbl = rts5209_sd_pull_ctl_disable_tbl;
+ pcr->ms_pull_ctl_enable_tbl = rts5209_ms_pull_ctl_enable_tbl;
+ pcr->ms_pull_ctl_disable_tbl = rts5209_ms_pull_ctl_disable_tbl;
+}
diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c
new file mode 100644
index 00000000000..9c8eec80cee
--- /dev/null
+++ b/drivers/mfd/rts5227.c
@@ -0,0 +1,301 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * 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, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG <wei_wang@realsil.com.cn>
+ * Roger Tseng <rogerable@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mfd/rtsx_pci.h>
+
+#include "rtsx_pcr.h"
+
+static void rts5227_fill_driving(struct rtsx_pcr *pcr, u8 voltage)
+{
+ u8 driving_3v3[4][3] = {
+ {0x13, 0x13, 0x13},
+ {0x96, 0x96, 0x96},
+ {0x7F, 0x7F, 0x7F},
+ {0x96, 0x96, 0x96},
+ };
+ u8 driving_1v8[4][3] = {
+ {0x99, 0x99, 0x99},
+ {0xAA, 0xAA, 0xAA},
+ {0xFE, 0xFE, 0xFE},
+ {0xB3, 0xB3, 0xB3},
+ };
+ u8 (*driving)[3], drive_sel;
+
+ if (voltage == OUTPUT_3V3) {
+ driving = driving_3v3;
+ drive_sel = pcr->sd30_drive_sel_3v3;
+ } else {
+ driving = driving_1v8;
+ drive_sel = pcr->sd30_drive_sel_1v8;
+ }
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CLK_DRIVE_SEL,
+ 0xFF, driving[drive_sel][0]);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CMD_DRIVE_SEL,
+ 0xFF, driving[drive_sel][1]);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DAT_DRIVE_SEL,
+ 0xFF, driving[drive_sel][2]);
+}
+
+static void rts5227_fetch_vendor_settings(struct rtsx_pcr *pcr)
+{
+ u32 reg;
+
+ rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, &reg);
+ dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg);
+
+ if (!rtsx_vendor_setting_valid(reg))
+ return;
+
+ pcr->aspm_en = rtsx_reg_to_aspm(reg);
+ pcr->sd30_drive_sel_1v8 = rtsx_reg_to_sd30_drive_sel_1v8(reg);
+ pcr->card_drive_sel &= 0x3F;
+ pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg);
+
+ rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, &reg);
+ dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg);
+ pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg);
+ if (rtsx_reg_check_reverse_socket(reg))
+ pcr->flags |= PCR_REVERSE_SOCKET;
+}
+
+static void rts5227_force_power_down(struct rtsx_pcr *pcr, u8 pm_state)
+{
+ /* Set relink_time to 0 */
+ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, 0xFF, 0);
+ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, 0xFF, 0);
+ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, 0x01, 0);
+
+ if (pm_state == HOST_ENTER_S3)
+ rtsx_pci_write_register(pcr, PM_CTRL3, 0x10, 0x10);
+
+ rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03);
+}
+
+static int rts5227_extra_init_hw(struct rtsx_pcr *pcr)
+{
+ u16 cap;
+
+ rtsx_pci_init_cmd(pcr);
+
+ /* Configure GPIO as output */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02);
+ /* Reset ASPM state to default value */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, ASPM_FORCE_CTL, 0x3F, 0);
+ /* Switch LDO3318 source from DV33 to card_3v3 */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x00);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01);
+ /* LED shine disabled, set initial shine cycle period */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02);
+ /* Configure LTR */
+ pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &cap);
+ if (cap & PCI_EXP_DEVCTL2_LTR_EN)
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LTR_CTL, 0xFF, 0xA3);
+ /* Configure OBFF */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OBFF_CFG, 0x03, 0x03);
+ /* Configure driving */
+ rts5227_fill_driving(pcr, OUTPUT_3V3);
+ /* Configure force_clock_req */
+ if (pcr->flags & PCR_REVERSE_SOCKET)
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+ AUTOLOAD_CFG_BASE + 3, 0xB8, 0xB8);
+ else
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+ AUTOLOAD_CFG_BASE + 3, 0xB8, 0x88);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PM_CTRL3, 0x10, 0x00);
+
+ return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static int rts5227_optimize_phy(struct rtsx_pcr *pcr)
+{
+ /* Optimize RX sensitivity */
+ return rtsx_pci_write_phy_register(pcr, 0x00, 0xBA42);
+}
+
+static int rts5227_turn_on_led(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02);
+}
+
+static int rts5227_turn_off_led(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00);
+}
+
+static int rts5227_enable_auto_blink(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08);
+}
+
+static int rts5227_disable_auto_blink(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00);
+}
+
+static int rts5227_card_power_on(struct rtsx_pcr *pcr, int card)
+{
+ int err;
+
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+ SD_POWER_MASK, SD_PARTIAL_POWER_ON);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, 0x02);
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ /* To avoid too large in-rush current */
+ udelay(150);
+
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+ SD_POWER_MASK, SD_POWER_ON);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, 0x06);
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rts5227_card_power_off(struct rtsx_pcr *pcr, int card)
+{
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+ SD_POWER_MASK | PMOS_STRG_MASK,
+ SD_POWER_OFF | PMOS_STRG_400mA);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, 0X00);
+ return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static int rts5227_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
+{
+ int err;
+
+ if (voltage == OUTPUT_3V3) {
+ err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24);
+ if (err < 0)
+ return err;
+ } else if (voltage == OUTPUT_1V8) {
+ err = rtsx_pci_write_phy_register(pcr, 0x11, 0x3C02);
+ if (err < 0)
+ return err;
+ err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C80 | 0x24);
+ if (err < 0)
+ return err;
+ } else {
+ return -EINVAL;
+ }
+
+ /* set pad drive */
+ rtsx_pci_init_cmd(pcr);
+ rts5227_fill_driving(pcr, voltage);
+ return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static const struct pcr_ops rts5227_pcr_ops = {
+ .fetch_vendor_settings = rts5227_fetch_vendor_settings,
+ .extra_init_hw = rts5227_extra_init_hw,
+ .optimize_phy = rts5227_optimize_phy,
+ .turn_on_led = rts5227_turn_on_led,
+ .turn_off_led = rts5227_turn_off_led,
+ .enable_auto_blink = rts5227_enable_auto_blink,
+ .disable_auto_blink = rts5227_disable_auto_blink,
+ .card_power_on = rts5227_card_power_on,
+ .card_power_off = rts5227_card_power_off,
+ .switch_output_voltage = rts5227_switch_output_voltage,
+ .cd_deglitch = NULL,
+ .conv_clk_and_div_n = NULL,
+ .force_power_down = rts5227_force_power_down,
+};
+
+/* SD Pull Control Enable:
+ * SD_DAT[3:0] ==> pull up
+ * SD_CD ==> pull up
+ * SD_WP ==> pull up
+ * SD_CMD ==> pull up
+ * SD_CLK ==> pull down
+ */
+static const u32 rts5227_sd_pull_ctl_enable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9),
+ 0,
+};
+
+/* SD Pull Control Disable:
+ * SD_DAT[3:0] ==> pull down
+ * SD_CD ==> pull up
+ * SD_WP ==> pull down
+ * SD_CMD ==> pull down
+ * SD_CLK ==> pull down
+ */
+static const u32 rts5227_sd_pull_ctl_disable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5),
+ 0,
+};
+
+/* MS Pull Control Enable:
+ * MS CD ==> pull up
+ * others ==> pull down
+ */
+static const u32 rts5227_ms_pull_ctl_enable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15),
+ 0,
+};
+
+/* MS Pull Control Disable:
+ * MS CD ==> pull up
+ * others ==> pull down
+ */
+static const u32 rts5227_ms_pull_ctl_disable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15),
+ 0,
+};
+
+void rts5227_init_params(struct rtsx_pcr *pcr)
+{
+ pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104;
+ pcr->num_slots = 2;
+ pcr->ops = &rts5227_pcr_ops;
+
+ pcr->flags = 0;
+ pcr->card_drive_sel = RTSX_CARD_DRIVE_DEFAULT;
+ pcr->sd30_drive_sel_1v8 = CFG_DRIVER_TYPE_B;
+ pcr->sd30_drive_sel_3v3 = CFG_DRIVER_TYPE_B;
+ pcr->aspm_en = ASPM_L1_EN;
+ pcr->tx_initial_phase = SET_CLOCK_PHASE(27, 27, 15);
+ pcr->rx_initial_phase = SET_CLOCK_PHASE(30, 7, 7);
+
+ pcr->sd_pull_ctl_enable_tbl = rts5227_sd_pull_ctl_enable_tbl;
+ pcr->sd_pull_ctl_disable_tbl = rts5227_sd_pull_ctl_disable_tbl;
+ pcr->ms_pull_ctl_enable_tbl = rts5227_ms_pull_ctl_enable_tbl;
+ pcr->ms_pull_ctl_disable_tbl = rts5227_ms_pull_ctl_disable_tbl;
+}
diff --git a/drivers/mfd/rts5229.c b/drivers/mfd/rts5229.c
new file mode 100644
index 00000000000..6353f5df087
--- /dev/null
+++ b/drivers/mfd/rts5229.c
@@ -0,0 +1,277 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * 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, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG <wei_wang@realsil.com.cn>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mfd/rtsx_pci.h>
+
+#include "rtsx_pcr.h"
+
+static u8 rts5229_get_ic_version(struct rtsx_pcr *pcr)
+{
+ u8 val;
+
+ rtsx_pci_read_register(pcr, DUMMY_REG_RESET_0, &val);
+ return val & 0x0F;
+}
+
+static void rts5229_fetch_vendor_settings(struct rtsx_pcr *pcr)
+{
+ u32 reg;
+
+ rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, &reg);
+ dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg);
+
+ if (!rtsx_vendor_setting_valid(reg))
+ return;
+
+ pcr->aspm_en = rtsx_reg_to_aspm(reg);
+ pcr->sd30_drive_sel_1v8 =
+ map_sd_drive(rtsx_reg_to_sd30_drive_sel_1v8(reg));
+ pcr->card_drive_sel &= 0x3F;
+ pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg);
+
+ rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, &reg);
+ dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg);
+ pcr->sd30_drive_sel_3v3 =
+ map_sd_drive(rtsx_reg_to_sd30_drive_sel_3v3(reg));
+}
+
+static void rts5229_force_power_down(struct rtsx_pcr *pcr, u8 pm_state)
+{
+ rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03);
+}
+
+static int rts5229_extra_init_hw(struct rtsx_pcr *pcr)
+{
+ rtsx_pci_init_cmd(pcr);
+
+ /* Configure GPIO as output */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02);
+ /* Reset ASPM state to default value */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, ASPM_FORCE_CTL, 0x3F, 0);
+ /* Force CLKREQ# PIN to drive 0 to request clock */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0x08, 0x08);
+ /* Switch LDO3318 source from DV33 to card_3v3 */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x00);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01);
+ /* LED shine disabled, set initial shine cycle period */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02);
+ /* Configure driving */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DRIVE_SEL,
+ 0xFF, pcr->sd30_drive_sel_3v3);
+
+ return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static int rts5229_optimize_phy(struct rtsx_pcr *pcr)
+{
+ /* Optimize RX sensitivity */
+ return rtsx_pci_write_phy_register(pcr, 0x00, 0xBA42);
+}
+
+static int rts5229_turn_on_led(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02);
+}
+
+static int rts5229_turn_off_led(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00);
+}
+
+static int rts5229_enable_auto_blink(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08);
+}
+
+static int rts5229_disable_auto_blink(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00);
+}
+
+static int rts5229_card_power_on(struct rtsx_pcr *pcr, int card)
+{
+ int err;
+
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+ SD_POWER_MASK, SD_PARTIAL_POWER_ON);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, 0x02);
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ /* To avoid too large in-rush current */
+ udelay(150);
+
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+ SD_POWER_MASK, SD_POWER_ON);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, 0x06);
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rts5229_card_power_off(struct rtsx_pcr *pcr, int card)
+{
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+ SD_POWER_MASK | PMOS_STRG_MASK,
+ SD_POWER_OFF | PMOS_STRG_400mA);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, 0x00);
+ return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static int rts5229_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
+{
+ int err;
+
+ if (voltage == OUTPUT_3V3) {
+ err = rtsx_pci_write_register(pcr,
+ SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_3v3);
+ if (err < 0)
+ return err;
+ err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24);
+ if (err < 0)
+ return err;
+ } else if (voltage == OUTPUT_1V8) {
+ err = rtsx_pci_write_register(pcr,
+ SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_1v8);
+ if (err < 0)
+ return err;
+ err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24);
+ if (err < 0)
+ return err;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct pcr_ops rts5229_pcr_ops = {
+ .fetch_vendor_settings = rts5229_fetch_vendor_settings,
+ .extra_init_hw = rts5229_extra_init_hw,
+ .optimize_phy = rts5229_optimize_phy,
+ .turn_on_led = rts5229_turn_on_led,
+ .turn_off_led = rts5229_turn_off_led,
+ .enable_auto_blink = rts5229_enable_auto_blink,
+ .disable_auto_blink = rts5229_disable_auto_blink,
+ .card_power_on = rts5229_card_power_on,
+ .card_power_off = rts5229_card_power_off,
+ .switch_output_voltage = rts5229_switch_output_voltage,
+ .cd_deglitch = NULL,
+ .conv_clk_and_div_n = NULL,
+ .force_power_down = rts5229_force_power_down,
+};
+
+/* SD Pull Control Enable:
+ * SD_DAT[3:0] ==> pull up
+ * SD_CD ==> pull up
+ * SD_WP ==> pull up
+ * SD_CMD ==> pull up
+ * SD_CLK ==> pull down
+ */
+static const u32 rts5229_sd_pull_ctl_enable_tbl1[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9),
+ 0,
+};
+
+/* For RTS5229 version C */
+static const u32 rts5229_sd_pull_ctl_enable_tbl2[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD9),
+ 0,
+};
+
+/* SD Pull Control Disable:
+ * SD_DAT[3:0] ==> pull down
+ * SD_CD ==> pull up
+ * SD_WP ==> pull down
+ * SD_CMD ==> pull down
+ * SD_CLK ==> pull down
+ */
+static const u32 rts5229_sd_pull_ctl_disable_tbl1[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5),
+ 0,
+};
+
+/* For RTS5229 version C */
+static const u32 rts5229_sd_pull_ctl_disable_tbl2[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE5),
+ 0,
+};
+
+/* MS Pull Control Enable:
+ * MS CD ==> pull up
+ * others ==> pull down
+ */
+static const u32 rts5229_ms_pull_ctl_enable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15),
+ 0,
+};
+
+/* MS Pull Control Disable:
+ * MS CD ==> pull up
+ * others ==> pull down
+ */
+static const u32 rts5229_ms_pull_ctl_disable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15),
+ 0,
+};
+
+void rts5229_init_params(struct rtsx_pcr *pcr)
+{
+ pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104;
+ pcr->num_slots = 2;
+ pcr->ops = &rts5229_pcr_ops;
+
+ pcr->flags = 0;
+ pcr->card_drive_sel = RTSX_CARD_DRIVE_DEFAULT;
+ pcr->sd30_drive_sel_1v8 = DRIVER_TYPE_B;
+ pcr->sd30_drive_sel_3v3 = DRIVER_TYPE_D;
+ pcr->aspm_en = ASPM_L1_EN;
+ pcr->tx_initial_phase = SET_CLOCK_PHASE(27, 27, 15);
+ pcr->rx_initial_phase = SET_CLOCK_PHASE(30, 6, 6);
+
+ pcr->ic_version = rts5229_get_ic_version(pcr);
+ if (pcr->ic_version == IC_VER_C) {
+ pcr->sd_pull_ctl_enable_tbl = rts5229_sd_pull_ctl_enable_tbl2;
+ pcr->sd_pull_ctl_disable_tbl = rts5229_sd_pull_ctl_disable_tbl2;
+ } else {
+ pcr->sd_pull_ctl_enable_tbl = rts5229_sd_pull_ctl_enable_tbl1;
+ pcr->sd_pull_ctl_disable_tbl = rts5229_sd_pull_ctl_disable_tbl1;
+ }
+ pcr->ms_pull_ctl_enable_tbl = rts5229_ms_pull_ctl_enable_tbl;
+ pcr->ms_pull_ctl_disable_tbl = rts5229_ms_pull_ctl_disable_tbl;
+}
diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c
new file mode 100644
index 00000000000..573de7bfcce
--- /dev/null
+++ b/drivers/mfd/rts5249.c
@@ -0,0 +1,353 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * 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, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG <wei_wang@realsil.com.cn>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mfd/rtsx_pci.h>
+
+#include "rtsx_pcr.h"
+
+static u8 rts5249_get_ic_version(struct rtsx_pcr *pcr)
+{
+ u8 val;
+
+ rtsx_pci_read_register(pcr, DUMMY_REG_RESET_0, &val);
+ return val & 0x0F;
+}
+
+static void rts5249_fill_driving(struct rtsx_pcr *pcr, u8 voltage)
+{
+ u8 driving_3v3[4][3] = {
+ {0x11, 0x11, 0x11},
+ {0x55, 0x55, 0x5C},
+ {0x99, 0x99, 0x92},
+ {0x99, 0x99, 0x92},
+ };
+ u8 driving_1v8[4][3] = {
+ {0x3C, 0x3C, 0x3C},
+ {0xB3, 0xB3, 0xB3},
+ {0xFE, 0xFE, 0xFE},
+ {0xC4, 0xC4, 0xC4},
+ };
+ u8 (*driving)[3], drive_sel;
+
+ if (voltage == OUTPUT_3V3) {
+ driving = driving_3v3;
+ drive_sel = pcr->sd30_drive_sel_3v3;
+ } else {
+ driving = driving_1v8;
+ drive_sel = pcr->sd30_drive_sel_1v8;
+ }
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CLK_DRIVE_SEL,
+ 0xFF, driving[drive_sel][0]);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CMD_DRIVE_SEL,
+ 0xFF, driving[drive_sel][1]);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DAT_DRIVE_SEL,
+ 0xFF, driving[drive_sel][2]);
+}
+
+static void rts5249_fetch_vendor_settings(struct rtsx_pcr *pcr)
+{
+ u32 reg;
+
+ rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, &reg);
+ dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg);
+
+ if (!rtsx_vendor_setting_valid(reg))
+ return;
+
+ pcr->aspm_en = rtsx_reg_to_aspm(reg);
+ pcr->sd30_drive_sel_1v8 = rtsx_reg_to_sd30_drive_sel_1v8(reg);
+ pcr->card_drive_sel &= 0x3F;
+ pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg);
+
+ rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, &reg);
+ dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg);
+ pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg);
+ if (rtsx_reg_check_reverse_socket(reg))
+ pcr->flags |= PCR_REVERSE_SOCKET;
+}
+
+static void rts5249_force_power_down(struct rtsx_pcr *pcr, u8 pm_state)
+{
+ /* Set relink_time to 0 */
+ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, 0xFF, 0);
+ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, 0xFF, 0);
+ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, 0x01, 0);
+
+ if (pm_state == HOST_ENTER_S3)
+ rtsx_pci_write_register(pcr, PM_CTRL3, 0x10, 0x10);
+
+ rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03);
+}
+
+static int rts5249_extra_init_hw(struct rtsx_pcr *pcr)
+{
+ rtsx_pci_init_cmd(pcr);
+
+ /* Configure GPIO as output */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02);
+ /* Reset ASPM state to default value */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, ASPM_FORCE_CTL, 0x3F, 0);
+ /* Switch LDO3318 source from DV33 to card_3v3 */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x00);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01);
+ /* LED shine disabled, set initial shine cycle period */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02);
+ /* Configure driving */
+ rts5249_fill_driving(pcr, OUTPUT_3V3);
+ if (pcr->flags & PCR_REVERSE_SOCKET)
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+ AUTOLOAD_CFG_BASE + 3, 0xB0, 0xB0);
+ else
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+ AUTOLOAD_CFG_BASE + 3, 0xB0, 0x80);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PM_CTRL3, 0x10, 0x00);
+
+ return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static int rts5249_optimize_phy(struct rtsx_pcr *pcr)
+{
+ int err;
+
+ err = rtsx_pci_write_phy_register(pcr, PHY_REG_REV,
+ PHY_REG_REV_RESV | PHY_REG_REV_RXIDLE_LATCHED |
+ PHY_REG_REV_P1_EN | PHY_REG_REV_RXIDLE_EN |
+ PHY_REG_REV_RX_PWST | PHY_REG_REV_CLKREQ_DLY_TIMER_1_0 |
+ PHY_REG_REV_STOP_CLKRD | PHY_REG_REV_STOP_CLKWR);
+ if (err < 0)
+ return err;
+
+ msleep(1);
+
+ err = rtsx_pci_write_phy_register(pcr, PHY_BPCR,
+ PHY_BPCR_IBRXSEL | PHY_BPCR_IBTXSEL |
+ PHY_BPCR_IB_FILTER | PHY_BPCR_CMIRROR_EN);
+ if (err < 0)
+ return err;
+ err = rtsx_pci_write_phy_register(pcr, PHY_PCR,
+ PHY_PCR_FORCE_CODE | PHY_PCR_OOBS_CALI_50 |
+ PHY_PCR_OOBS_VCM_08 | PHY_PCR_OOBS_SEN_90 |
+ PHY_PCR_RSSI_EN);
+ if (err < 0)
+ return err;
+ err = rtsx_pci_write_phy_register(pcr, PHY_RCR2,
+ PHY_RCR2_EMPHASE_EN | PHY_RCR2_NADJR |
+ PHY_RCR2_CDR_CP_10 | PHY_RCR2_CDR_SR_2 |
+ PHY_RCR2_FREQSEL_12 | PHY_RCR2_CPADJEN |
+ PHY_RCR2_CDR_SC_8 | PHY_RCR2_CALIB_LATE);
+ if (err < 0)
+ return err;
+ err = rtsx_pci_write_phy_register(pcr, PHY_FLD4,
+ PHY_FLD4_FLDEN_SEL | PHY_FLD4_REQ_REF |
+ PHY_FLD4_RXAMP_OFF | PHY_FLD4_REQ_ADDA |
+ PHY_FLD4_BER_COUNT | PHY_FLD4_BER_TIMER |
+ PHY_FLD4_BER_CHK_EN);
+ if (err < 0)
+ return err;
+ err = rtsx_pci_write_phy_register(pcr, PHY_RDR, PHY_RDR_RXDSEL_1_9);
+ if (err < 0)
+ return err;
+ err = rtsx_pci_write_phy_register(pcr, PHY_RCR1,
+ PHY_RCR1_ADP_TIME | PHY_RCR1_VCO_COARSE);
+ if (err < 0)
+ return err;
+ err = rtsx_pci_write_phy_register(pcr, PHY_FLD3,
+ PHY_FLD3_TIMER_4 | PHY_FLD3_TIMER_6 |
+ PHY_FLD3_RXDELINK);
+ if (err < 0)
+ return err;
+ return rtsx_pci_write_phy_register(pcr, PHY_TUNE,
+ PHY_TUNE_TUNEREF_1_0 | PHY_TUNE_VBGSEL_1252 |
+ PHY_TUNE_SDBUS_33 | PHY_TUNE_TUNED18 |
+ PHY_TUNE_TUNED12);
+}
+
+static int rts5249_turn_on_led(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02);
+}
+
+static int rts5249_turn_off_led(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00);
+}
+
+static int rts5249_enable_auto_blink(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08);
+}
+
+static int rts5249_disable_auto_blink(struct rtsx_pcr *pcr)
+{
+ return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00);
+}
+
+static int rts5249_card_power_on(struct rtsx_pcr *pcr, int card)
+{
+ int err;
+
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+ SD_POWER_MASK, SD_VCC_PARTIAL_POWER_ON);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, 0x02);
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ msleep(5);
+
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+ SD_POWER_MASK, SD_VCC_POWER_ON);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, 0x06);
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rts5249_card_power_off(struct rtsx_pcr *pcr, int card)
+{
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+ SD_POWER_MASK, SD_POWER_OFF);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, 0x00);
+ return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static int rts5249_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
+{
+ int err;
+
+ if (voltage == OUTPUT_3V3) {
+ err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, 0x4FC0 | 0x24);
+ if (err < 0)
+ return err;
+ } else if (voltage == OUTPUT_1V8) {
+ err = rtsx_pci_write_phy_register(pcr, PHY_BACR, 0x3C02);
+ if (err < 0)
+ return err;
+ err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, 0x4C40 | 0x24);
+ if (err < 0)
+ return err;
+ } else {
+ return -EINVAL;
+ }
+
+ /* set pad drive */
+ rtsx_pci_init_cmd(pcr);
+ rts5249_fill_driving(pcr, voltage);
+ return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static const struct pcr_ops rts5249_pcr_ops = {
+ .fetch_vendor_settings = rts5249_fetch_vendor_settings,
+ .extra_init_hw = rts5249_extra_init_hw,
+ .optimize_phy = rts5249_optimize_phy,
+ .turn_on_led = rts5249_turn_on_led,
+ .turn_off_led = rts5249_turn_off_led,
+ .enable_auto_blink = rts5249_enable_auto_blink,
+ .disable_auto_blink = rts5249_disable_auto_blink,
+ .card_power_on = rts5249_card_power_on,
+ .card_power_off = rts5249_card_power_off,
+ .switch_output_voltage = rts5249_switch_output_voltage,
+ .force_power_down = rts5249_force_power_down,
+};
+
+/* SD Pull Control Enable:
+ * SD_DAT[3:0] ==> pull up
+ * SD_CD ==> pull up
+ * SD_WP ==> pull up
+ * SD_CMD ==> pull up
+ * SD_CLK ==> pull down
+ */
+static const u32 rts5249_sd_pull_ctl_enable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL1, 0x66),
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9),
+ RTSX_REG_PAIR(CARD_PULL_CTL4, 0xAA),
+ 0,
+};
+
+/* SD Pull Control Disable:
+ * SD_DAT[3:0] ==> pull down
+ * SD_CD ==> pull up
+ * SD_WP ==> pull down
+ * SD_CMD ==> pull down
+ * SD_CLK ==> pull down
+ */
+static const u32 rts5249_sd_pull_ctl_disable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL1, 0x66),
+ RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5),
+ RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55),
+ 0,
+};
+
+/* MS Pull Control Enable:
+ * MS CD ==> pull up
+ * others ==> pull down
+ */
+static const u32 rts5249_ms_pull_ctl_enable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15),
+ 0,
+};
+
+/* MS Pull Control Disable:
+ * MS CD ==> pull up
+ * others ==> pull down
+ */
+static const u32 rts5249_ms_pull_ctl_disable_tbl[] = {
+ RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55),
+ RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15),
+ 0,
+};
+
+void rts5249_init_params(struct rtsx_pcr *pcr)
+{
+ pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104;
+ pcr->num_slots = 2;
+ pcr->ops = &rts5249_pcr_ops;
+
+ pcr->flags = 0;
+ pcr->card_drive_sel = RTSX_CARD_DRIVE_DEFAULT;
+ pcr->sd30_drive_sel_1v8 = CFG_DRIVER_TYPE_C;
+ pcr->sd30_drive_sel_3v3 = CFG_DRIVER_TYPE_B;
+ pcr->aspm_en = ASPM_L1_EN;
+ pcr->tx_initial_phase = SET_CLOCK_PHASE(1, 29, 16);
+ pcr->rx_initial_phase = SET_CLOCK_PHASE(24, 6, 5);
+
+ pcr->ic_version = rts5249_get_ic_version(pcr);
+ pcr->sd_pull_ctl_enable_tbl = rts5249_sd_pull_ctl_enable_tbl;
+ pcr->sd_pull_ctl_disable_tbl = rts5249_sd_pull_ctl_disable_tbl;
+ pcr->ms_pull_ctl_enable_tbl = rts5249_ms_pull_ctl_enable_tbl;
+ pcr->ms_pull_ctl_disable_tbl = rts5249_ms_pull_ctl_disable_tbl;
+}
diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c
new file mode 100644
index 00000000000..1d15735f9ef
--- /dev/null
+++ b/drivers/mfd/rtsx_pcr.c
@@ -0,0 +1,1368 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * 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, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG <wei_wang@realsil.com.cn>
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/highmem.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/idr.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/rtsx_pci.h>
+#include <asm/unaligned.h>
+
+#include "rtsx_pcr.h"
+
+static bool msi_en = true;
+module_param(msi_en, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(msi_en, "Enable MSI");
+
+static DEFINE_IDR(rtsx_pci_idr);
+static DEFINE_SPINLOCK(rtsx_pci_lock);
+
+static struct mfd_cell rtsx_pcr_cells[] = {
+ [RTSX_SD_CARD] = {
+ .name = DRV_NAME_RTSX_PCI_SDMMC,
+ },
+ [RTSX_MS_CARD] = {
+ .name = DRV_NAME_RTSX_PCI_MS,
+ },
+};
+
+static const struct pci_device_id rtsx_pci_ids[] = {
+ { PCI_DEVICE(0x10EC, 0x5209), PCI_CLASS_OTHERS << 16, 0xFF0000 },
+ { PCI_DEVICE(0x10EC, 0x5229), PCI_CLASS_OTHERS << 16, 0xFF0000 },
+ { PCI_DEVICE(0x10EC, 0x5289), PCI_CLASS_OTHERS << 16, 0xFF0000 },
+ { PCI_DEVICE(0x10EC, 0x5227), PCI_CLASS_OTHERS << 16, 0xFF0000 },
+ { PCI_DEVICE(0x10EC, 0x5249), PCI_CLASS_OTHERS << 16, 0xFF0000 },
+ { PCI_DEVICE(0x10EC, 0x5287), PCI_CLASS_OTHERS << 16, 0xFF0000 },
+ { PCI_DEVICE(0x10EC, 0x5286), PCI_CLASS_OTHERS << 16, 0xFF0000 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, rtsx_pci_ids);
+
+void rtsx_pci_start_run(struct rtsx_pcr *pcr)
+{
+ /* If pci device removed, don't queue idle work any more */
+ if (pcr->remove_pci)
+ return;
+
+ if (pcr->state != PDEV_STAT_RUN) {
+ pcr->state = PDEV_STAT_RUN;
+ if (pcr->ops->enable_auto_blink)
+ pcr->ops->enable_auto_blink(pcr);
+
+ if (pcr->aspm_en)
+ rtsx_pci_write_config_byte(pcr, LCTLR, 0);
+ }
+
+ mod_delayed_work(system_wq, &pcr->idle_work, msecs_to_jiffies(200));
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_start_run);
+
+int rtsx_pci_write_register(struct rtsx_pcr *pcr, u16 addr, u8 mask, u8 data)
+{
+ int i;
+ u32 val = HAIMR_WRITE_START;
+
+ val |= (u32)(addr & 0x3FFF) << 16;
+ val |= (u32)mask << 8;
+ val |= (u32)data;
+
+ rtsx_pci_writel(pcr, RTSX_HAIMR, val);
+
+ for (i = 0; i < MAX_RW_REG_CNT; i++) {
+ val = rtsx_pci_readl(pcr, RTSX_HAIMR);
+ if ((val & HAIMR_TRANS_END) == 0) {
+ if (data != (u8)val)
+ return -EIO;
+ return 0;
+ }
+ }
+
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_write_register);
+
+int rtsx_pci_read_register(struct rtsx_pcr *pcr, u16 addr, u8 *data)
+{
+ u32 val = HAIMR_READ_START;
+ int i;
+
+ val |= (u32)(addr & 0x3FFF) << 16;
+ rtsx_pci_writel(pcr, RTSX_HAIMR, val);
+
+ for (i = 0; i < MAX_RW_REG_CNT; i++) {
+ val = rtsx_pci_readl(pcr, RTSX_HAIMR);
+ if ((val & HAIMR_TRANS_END) == 0)
+ break;
+ }
+
+ if (i >= MAX_RW_REG_CNT)
+ return -ETIMEDOUT;
+
+ if (data)
+ *data = (u8)(val & 0xFF);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_read_register);
+
+int rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val)
+{
+ int err, i, finished = 0;
+ u8 tmp;
+
+ rtsx_pci_init_cmd(pcr);
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYDATA0, 0xFF, (u8)val);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYDATA1, 0xFF, (u8)(val >> 8));
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYADDR, 0xFF, addr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYRWCTL, 0xFF, 0x81);
+
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < 100000; i++) {
+ err = rtsx_pci_read_register(pcr, PHYRWCTL, &tmp);
+ if (err < 0)
+ return err;
+
+ if (!(tmp & 0x80)) {
+ finished = 1;
+ break;
+ }
+ }
+
+ if (!finished)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_write_phy_register);
+
+int rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val)
+{
+ int err, i, finished = 0;
+ u16 data;
+ u8 *ptr, tmp;
+
+ rtsx_pci_init_cmd(pcr);
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYADDR, 0xFF, addr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYRWCTL, 0xFF, 0x80);
+
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < 100000; i++) {
+ err = rtsx_pci_read_register(pcr, PHYRWCTL, &tmp);
+ if (err < 0)
+ return err;
+
+ if (!(tmp & 0x80)) {
+ finished = 1;
+ break;
+ }
+ }
+
+ if (!finished)
+ return -ETIMEDOUT;
+
+ rtsx_pci_init_cmd(pcr);
+
+ rtsx_pci_add_cmd(pcr, READ_REG_CMD, PHYDATA0, 0, 0);
+ rtsx_pci_add_cmd(pcr, READ_REG_CMD, PHYDATA1, 0, 0);
+
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ ptr = rtsx_pci_get_cmd_data(pcr);
+ data = ((u16)ptr[1] << 8) | ptr[0];
+
+ if (val)
+ *val = data;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_read_phy_register);
+
+void rtsx_pci_stop_cmd(struct rtsx_pcr *pcr)
+{
+ rtsx_pci_writel(pcr, RTSX_HCBCTLR, STOP_CMD);
+ rtsx_pci_writel(pcr, RTSX_HDBCTLR, STOP_DMA);
+
+ rtsx_pci_write_register(pcr, DMACTL, 0x80, 0x80);
+ rtsx_pci_write_register(pcr, RBCTL, 0x80, 0x80);
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_stop_cmd);
+
+void rtsx_pci_add_cmd(struct rtsx_pcr *pcr,
+ u8 cmd_type, u16 reg_addr, u8 mask, u8 data)
+{
+ unsigned long flags;
+ u32 val = 0;
+ u32 *ptr = (u32 *)(pcr->host_cmds_ptr);
+
+ val |= (u32)(cmd_type & 0x03) << 30;
+ val |= (u32)(reg_addr & 0x3FFF) << 16;
+ val |= (u32)mask << 8;
+ val |= (u32)data;
+
+ spin_lock_irqsave(&pcr->lock, flags);
+ ptr += pcr->ci;
+ if (pcr->ci < (HOST_CMDS_BUF_LEN / 4)) {
+ put_unaligned_le32(val, ptr);
+ ptr++;
+ pcr->ci++;
+ }
+ spin_unlock_irqrestore(&pcr->lock, flags);
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_add_cmd);
+
+void rtsx_pci_send_cmd_no_wait(struct rtsx_pcr *pcr)
+{
+ u32 val = 1 << 31;
+
+ rtsx_pci_writel(pcr, RTSX_HCBAR, pcr->host_cmds_addr);
+
+ val |= (u32)(pcr->ci * 4) & 0x00FFFFFF;
+ /* Hardware Auto Response */
+ val |= 0x40000000;
+ rtsx_pci_writel(pcr, RTSX_HCBCTLR, val);
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_send_cmd_no_wait);
+
+int rtsx_pci_send_cmd(struct rtsx_pcr *pcr, int timeout)
+{
+ struct completion trans_done;
+ u32 val = 1 << 31;
+ long timeleft;
+ unsigned long flags;
+ int err = 0;
+
+ spin_lock_irqsave(&pcr->lock, flags);
+
+ /* set up data structures for the wakeup system */
+ pcr->done = &trans_done;
+ pcr->trans_result = TRANS_NOT_READY;
+ init_completion(&trans_done);
+
+ rtsx_pci_writel(pcr, RTSX_HCBAR, pcr->host_cmds_addr);
+
+ val |= (u32)(pcr->ci * 4) & 0x00FFFFFF;
+ /* Hardware Auto Response */
+ val |= 0x40000000;
+ rtsx_pci_writel(pcr, RTSX_HCBCTLR, val);
+
+ spin_unlock_irqrestore(&pcr->lock, flags);
+
+ /* Wait for TRANS_OK_INT */
+ timeleft = wait_for_completion_interruptible_timeout(
+ &trans_done, msecs_to_jiffies(timeout));
+ if (timeleft <= 0) {
+ dev_dbg(&(pcr->pci->dev), "Timeout (%s %d)\n",
+ __func__, __LINE__);
+ err = -ETIMEDOUT;
+ goto finish_send_cmd;
+ }
+
+ spin_lock_irqsave(&pcr->lock, flags);
+ if (pcr->trans_result == TRANS_RESULT_FAIL)
+ err = -EINVAL;
+ else if (pcr->trans_result == TRANS_RESULT_OK)
+ err = 0;
+ else if (pcr->trans_result == TRANS_NO_DEVICE)
+ err = -ENODEV;
+ spin_unlock_irqrestore(&pcr->lock, flags);
+
+finish_send_cmd:
+ spin_lock_irqsave(&pcr->lock, flags);
+ pcr->done = NULL;
+ spin_unlock_irqrestore(&pcr->lock, flags);
+
+ if ((err < 0) && (err != -ENODEV))
+ rtsx_pci_stop_cmd(pcr);
+
+ if (pcr->finish_me)
+ complete(pcr->finish_me);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_send_cmd);
+
+static void rtsx_pci_add_sg_tbl(struct rtsx_pcr *pcr,
+ dma_addr_t addr, unsigned int len, int end)
+{
+ u64 *ptr = (u64 *)(pcr->host_sg_tbl_ptr) + pcr->sgi;
+ u64 val;
+ u8 option = SG_VALID | SG_TRANS_DATA;
+
+ dev_dbg(&(pcr->pci->dev), "DMA addr: 0x%x, Len: 0x%x\n",
+ (unsigned int)addr, len);
+
+ if (end)
+ option |= SG_END;
+ val = ((u64)addr << 32) | ((u64)len << 12) | option;
+
+ put_unaligned_le64(val, ptr);
+ pcr->sgi++;
+}
+
+int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist,
+ int num_sg, bool read, int timeout)
+{
+ struct completion trans_done;
+ u8 dir;
+ int err = 0, i, count;
+ long timeleft;
+ unsigned long flags;
+ struct scatterlist *sg;
+ enum dma_data_direction dma_dir;
+ u32 val;
+ dma_addr_t addr;
+ unsigned int len;
+
+ dev_dbg(&(pcr->pci->dev), "--> %s: num_sg = %d\n", __func__, num_sg);
+
+ /* don't transfer data during abort processing */
+ if (pcr->remove_pci)
+ return -EINVAL;
+
+ if ((sglist == NULL) || (num_sg <= 0))
+ return -EINVAL;
+
+ if (read) {
+ dir = DEVICE_TO_HOST;
+ dma_dir = DMA_FROM_DEVICE;
+ } else {
+ dir = HOST_TO_DEVICE;
+ dma_dir = DMA_TO_DEVICE;
+ }
+
+ count = dma_map_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir);
+ if (count < 1) {
+ dev_err(&(pcr->pci->dev), "scatterlist map failed\n");
+ return -EINVAL;
+ }
+ dev_dbg(&(pcr->pci->dev), "DMA mapping count: %d\n", count);
+
+ val = ((u32)(dir & 0x01) << 29) | TRIG_DMA | ADMA_MODE;
+ pcr->sgi = 0;
+ for_each_sg(sglist, sg, count, i) {
+ addr = sg_dma_address(sg);
+ len = sg_dma_len(sg);
+ rtsx_pci_add_sg_tbl(pcr, addr, len, i == count - 1);
+ }
+
+ spin_lock_irqsave(&pcr->lock, flags);
+
+ pcr->done = &trans_done;
+ pcr->trans_result = TRANS_NOT_READY;
+ init_completion(&trans_done);
+ rtsx_pci_writel(pcr, RTSX_HDBAR, pcr->host_sg_tbl_addr);
+ rtsx_pci_writel(pcr, RTSX_HDBCTLR, val);
+
+ spin_unlock_irqrestore(&pcr->lock, flags);
+
+ timeleft = wait_for_completion_interruptible_timeout(
+ &trans_done, msecs_to_jiffies(timeout));
+ if (timeleft <= 0) {
+ dev_dbg(&(pcr->pci->dev), "Timeout (%s %d)\n",
+ __func__, __LINE__);
+ err = -ETIMEDOUT;
+ goto out;
+ }
+
+ spin_lock_irqsave(&pcr->lock, flags);
+
+ if (pcr->trans_result == TRANS_RESULT_FAIL)
+ err = -EINVAL;
+ else if (pcr->trans_result == TRANS_NO_DEVICE)
+ err = -ENODEV;
+
+ spin_unlock_irqrestore(&pcr->lock, flags);
+
+out:
+ spin_lock_irqsave(&pcr->lock, flags);
+ pcr->done = NULL;
+ spin_unlock_irqrestore(&pcr->lock, flags);
+
+ dma_unmap_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir);
+
+ if ((err < 0) && (err != -ENODEV))
+ rtsx_pci_stop_cmd(pcr);
+
+ if (pcr->finish_me)
+ complete(pcr->finish_me);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_transfer_data);
+
+int rtsx_pci_read_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len)
+{
+ int err;
+ int i, j;
+ u16 reg;
+ u8 *ptr;
+
+ if (buf_len > 512)
+ buf_len = 512;
+
+ ptr = buf;
+ reg = PPBUF_BASE2;
+ for (i = 0; i < buf_len / 256; i++) {
+ rtsx_pci_init_cmd(pcr);
+
+ for (j = 0; j < 256; j++)
+ rtsx_pci_add_cmd(pcr, READ_REG_CMD, reg++, 0, 0);
+
+ err = rtsx_pci_send_cmd(pcr, 250);
+ if (err < 0)
+ return err;
+
+ memcpy(ptr, rtsx_pci_get_cmd_data(pcr), 256);
+ ptr += 256;
+ }
+
+ if (buf_len % 256) {
+ rtsx_pci_init_cmd(pcr);
+
+ for (j = 0; j < buf_len % 256; j++)
+ rtsx_pci_add_cmd(pcr, READ_REG_CMD, reg++, 0, 0);
+
+ err = rtsx_pci_send_cmd(pcr, 250);
+ if (err < 0)
+ return err;
+ }
+
+ memcpy(ptr, rtsx_pci_get_cmd_data(pcr), buf_len % 256);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_read_ppbuf);
+
+int rtsx_pci_write_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len)
+{
+ int err;
+ int i, j;
+ u16 reg;
+ u8 *ptr;
+
+ if (buf_len > 512)
+ buf_len = 512;
+
+ ptr = buf;
+ reg = PPBUF_BASE2;
+ for (i = 0; i < buf_len / 256; i++) {
+ rtsx_pci_init_cmd(pcr);
+
+ for (j = 0; j < 256; j++) {
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+ reg++, 0xFF, *ptr);
+ ptr++;
+ }
+
+ err = rtsx_pci_send_cmd(pcr, 250);
+ if (err < 0)
+ return err;
+ }
+
+ if (buf_len % 256) {
+ rtsx_pci_init_cmd(pcr);
+
+ for (j = 0; j < buf_len % 256; j++) {
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+ reg++, 0xFF, *ptr);
+ ptr++;
+ }
+
+ err = rtsx_pci_send_cmd(pcr, 250);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_write_ppbuf);
+
+static int rtsx_pci_set_pull_ctl(struct rtsx_pcr *pcr, const u32 *tbl)
+{
+ int err;
+
+ rtsx_pci_init_cmd(pcr);
+
+ while (*tbl & 0xFFFF0000) {
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+ (u16)(*tbl >> 16), 0xFF, (u8)(*tbl));
+ tbl++;
+ }
+
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+int rtsx_pci_card_pull_ctl_enable(struct rtsx_pcr *pcr, int card)
+{
+ const u32 *tbl;
+
+ if (card == RTSX_SD_CARD)
+ tbl = pcr->sd_pull_ctl_enable_tbl;
+ else if (card == RTSX_MS_CARD)
+ tbl = pcr->ms_pull_ctl_enable_tbl;
+ else
+ return -EINVAL;
+
+ return rtsx_pci_set_pull_ctl(pcr, tbl);
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_card_pull_ctl_enable);
+
+int rtsx_pci_card_pull_ctl_disable(struct rtsx_pcr *pcr, int card)
+{
+ const u32 *tbl;
+
+ if (card == RTSX_SD_CARD)
+ tbl = pcr->sd_pull_ctl_disable_tbl;
+ else if (card == RTSX_MS_CARD)
+ tbl = pcr->ms_pull_ctl_disable_tbl;
+ else
+ return -EINVAL;
+
+
+ return rtsx_pci_set_pull_ctl(pcr, tbl);
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_card_pull_ctl_disable);
+
+static void rtsx_pci_enable_bus_int(struct rtsx_pcr *pcr)
+{
+ pcr->bier = TRANS_OK_INT_EN | TRANS_FAIL_INT_EN | SD_INT_EN;
+
+ if (pcr->num_slots > 1)
+ pcr->bier |= MS_INT_EN;
+
+ /* Enable Bus Interrupt */
+ rtsx_pci_writel(pcr, RTSX_BIER, pcr->bier);
+
+ dev_dbg(&(pcr->pci->dev), "RTSX_BIER: 0x%08x\n", pcr->bier);
+}
+
+static inline u8 double_ssc_depth(u8 depth)
+{
+ return ((depth > 1) ? (depth - 1) : depth);
+}
+
+static u8 revise_ssc_depth(u8 ssc_depth, u8 div)
+{
+ if (div > CLK_DIV_1) {
+ if (ssc_depth > (div - 1))
+ ssc_depth -= (div - 1);
+ else
+ ssc_depth = SSC_DEPTH_4M;
+ }
+
+ return ssc_depth;
+}
+
+int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
+ u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk)
+{
+ int err, clk;
+ u8 n, clk_divider, mcu_cnt, div;
+ u8 depth[] = {
+ [RTSX_SSC_DEPTH_4M] = SSC_DEPTH_4M,
+ [RTSX_SSC_DEPTH_2M] = SSC_DEPTH_2M,
+ [RTSX_SSC_DEPTH_1M] = SSC_DEPTH_1M,
+ [RTSX_SSC_DEPTH_500K] = SSC_DEPTH_500K,
+ [RTSX_SSC_DEPTH_250K] = SSC_DEPTH_250K,
+ };
+
+ if (initial_mode) {
+ /* We use 250k(around) here, in initial stage */
+ clk_divider = SD_CLK_DIVIDE_128;
+ card_clock = 30000000;
+ } else {
+ clk_divider = SD_CLK_DIVIDE_0;
+ }
+ err = rtsx_pci_write_register(pcr, SD_CFG1,
+ SD_CLK_DIVIDE_MASK, clk_divider);
+ if (err < 0)
+ return err;
+
+ card_clock /= 1000000;
+ dev_dbg(&(pcr->pci->dev), "Switch card clock to %dMHz\n", card_clock);
+
+ clk = card_clock;
+ if (!initial_mode && double_clk)
+ clk = card_clock * 2;
+ dev_dbg(&(pcr->pci->dev),
+ "Internal SSC clock: %dMHz (cur_clock = %d)\n",
+ clk, pcr->cur_clock);
+
+ if (clk == pcr->cur_clock)
+ return 0;
+
+ if (pcr->ops->conv_clk_and_div_n)
+ n = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N);
+ else
+ n = (u8)(clk - 2);
+ if ((clk <= 2) || (n > MAX_DIV_N_PCR))
+ return -EINVAL;
+
+ mcu_cnt = (u8)(125/clk + 3);
+ if (mcu_cnt > 15)
+ mcu_cnt = 15;
+
+ /* Make sure that the SSC clock div_n is not less than MIN_DIV_N_PCR */
+ div = CLK_DIV_1;
+ while ((n < MIN_DIV_N_PCR) && (div < CLK_DIV_8)) {
+ if (pcr->ops->conv_clk_and_div_n) {
+ int dbl_clk = pcr->ops->conv_clk_and_div_n(n,
+ DIV_N_TO_CLK) * 2;
+ n = (u8)pcr->ops->conv_clk_and_div_n(dbl_clk,
+ CLK_TO_DIV_N);
+ } else {
+ n = (n + 2) * 2 - 2;
+ }
+ div++;
+ }
+ dev_dbg(&(pcr->pci->dev), "n = %d, div = %d\n", n, div);
+
+ ssc_depth = depth[ssc_depth];
+ if (double_clk)
+ ssc_depth = double_ssc_depth(ssc_depth);
+
+ ssc_depth = revise_ssc_depth(ssc_depth, div);
+ dev_dbg(&(pcr->pci->dev), "ssc_depth = %d\n", ssc_depth);
+
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL,
+ CLK_LOW_FREQ, CLK_LOW_FREQ);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_DIV,
+ 0xFF, (div << 4) | mcu_cnt);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2,
+ SSC_DEPTH_MASK, ssc_depth);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB);
+ if (vpclk) {
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL,
+ PHASE_NOT_RESET, 0);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL,
+ PHASE_NOT_RESET, PHASE_NOT_RESET);
+ }
+
+ err = rtsx_pci_send_cmd(pcr, 2000);
+ if (err < 0)
+ return err;
+
+ /* Wait SSC clock stable */
+ udelay(10);
+ err = rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, 0);
+ if (err < 0)
+ return err;
+
+ pcr->cur_clock = clk;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_switch_clock);
+
+int rtsx_pci_card_power_on(struct rtsx_pcr *pcr, int card)
+{
+ if (pcr->ops->card_power_on)
+ return pcr->ops->card_power_on(pcr, card);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_card_power_on);
+
+int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card)
+{
+ if (pcr->ops->card_power_off)
+ return pcr->ops->card_power_off(pcr, card);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_card_power_off);
+
+int rtsx_pci_card_exclusive_check(struct rtsx_pcr *pcr, int card)
+{
+ unsigned int cd_mask[] = {
+ [RTSX_SD_CARD] = SD_EXIST,
+ [RTSX_MS_CARD] = MS_EXIST
+ };
+
+ if (!(pcr->flags & PCR_MS_PMOS)) {
+ /* When using single PMOS, accessing card is not permitted
+ * if the existing card is not the designated one.
+ */
+ if (pcr->card_exist & (~cd_mask[card]))
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_card_exclusive_check);
+
+int rtsx_pci_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
+{
+ if (pcr->ops->switch_output_voltage)
+ return pcr->ops->switch_output_voltage(pcr, voltage);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_switch_output_voltage);
+
+unsigned int rtsx_pci_card_exist(struct rtsx_pcr *pcr)
+{
+ unsigned int val;
+
+ val = rtsx_pci_readl(pcr, RTSX_BIPR);
+ if (pcr->ops->cd_deglitch)
+ val = pcr->ops->cd_deglitch(pcr);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_card_exist);
+
+void rtsx_pci_complete_unfinished_transfer(struct rtsx_pcr *pcr)
+{
+ struct completion finish;
+
+ pcr->finish_me = &finish;
+ init_completion(&finish);
+
+ if (pcr->done)
+ complete(pcr->done);
+
+ if (!pcr->remove_pci)
+ rtsx_pci_stop_cmd(pcr);
+
+ wait_for_completion_interruptible_timeout(&finish,
+ msecs_to_jiffies(2));
+ pcr->finish_me = NULL;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_complete_unfinished_transfer);
+
+static void rtsx_pci_card_detect(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct rtsx_pcr *pcr;
+ unsigned long flags;
+ unsigned int card_detect = 0, card_inserted, card_removed;
+ u32 irq_status;
+
+ dwork = to_delayed_work(work);
+ pcr = container_of(dwork, struct rtsx_pcr, carddet_work);
+
+ dev_dbg(&(pcr->pci->dev), "--> %s\n", __func__);
+
+ mutex_lock(&pcr->pcr_mutex);
+ spin_lock_irqsave(&pcr->lock, flags);
+
+ irq_status = rtsx_pci_readl(pcr, RTSX_BIPR);
+ dev_dbg(&(pcr->pci->dev), "irq_status: 0x%08x\n", irq_status);
+
+ irq_status &= CARD_EXIST;
+ card_inserted = pcr->card_inserted & irq_status;
+ card_removed = pcr->card_removed;
+ pcr->card_inserted = 0;
+ pcr->card_removed = 0;
+
+ spin_unlock_irqrestore(&pcr->lock, flags);
+
+ if (card_inserted || card_removed) {
+ dev_dbg(&(pcr->pci->dev),
+ "card_inserted: 0x%x, card_removed: 0x%x\n",
+ card_inserted, card_removed);
+
+ if (pcr->ops->cd_deglitch)
+ card_inserted = pcr->ops->cd_deglitch(pcr);
+
+ card_detect = card_inserted | card_removed;
+
+ pcr->card_exist |= card_inserted;
+ pcr->card_exist &= ~card_removed;
+ }
+
+ mutex_unlock(&pcr->pcr_mutex);
+
+ if ((card_detect & SD_EXIST) && pcr->slots[RTSX_SD_CARD].card_event)
+ pcr->slots[RTSX_SD_CARD].card_event(
+ pcr->slots[RTSX_SD_CARD].p_dev);
+ if ((card_detect & MS_EXIST) && pcr->slots[RTSX_MS_CARD].card_event)
+ pcr->slots[RTSX_MS_CARD].card_event(
+ pcr->slots[RTSX_MS_CARD].p_dev);
+}
+
+static irqreturn_t rtsx_pci_isr(int irq, void *dev_id)
+{
+ struct rtsx_pcr *pcr = dev_id;
+ u32 int_reg;
+
+ if (!pcr)
+ return IRQ_NONE;
+
+ spin_lock(&pcr->lock);
+
+ int_reg = rtsx_pci_readl(pcr, RTSX_BIPR);
+ /* Clear interrupt flag */
+ rtsx_pci_writel(pcr, RTSX_BIPR, int_reg);
+ if ((int_reg & pcr->bier) == 0) {
+ spin_unlock(&pcr->lock);
+ return IRQ_NONE;
+ }
+ if (int_reg == 0xFFFFFFFF) {
+ spin_unlock(&pcr->lock);
+ return IRQ_HANDLED;
+ }
+
+ int_reg &= (pcr->bier | 0x7FFFFF);
+
+ if (int_reg & SD_INT) {
+ if (int_reg & SD_EXIST) {
+ pcr->card_inserted |= SD_EXIST;
+ } else {
+ pcr->card_removed |= SD_EXIST;
+ pcr->card_inserted &= ~SD_EXIST;
+ }
+ }
+
+ if (int_reg & MS_INT) {
+ if (int_reg & MS_EXIST) {
+ pcr->card_inserted |= MS_EXIST;
+ } else {
+ pcr->card_removed |= MS_EXIST;
+ pcr->card_inserted &= ~MS_EXIST;
+ }
+ }
+
+ if (int_reg & (NEED_COMPLETE_INT | DELINK_INT)) {
+ if (int_reg & (TRANS_FAIL_INT | DELINK_INT)) {
+ pcr->trans_result = TRANS_RESULT_FAIL;
+ if (pcr->done)
+ complete(pcr->done);
+ } else if (int_reg & TRANS_OK_INT) {
+ pcr->trans_result = TRANS_RESULT_OK;
+ if (pcr->done)
+ complete(pcr->done);
+ }
+ }
+
+ if (pcr->card_inserted || pcr->card_removed)
+ schedule_delayed_work(&pcr->carddet_work,
+ msecs_to_jiffies(200));
+
+ spin_unlock(&pcr->lock);
+ return IRQ_HANDLED;
+}
+
+static int rtsx_pci_acquire_irq(struct rtsx_pcr *pcr)
+{
+ dev_info(&(pcr->pci->dev), "%s: pcr->msi_en = %d, pci->irq = %d\n",
+ __func__, pcr->msi_en, pcr->pci->irq);
+
+ if (request_irq(pcr->pci->irq, rtsx_pci_isr,
+ pcr->msi_en ? 0 : IRQF_SHARED,
+ DRV_NAME_RTSX_PCI, pcr)) {
+ dev_err(&(pcr->pci->dev),
+ "rtsx_sdmmc: unable to grab IRQ %d, disabling device\n",
+ pcr->pci->irq);
+ return -1;
+ }
+
+ pcr->irq = pcr->pci->irq;
+ pci_intx(pcr->pci, !pcr->msi_en);
+
+ return 0;
+}
+
+static void rtsx_pci_idle_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct rtsx_pcr *pcr = container_of(dwork, struct rtsx_pcr, idle_work);
+
+ dev_dbg(&(pcr->pci->dev), "--> %s\n", __func__);
+
+ mutex_lock(&pcr->pcr_mutex);
+
+ pcr->state = PDEV_STAT_IDLE;
+
+ if (pcr->ops->disable_auto_blink)
+ pcr->ops->disable_auto_blink(pcr);
+ if (pcr->ops->turn_off_led)
+ pcr->ops->turn_off_led(pcr);
+
+ if (pcr->aspm_en)
+ rtsx_pci_write_config_byte(pcr, LCTLR, pcr->aspm_en);
+
+ mutex_unlock(&pcr->pcr_mutex);
+}
+
+static void rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state)
+{
+ if (pcr->ops->turn_off_led)
+ pcr->ops->turn_off_led(pcr);
+
+ rtsx_pci_writel(pcr, RTSX_BIER, 0);
+ pcr->bier = 0;
+
+ rtsx_pci_write_register(pcr, PETXCFG, 0x08, 0x08);
+ rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, pm_state);
+
+ if (pcr->ops->force_power_down)
+ pcr->ops->force_power_down(pcr, pm_state);
+}
+
+static int rtsx_pci_init_hw(struct rtsx_pcr *pcr)
+{
+ int err;
+
+ rtsx_pci_writel(pcr, RTSX_HCBAR, pcr->host_cmds_addr);
+
+ rtsx_pci_enable_bus_int(pcr);
+
+ /* Power on SSC */
+ err = rtsx_pci_write_register(pcr, FPDCTL, SSC_POWER_DOWN, 0);
+ if (err < 0)
+ return err;
+
+ /* Wait SSC power stable */
+ udelay(200);
+
+ if (pcr->ops->optimize_phy) {
+ err = pcr->ops->optimize_phy(pcr);
+ if (err < 0)
+ return err;
+ }
+
+ rtsx_pci_init_cmd(pcr);
+
+ /* Set mcu_cnt to 7 to ensure data can be sampled properly */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_DIV, 0x07, 0x07);
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, HOST_SLEEP_STATE, 0x03, 0x00);
+ /* Disable card clock */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, 0x1E, 0);
+ /* Reset delink mode */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CHANGE_LINK_STATE, 0x0A, 0);
+ /* Card driving select */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DRIVE_SEL,
+ 0xFF, pcr->card_drive_sel);
+ /* Enable SSC Clock */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1,
+ 0xFF, SSC_8X_EN | SSC_SEL_4M);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, 0x12);
+ /* Disable cd_pwr_save */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CHANGE_LINK_STATE, 0x16, 0x10);
+ /* Clear Link Ready Interrupt */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0,
+ LINK_RDY_INT, LINK_RDY_INT);
+ /* Enlarge the estimation window of PERST# glitch
+ * to reduce the chance of invalid card interrupt
+ */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PERST_GLITCH_WIDTH, 0xFF, 0x80);
+ /* Update RC oscillator to 400k
+ * bit[0] F_HIGH: for RC oscillator, Rst_value is 1'b1
+ * 1: 2M 0: 400k
+ */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RCCTL, 0x01, 0x00);
+ /* Set interrupt write clear
+ * bit 1: U_elbi_if_rd_clr_en
+ * 1: Enable ELBI interrupt[31:22] & [7:0] flag read clear
+ * 0: ELBI interrupt flag[31:22] & [7:0] only can be write clear
+ */
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, NFTS_TX_CTRL, 0x02, 0);
+
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ rtsx_pci_write_config_byte(pcr, LCTLR, 0);
+
+ /* Enable clk_request_n to enable clock power management */
+ rtsx_pci_write_config_byte(pcr, 0x81, 1);
+ /* Enter L1 when host tx idle */
+ rtsx_pci_write_config_byte(pcr, 0x70F, 0x5B);
+
+ if (pcr->ops->extra_init_hw) {
+ err = pcr->ops->extra_init_hw(pcr);
+ if (err < 0)
+ return err;
+ }
+
+ /* No CD interrupt if probing driver with card inserted.
+ * So we need to initialize pcr->card_exist here.
+ */
+ if (pcr->ops->cd_deglitch)
+ pcr->card_exist = pcr->ops->cd_deglitch(pcr);
+ else
+ pcr->card_exist = rtsx_pci_readl(pcr, RTSX_BIPR) & CARD_EXIST;
+
+ return 0;
+}
+
+static int rtsx_pci_init_chip(struct rtsx_pcr *pcr)
+{
+ int err;
+
+ spin_lock_init(&pcr->lock);
+ mutex_init(&pcr->pcr_mutex);
+
+ switch (PCI_PID(pcr)) {
+ default:
+ case 0x5209:
+ rts5209_init_params(pcr);
+ break;
+
+ case 0x5229:
+ rts5229_init_params(pcr);
+ break;
+
+ case 0x5289:
+ rtl8411_init_params(pcr);
+ break;
+
+ case 0x5227:
+ rts5227_init_params(pcr);
+ break;
+
+ case 0x5249:
+ rts5249_init_params(pcr);
+ break;
+
+ case 0x5287:
+ rtl8411b_init_params(pcr);
+ break;
+
+ case 0x5286:
+ rtl8402_init_params(pcr);
+ break;
+ }
+
+ dev_dbg(&(pcr->pci->dev), "PID: 0x%04x, IC version: 0x%02x\n",
+ PCI_PID(pcr), pcr->ic_version);
+
+ pcr->slots = kcalloc(pcr->num_slots, sizeof(struct rtsx_slot),
+ GFP_KERNEL);
+ if (!pcr->slots)
+ return -ENOMEM;
+
+ if (pcr->ops->fetch_vendor_settings)
+ pcr->ops->fetch_vendor_settings(pcr);
+
+ dev_dbg(&(pcr->pci->dev), "pcr->aspm_en = 0x%x\n", pcr->aspm_en);
+ dev_dbg(&(pcr->pci->dev), "pcr->sd30_drive_sel_1v8 = 0x%x\n",
+ pcr->sd30_drive_sel_1v8);
+ dev_dbg(&(pcr->pci->dev), "pcr->sd30_drive_sel_3v3 = 0x%x\n",
+ pcr->sd30_drive_sel_3v3);
+ dev_dbg(&(pcr->pci->dev), "pcr->card_drive_sel = 0x%x\n",
+ pcr->card_drive_sel);
+ dev_dbg(&(pcr->pci->dev), "pcr->flags = 0x%x\n", pcr->flags);
+
+ pcr->state = PDEV_STAT_IDLE;
+ err = rtsx_pci_init_hw(pcr);
+ if (err < 0) {
+ kfree(pcr->slots);
+ return err;
+ }
+
+ return 0;
+}
+
+static int rtsx_pci_probe(struct pci_dev *pcidev,
+ const struct pci_device_id *id)
+{
+ struct rtsx_pcr *pcr;
+ struct pcr_handle *handle;
+ u32 base, len;
+ int ret, i;
+
+ dev_dbg(&(pcidev->dev),
+ ": Realtek PCI-E Card Reader found at %s [%04x:%04x] (rev %x)\n",
+ pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device,
+ (int)pcidev->revision);
+
+ ret = pci_set_dma_mask(pcidev, DMA_BIT_MASK(32));
+ if (ret < 0)
+ return ret;
+
+ ret = pci_enable_device(pcidev);
+ if (ret)
+ return ret;
+
+ ret = pci_request_regions(pcidev, DRV_NAME_RTSX_PCI);
+ if (ret)
+ goto disable;
+
+ pcr = kzalloc(sizeof(*pcr), GFP_KERNEL);
+ if (!pcr) {
+ ret = -ENOMEM;
+ goto release_pci;
+ }
+
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ if (!handle) {
+ ret = -ENOMEM;
+ goto free_pcr;
+ }
+ handle->pcr = pcr;
+
+ idr_preload(GFP_KERNEL);
+ spin_lock(&rtsx_pci_lock);
+ ret = idr_alloc(&rtsx_pci_idr, pcr, 0, 0, GFP_NOWAIT);
+ if (ret >= 0)
+ pcr->id = ret;
+ spin_unlock(&rtsx_pci_lock);
+ idr_preload_end();
+ if (ret < 0)
+ goto free_handle;
+
+ pcr->pci = pcidev;
+ dev_set_drvdata(&pcidev->dev, handle);
+
+ len = pci_resource_len(pcidev, 0);
+ base = pci_resource_start(pcidev, 0);
+ pcr->remap_addr = ioremap_nocache(base, len);
+ if (!pcr->remap_addr) {
+ ret = -ENOMEM;
+ goto free_handle;
+ }
+
+ pcr->rtsx_resv_buf = dma_alloc_coherent(&(pcidev->dev),
+ RTSX_RESV_BUF_LEN, &(pcr->rtsx_resv_buf_addr),
+ GFP_KERNEL);
+ if (pcr->rtsx_resv_buf == NULL) {
+ ret = -ENXIO;
+ goto unmap;
+ }
+ pcr->host_cmds_ptr = pcr->rtsx_resv_buf;
+ pcr->host_cmds_addr = pcr->rtsx_resv_buf_addr;
+ pcr->host_sg_tbl_ptr = pcr->rtsx_resv_buf + HOST_CMDS_BUF_LEN;
+ pcr->host_sg_tbl_addr = pcr->rtsx_resv_buf_addr + HOST_CMDS_BUF_LEN;
+
+ pcr->card_inserted = 0;
+ pcr->card_removed = 0;
+ INIT_DELAYED_WORK(&pcr->carddet_work, rtsx_pci_card_detect);
+ INIT_DELAYED_WORK(&pcr->idle_work, rtsx_pci_idle_work);
+
+ pcr->msi_en = msi_en;
+ if (pcr->msi_en) {
+ ret = pci_enable_msi(pcidev);
+ if (ret < 0)
+ pcr->msi_en = false;
+ }
+
+ ret = rtsx_pci_acquire_irq(pcr);
+ if (ret < 0)
+ goto disable_msi;
+
+ pci_set_master(pcidev);
+ synchronize_irq(pcr->irq);
+
+ ret = rtsx_pci_init_chip(pcr);
+ if (ret < 0)
+ goto disable_irq;
+
+ for (i = 0; i < ARRAY_SIZE(rtsx_pcr_cells); i++) {
+ rtsx_pcr_cells[i].platform_data = handle;
+ rtsx_pcr_cells[i].pdata_size = sizeof(*handle);
+ }
+ ret = mfd_add_devices(&pcidev->dev, pcr->id, rtsx_pcr_cells,
+ ARRAY_SIZE(rtsx_pcr_cells), NULL, 0, NULL);
+ if (ret < 0)
+ goto disable_irq;
+
+ schedule_delayed_work(&pcr->idle_work, msecs_to_jiffies(200));
+
+ return 0;
+
+disable_irq:
+ free_irq(pcr->irq, (void *)pcr);
+disable_msi:
+ if (pcr->msi_en)
+ pci_disable_msi(pcr->pci);
+ dma_free_coherent(&(pcr->pci->dev), RTSX_RESV_BUF_LEN,
+ pcr->rtsx_resv_buf, pcr->rtsx_resv_buf_addr);
+unmap:
+ iounmap(pcr->remap_addr);
+free_handle:
+ kfree(handle);
+free_pcr:
+ kfree(pcr);
+release_pci:
+ pci_release_regions(pcidev);
+disable:
+ pci_disable_device(pcidev);
+
+ return ret;
+}
+
+static void rtsx_pci_remove(struct pci_dev *pcidev)
+{
+ struct pcr_handle *handle = pci_get_drvdata(pcidev);
+ struct rtsx_pcr *pcr = handle->pcr;
+
+ pcr->remove_pci = true;
+
+ /* Disable interrupts at the pcr level */
+ spin_lock_irq(&pcr->lock);
+ rtsx_pci_writel(pcr, RTSX_BIER, 0);
+ pcr->bier = 0;
+ spin_unlock_irq(&pcr->lock);
+
+ cancel_delayed_work_sync(&pcr->carddet_work);
+ cancel_delayed_work_sync(&pcr->idle_work);
+
+ mfd_remove_devices(&pcidev->dev);
+
+ dma_free_coherent(&(pcr->pci->dev), RTSX_RESV_BUF_LEN,
+ pcr->rtsx_resv_buf, pcr->rtsx_resv_buf_addr);
+ free_irq(pcr->irq, (void *)pcr);
+ if (pcr->msi_en)
+ pci_disable_msi(pcr->pci);
+ iounmap(pcr->remap_addr);
+
+ pci_release_regions(pcidev);
+ pci_disable_device(pcidev);
+
+ spin_lock(&rtsx_pci_lock);
+ idr_remove(&rtsx_pci_idr, pcr->id);
+ spin_unlock(&rtsx_pci_lock);
+
+ kfree(pcr->slots);
+ kfree(pcr);
+ kfree(handle);
+
+ dev_dbg(&(pcidev->dev),
+ ": Realtek PCI-E Card Reader at %s [%04x:%04x] has been removed\n",
+ pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device);
+}
+
+#ifdef CONFIG_PM
+
+static int rtsx_pci_suspend(struct pci_dev *pcidev, pm_message_t state)
+{
+ struct pcr_handle *handle;
+ struct rtsx_pcr *pcr;
+
+ dev_dbg(&(pcidev->dev), "--> %s\n", __func__);
+
+ handle = pci_get_drvdata(pcidev);
+ pcr = handle->pcr;
+
+ cancel_delayed_work(&pcr->carddet_work);
+ cancel_delayed_work(&pcr->idle_work);
+
+ mutex_lock(&pcr->pcr_mutex);
+
+ rtsx_pci_power_off(pcr, HOST_ENTER_S3);
+
+ pci_save_state(pcidev);
+ pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0);
+ pci_disable_device(pcidev);
+ pci_set_power_state(pcidev, pci_choose_state(pcidev, state));
+
+ mutex_unlock(&pcr->pcr_mutex);
+ return 0;
+}
+
+static int rtsx_pci_resume(struct pci_dev *pcidev)
+{
+ struct pcr_handle *handle;
+ struct rtsx_pcr *pcr;
+ int ret = 0;
+
+ dev_dbg(&(pcidev->dev), "--> %s\n", __func__);
+
+ handle = pci_get_drvdata(pcidev);
+ pcr = handle->pcr;
+
+ mutex_lock(&pcr->pcr_mutex);
+
+ pci_set_power_state(pcidev, PCI_D0);
+ pci_restore_state(pcidev);
+ ret = pci_enable_device(pcidev);
+ if (ret)
+ goto out;
+ pci_set_master(pcidev);
+
+ ret = rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, 0x00);
+ if (ret)
+ goto out;
+
+ ret = rtsx_pci_init_hw(pcr);
+ if (ret)
+ goto out;
+
+ schedule_delayed_work(&pcr->idle_work, msecs_to_jiffies(200));
+
+out:
+ mutex_unlock(&pcr->pcr_mutex);
+ return ret;
+}
+
+static void rtsx_pci_shutdown(struct pci_dev *pcidev)
+{
+ struct pcr_handle *handle;
+ struct rtsx_pcr *pcr;
+
+ dev_dbg(&(pcidev->dev), "--> %s\n", __func__);
+
+ handle = pci_get_drvdata(pcidev);
+ pcr = handle->pcr;
+ rtsx_pci_power_off(pcr, HOST_ENTER_S1);
+
+ pci_disable_device(pcidev);
+}
+
+#else /* CONFIG_PM */
+
+#define rtsx_pci_suspend NULL
+#define rtsx_pci_resume NULL
+#define rtsx_pci_shutdown NULL
+
+#endif /* CONFIG_PM */
+
+static struct pci_driver rtsx_pci_driver = {
+ .name = DRV_NAME_RTSX_PCI,
+ .id_table = rtsx_pci_ids,
+ .probe = rtsx_pci_probe,
+ .remove = rtsx_pci_remove,
+ .suspend = rtsx_pci_suspend,
+ .resume = rtsx_pci_resume,
+ .shutdown = rtsx_pci_shutdown,
+};
+module_pci_driver(rtsx_pci_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Wei WANG <wei_wang@realsil.com.cn>");
+MODULE_DESCRIPTION("Realtek PCI-E Card Reader Driver");
diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h
new file mode 100644
index 00000000000..07e4c2ebf05
--- /dev/null
+++ b/drivers/mfd/rtsx_pcr.h
@@ -0,0 +1,75 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * 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, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG <wei_wang@realsil.com.cn>
+ */
+
+#ifndef __RTSX_PCR_H
+#define __RTSX_PCR_H
+
+#include <linux/mfd/rtsx_pci.h>
+
+#define MIN_DIV_N_PCR 80
+#define MAX_DIV_N_PCR 208
+
+void rts5209_init_params(struct rtsx_pcr *pcr);
+void rts5229_init_params(struct rtsx_pcr *pcr);
+void rtl8411_init_params(struct rtsx_pcr *pcr);
+void rtl8402_init_params(struct rtsx_pcr *pcr);
+void rts5227_init_params(struct rtsx_pcr *pcr);
+void rts5249_init_params(struct rtsx_pcr *pcr);
+void rtl8411b_init_params(struct rtsx_pcr *pcr);
+
+static inline u8 map_sd_drive(int idx)
+{
+ u8 sd_drive[4] = {
+ 0x01, /* Type D */
+ 0x02, /* Type C */
+ 0x05, /* Type A */
+ 0x03 /* Type B */
+ };
+
+ return sd_drive[idx];
+}
+
+#define rtsx_vendor_setting_valid(reg) (!((reg) & 0x1000000))
+#define rts5209_vendor_setting1_valid(reg) (!((reg) & 0x80))
+#define rts5209_vendor_setting2_valid(reg) ((reg) & 0x80)
+
+#define rtsx_reg_to_aspm(reg) (((reg) >> 28) & 0x03)
+#define rtsx_reg_to_sd30_drive_sel_1v8(reg) (((reg) >> 26) & 0x03)
+#define rtsx_reg_to_sd30_drive_sel_3v3(reg) (((reg) >> 5) & 0x03)
+#define rtsx_reg_to_card_drive_sel(reg) ((((reg) >> 25) & 0x01) << 6)
+#define rtsx_reg_check_reverse_socket(reg) ((reg) & 0x4000)
+#define rts5209_reg_to_aspm(reg) (((reg) >> 5) & 0x03)
+#define rts5209_reg_check_ms_pmos(reg) (!((reg) & 0x08))
+#define rts5209_reg_to_sd30_drive_sel_1v8(reg) (((reg) >> 3) & 0x07)
+#define rts5209_reg_to_sd30_drive_sel_3v3(reg) ((reg) & 0x07)
+#define rts5209_reg_to_card_drive_sel(reg) ((reg) >> 8)
+#define rtl8411_reg_to_sd30_drive_sel_3v3(reg) (((reg) >> 5) & 0x07)
+#define rtl8411b_reg_to_sd30_drive_sel_3v3(reg) ((reg) & 0x03)
+
+#define set_pull_ctrl_tables(pcr, __device) \
+do { \
+ pcr->sd_pull_ctl_enable_tbl = __device##_sd_pull_ctl_enable_tbl; \
+ pcr->sd_pull_ctl_disable_tbl = __device##_sd_pull_ctl_disable_tbl; \
+ pcr->ms_pull_ctl_enable_tbl = __device##_ms_pull_ctl_enable_tbl; \
+ pcr->ms_pull_ctl_disable_tbl = __device##_ms_pull_ctl_disable_tbl; \
+} while (0)
+
+#endif
diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c
new file mode 100644
index 00000000000..6352bec8419
--- /dev/null
+++ b/drivers/mfd/rtsx_usb.c
@@ -0,0 +1,766 @@
+/* Driver for Realtek USB card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Roger Tseng <rogerable@realtek.com>
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/usb.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/rtsx_usb.h>
+
+static int polling_pipe = 1;
+module_param(polling_pipe, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(polling_pipe, "polling pipe (0: ctl, 1: bulk)");
+
+static const struct mfd_cell rtsx_usb_cells[] = {
+ [RTSX_USB_SD_CARD] = {
+ .name = "rtsx_usb_sdmmc",
+ .pdata_size = 0,
+ },
+ [RTSX_USB_MS_CARD] = {
+ .name = "rtsx_usb_ms",
+ .pdata_size = 0,
+ },
+};
+
+static void rtsx_usb_sg_timed_out(unsigned long data)
+{
+ struct rtsx_ucr *ucr = (struct rtsx_ucr *)data;
+
+ dev_dbg(&ucr->pusb_intf->dev, "%s: sg transfer timed out", __func__);
+ usb_sg_cancel(&ucr->current_sg);
+
+ /* we know the cancellation is caused by time-out */
+ ucr->current_sg.status = -ETIMEDOUT;
+}
+
+static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr,
+ unsigned int pipe, struct scatterlist *sg, int num_sg,
+ unsigned int length, unsigned int *act_len, int timeout)
+{
+ int ret;
+
+ dev_dbg(&ucr->pusb_intf->dev, "%s: xfer %u bytes, %d entries\n",
+ __func__, length, num_sg);
+ ret = usb_sg_init(&ucr->current_sg, ucr->pusb_dev, pipe, 0,
+ sg, num_sg, length, GFP_NOIO);
+ if (ret)
+ return ret;
+
+ ucr->sg_timer.expires = jiffies + msecs_to_jiffies(timeout);
+ add_timer(&ucr->sg_timer);
+ usb_sg_wait(&ucr->current_sg);
+ del_timer_sync(&ucr->sg_timer);
+
+ if (act_len)
+ *act_len = ucr->current_sg.bytes;
+
+ return ucr->current_sg.status;
+}
+
+int rtsx_usb_transfer_data(struct rtsx_ucr *ucr, unsigned int pipe,
+ void *buf, unsigned int len, int num_sg,
+ unsigned int *act_len, int timeout)
+{
+ if (timeout < 600)
+ timeout = 600;
+
+ if (num_sg)
+ return rtsx_usb_bulk_transfer_sglist(ucr, pipe,
+ (struct scatterlist *)buf, num_sg, len, act_len,
+ timeout);
+ else
+ return usb_bulk_msg(ucr->pusb_dev, pipe, buf, len, act_len,
+ timeout);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_transfer_data);
+
+static inline void rtsx_usb_seq_cmd_hdr(struct rtsx_ucr *ucr,
+ u16 addr, u16 len, u8 seq_type)
+{
+ rtsx_usb_cmd_hdr_tag(ucr);
+
+ ucr->cmd_buf[PACKET_TYPE] = seq_type;
+ ucr->cmd_buf[5] = (u8)(len >> 8);
+ ucr->cmd_buf[6] = (u8)len;
+ ucr->cmd_buf[8] = (u8)(addr >> 8);
+ ucr->cmd_buf[9] = (u8)addr;
+
+ if (seq_type == SEQ_WRITE)
+ ucr->cmd_buf[STAGE_FLAG] = 0;
+ else
+ ucr->cmd_buf[STAGE_FLAG] = STAGE_R;
+}
+
+static int rtsx_usb_seq_write_register(struct rtsx_ucr *ucr,
+ u16 addr, u16 len, u8 *data)
+{
+ u16 cmd_len = ALIGN(SEQ_WRITE_DATA_OFFSET + len, 4);
+
+ if (!data)
+ return -EINVAL;
+
+ if (cmd_len > IOBUF_SIZE)
+ return -EINVAL;
+
+ rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_WRITE);
+ memcpy(ucr->cmd_buf + SEQ_WRITE_DATA_OFFSET, data, len);
+
+ return rtsx_usb_transfer_data(ucr,
+ usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT),
+ ucr->cmd_buf, cmd_len, 0, NULL, 100);
+}
+
+static int rtsx_usb_seq_read_register(struct rtsx_ucr *ucr,
+ u16 addr, u16 len, u8 *data)
+{
+ int i, ret;
+ u16 rsp_len = round_down(len, 4);
+ u16 res_len = len - rsp_len;
+
+ if (!data)
+ return -EINVAL;
+
+ /* 4-byte aligned part */
+ if (rsp_len) {
+ rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_READ);
+ ret = rtsx_usb_transfer_data(ucr,
+ usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT),
+ ucr->cmd_buf, 12, 0, NULL, 100);
+ if (ret)
+ return ret;
+
+ ret = rtsx_usb_transfer_data(ucr,
+ usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN),
+ data, rsp_len, 0, NULL, 100);
+ if (ret)
+ return ret;
+ }
+
+ /* unaligned part */
+ for (i = 0; i < res_len; i++) {
+ ret = rtsx_usb_read_register(ucr, addr + rsp_len + i,
+ data + rsp_len + i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int rtsx_usb_read_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len)
+{
+ return rtsx_usb_seq_read_register(ucr, PPBUF_BASE2, (u16)buf_len, buf);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_read_ppbuf);
+
+int rtsx_usb_write_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len)
+{
+ return rtsx_usb_seq_write_register(ucr, PPBUF_BASE2, (u16)buf_len, buf);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_write_ppbuf);
+
+int rtsx_usb_ep0_write_register(struct rtsx_ucr *ucr, u16 addr,
+ u8 mask, u8 data)
+{
+ u16 value, index;
+
+ addr |= EP0_WRITE_REG_CMD << EP0_OP_SHIFT;
+ value = swab16(addr);
+ index = mask | data << 8;
+
+ return usb_control_msg(ucr->pusb_dev,
+ usb_sndctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 100);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_ep0_write_register);
+
+int rtsx_usb_ep0_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data)
+{
+ u16 value;
+
+ if (!data)
+ return -EINVAL;
+ *data = 0;
+
+ addr |= EP0_READ_REG_CMD << EP0_OP_SHIFT;
+ value = swab16(addr);
+
+ return usb_control_msg(ucr->pusb_dev,
+ usb_rcvctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, 0, data, 1, 100);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_ep0_read_register);
+
+void rtsx_usb_add_cmd(struct rtsx_ucr *ucr, u8 cmd_type, u16 reg_addr,
+ u8 mask, u8 data)
+{
+ int i;
+
+ if (ucr->cmd_idx < (IOBUF_SIZE - CMD_OFFSET) / 4) {
+ i = CMD_OFFSET + ucr->cmd_idx * 4;
+
+ ucr->cmd_buf[i++] = ((cmd_type & 0x03) << 6) |
+ (u8)((reg_addr >> 8) & 0x3F);
+ ucr->cmd_buf[i++] = (u8)reg_addr;
+ ucr->cmd_buf[i++] = mask;
+ ucr->cmd_buf[i++] = data;
+
+ ucr->cmd_idx++;
+ }
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_add_cmd);
+
+int rtsx_usb_send_cmd(struct rtsx_ucr *ucr, u8 flag, int timeout)
+{
+ int ret;
+
+ ucr->cmd_buf[CNT_H] = (u8)(ucr->cmd_idx >> 8);
+ ucr->cmd_buf[CNT_L] = (u8)(ucr->cmd_idx);
+ ucr->cmd_buf[STAGE_FLAG] = flag;
+
+ ret = rtsx_usb_transfer_data(ucr,
+ usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT),
+ ucr->cmd_buf, ucr->cmd_idx * 4 + CMD_OFFSET,
+ 0, NULL, timeout);
+ if (ret) {
+ rtsx_usb_clear_fsm_err(ucr);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_send_cmd);
+
+int rtsx_usb_get_rsp(struct rtsx_ucr *ucr, int rsp_len, int timeout)
+{
+ if (rsp_len <= 0)
+ return -EINVAL;
+
+ rsp_len = ALIGN(rsp_len, 4);
+
+ return rtsx_usb_transfer_data(ucr,
+ usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN),
+ ucr->rsp_buf, rsp_len, 0, NULL, timeout);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_get_rsp);
+
+static int rtsx_usb_get_status_with_bulk(struct rtsx_ucr *ucr, u16 *status)
+{
+ int ret;
+
+ rtsx_usb_init_cmd(ucr);
+ rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_EXIST, 0x00, 0x00);
+ rtsx_usb_add_cmd(ucr, READ_REG_CMD, OCPSTAT, 0x00, 0x00);
+ ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100);
+ if (ret)
+ return ret;
+
+ ret = rtsx_usb_get_rsp(ucr, 2, 100);
+ if (ret)
+ return ret;
+
+ *status = ((ucr->rsp_buf[0] >> 2) & 0x0f) |
+ ((ucr->rsp_buf[1] & 0x03) << 4);
+
+ return 0;
+}
+
+int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status)
+{
+ int ret;
+
+ if (!status)
+ return -EINVAL;
+
+ if (polling_pipe == 0)
+ ret = usb_control_msg(ucr->pusb_dev,
+ usb_rcvctrlpipe(ucr->pusb_dev, 0),
+ RTSX_USB_REQ_POLL,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, status, 2, 100);
+ else
+ ret = rtsx_usb_get_status_with_bulk(ucr, status);
+
+ /* usb_control_msg may return positive when success */
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_get_card_status);
+
+static int rtsx_usb_write_phy_register(struct rtsx_ucr *ucr, u8 addr, u8 val)
+{
+ dev_dbg(&ucr->pusb_intf->dev, "Write 0x%x to phy register 0x%x\n",
+ val, addr);
+
+ rtsx_usb_init_cmd(ucr);
+
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VSTAIN, 0xFF, val);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, 0xFF, addr & 0x0F);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL,
+ 0xFF, (addr >> 4) & 0x0F);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01);
+
+ return rtsx_usb_send_cmd(ucr, MODE_C, 100);
+}
+
+int rtsx_usb_write_register(struct rtsx_ucr *ucr, u16 addr, u8 mask, u8 data)
+{
+ rtsx_usb_init_cmd(ucr);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, addr, mask, data);
+ return rtsx_usb_send_cmd(ucr, MODE_C, 100);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_write_register);
+
+int rtsx_usb_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data)
+{
+ int ret;
+
+ if (data != NULL)
+ *data = 0;
+
+ rtsx_usb_init_cmd(ucr);
+ rtsx_usb_add_cmd(ucr, READ_REG_CMD, addr, 0, 0);
+ ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100);
+ if (ret)
+ return ret;
+
+ ret = rtsx_usb_get_rsp(ucr, 1, 100);
+ if (ret)
+ return ret;
+
+ if (data != NULL)
+ *data = ucr->rsp_buf[0];
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_read_register);
+
+static inline u8 double_ssc_depth(u8 depth)
+{
+ return (depth > 1) ? (depth - 1) : depth;
+}
+
+static u8 revise_ssc_depth(u8 ssc_depth, u8 div)
+{
+ if (div > CLK_DIV_1) {
+ if (ssc_depth > div - 1)
+ ssc_depth -= (div - 1);
+ else
+ ssc_depth = SSC_DEPTH_2M;
+ }
+
+ return ssc_depth;
+}
+
+int rtsx_usb_switch_clock(struct rtsx_ucr *ucr, unsigned int card_clock,
+ u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk)
+{
+ int ret;
+ u8 n, clk_divider, mcu_cnt, div;
+
+ if (!card_clock) {
+ ucr->cur_clk = 0;
+ return 0;
+ }
+
+ if (initial_mode) {
+ /* We use 250k(around) here, in initial stage */
+ clk_divider = SD_CLK_DIVIDE_128;
+ card_clock = 30000000;
+ } else {
+ clk_divider = SD_CLK_DIVIDE_0;
+ }
+
+ ret = rtsx_usb_write_register(ucr, SD_CFG1,
+ SD_CLK_DIVIDE_MASK, clk_divider);
+ if (ret < 0)
+ return ret;
+
+ card_clock /= 1000000;
+ dev_dbg(&ucr->pusb_intf->dev,
+ "Switch card clock to %dMHz\n", card_clock);
+
+ if (!initial_mode && double_clk)
+ card_clock *= 2;
+ dev_dbg(&ucr->pusb_intf->dev,
+ "Internal SSC clock: %dMHz (cur_clk = %d)\n",
+ card_clock, ucr->cur_clk);
+
+ if (card_clock == ucr->cur_clk)
+ return 0;
+
+ /* Converting clock value into internal settings: n and div */
+ n = card_clock - 2;
+ if ((card_clock <= 2) || (n > MAX_DIV_N))
+ return -EINVAL;
+
+ mcu_cnt = 60/card_clock + 3;
+ if (mcu_cnt > 15)
+ mcu_cnt = 15;
+
+ /* Make sure that the SSC clock div_n is not less than MIN_DIV_N */
+
+ div = CLK_DIV_1;
+ while (n < MIN_DIV_N && div < CLK_DIV_4) {
+ n = (n + 2) * 2 - 2;
+ div++;
+ }
+ dev_dbg(&ucr->pusb_intf->dev, "n = %d, div = %d\n", n, div);
+
+ if (double_clk)
+ ssc_depth = double_ssc_depth(ssc_depth);
+
+ ssc_depth = revise_ssc_depth(ssc_depth, div);
+ dev_dbg(&ucr->pusb_intf->dev, "ssc_depth = %d\n", ssc_depth);
+
+ rtsx_usb_init_cmd(ucr);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, CLK_CHANGE, CLK_CHANGE);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV,
+ 0x3F, (div << 4) | mcu_cnt);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL2,
+ SSC_DEPTH_MASK, ssc_depth);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB);
+ if (vpclk) {
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL,
+ PHASE_NOT_RESET, 0);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL,
+ PHASE_NOT_RESET, PHASE_NOT_RESET);
+ }
+
+ ret = rtsx_usb_send_cmd(ucr, MODE_C, 2000);
+ if (ret < 0)
+ return ret;
+
+ ret = rtsx_usb_write_register(ucr, SSC_CTL1, 0xff,
+ SSC_RSTB | SSC_8X_EN | SSC_SEL_4M);
+ if (ret < 0)
+ return ret;
+
+ /* Wait SSC clock stable */
+ usleep_range(100, 1000);
+
+ ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0);
+ if (ret < 0)
+ return ret;
+
+ ucr->cur_clk = card_clock;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_switch_clock);
+
+int rtsx_usb_card_exclusive_check(struct rtsx_ucr *ucr, int card)
+{
+ int ret;
+ u16 val;
+ u16 cd_mask[] = {
+ [RTSX_USB_SD_CARD] = (CD_MASK & ~SD_CD),
+ [RTSX_USB_MS_CARD] = (CD_MASK & ~MS_CD)
+ };
+
+ ret = rtsx_usb_get_card_status(ucr, &val);
+ /*
+ * If get status fails, return 0 (ok) for the exclusive check
+ * and let the flow fail at somewhere else.
+ */
+ if (ret)
+ return 0;
+
+ if (val & cd_mask[card])
+ return -EIO;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_card_exclusive_check);
+
+static int rtsx_usb_reset_chip(struct rtsx_ucr *ucr)
+{
+ int ret;
+ u8 val;
+
+ rtsx_usb_init_cmd(ucr);
+
+ if (CHECK_PKG(ucr, LQFP48)) {
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
+ LDO3318_PWR_MASK, LDO_SUSPEND);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
+ FORCE_LDO_POWERB, FORCE_LDO_POWERB);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1,
+ 0x30, 0x10);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5,
+ 0x03, 0x01);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6,
+ 0x0C, 0x04);
+ }
+
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SYS_DUMMY0, NYET_MSAK, NYET_EN);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CD_DEGLITCH_WIDTH, 0xFF, 0x08);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+ CD_DEGLITCH_EN, XD_CD_DEGLITCH_EN, 0x0);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD30_DRIVE_SEL,
+ SD30_DRIVE_MASK, DRIVER_TYPE_D);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+ CARD_DRIVE_SEL, SD20_DRIVE_MASK, 0x0);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, LDO_POWER_CFG, 0xE0, 0x0);
+
+ if (ucr->is_rts5179)
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+ CARD_PULL_CTL5, 0x03, 0x01);
+
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DMA1_CTL,
+ EXTEND_DMA1_ASYNC_SIGNAL, EXTEND_DMA1_ASYNC_SIGNAL);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_INT_PEND,
+ XD_INT | MS_INT | SD_INT,
+ XD_INT | MS_INT | SD_INT);
+
+ ret = rtsx_usb_send_cmd(ucr, MODE_C, 100);
+ if (ret)
+ return ret;
+
+ /* config non-crystal mode */
+ rtsx_usb_read_register(ucr, CFG_MODE, &val);
+ if ((val & XTAL_FREE) || ((val & CLK_MODE_MASK) == CLK_MODE_NON_XTAL)) {
+ ret = rtsx_usb_write_phy_register(ucr, 0xC2, 0x7C);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rtsx_usb_init_chip(struct rtsx_ucr *ucr)
+{
+ int ret;
+ u8 val;
+
+ rtsx_usb_clear_fsm_err(ucr);
+
+ /* power on SSC */
+ ret = rtsx_usb_write_register(ucr,
+ FPDCTL, SSC_POWER_MASK, SSC_POWER_ON);
+ if (ret)
+ return ret;
+
+ usleep_range(100, 1000);
+ ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0x00);
+ if (ret)
+ return ret;
+
+ /* determine IC version */
+ ret = rtsx_usb_read_register(ucr, HW_VERSION, &val);
+ if (ret)
+ return ret;
+
+ ucr->ic_version = val & HW_VER_MASK;
+
+ /* determine package */
+ ret = rtsx_usb_read_register(ucr, CARD_SHARE_MODE, &val);
+ if (ret)
+ return ret;
+
+ if (val & CARD_SHARE_LQFP_SEL) {
+ ucr->package = LQFP48;
+ dev_dbg(&ucr->pusb_intf->dev, "Package: LQFP48\n");
+ } else {
+ ucr->package = QFN24;
+ dev_dbg(&ucr->pusb_intf->dev, "Package: QFN24\n");
+ }
+
+ /* determine IC variations */
+ rtsx_usb_read_register(ucr, CFG_MODE_1, &val);
+ if (val & RTS5179) {
+ ucr->is_rts5179 = true;
+ dev_dbg(&ucr->pusb_intf->dev, "Device is rts5179\n");
+ } else {
+ ucr->is_rts5179 = false;
+ }
+
+ return rtsx_usb_reset_chip(ucr);
+}
+
+static int rtsx_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(intf);
+ struct rtsx_ucr *ucr;
+ int ret;
+
+ dev_dbg(&intf->dev,
+ ": Realtek USB Card Reader found at bus %03d address %03d\n",
+ usb_dev->bus->busnum, usb_dev->devnum);
+
+ ucr = devm_kzalloc(&intf->dev, sizeof(*ucr), GFP_KERNEL);
+ if (!ucr)
+ return -ENOMEM;
+
+ ucr->pusb_dev = usb_dev;
+
+ ucr->iobuf = usb_alloc_coherent(ucr->pusb_dev, IOBUF_SIZE,
+ GFP_KERNEL, &ucr->iobuf_dma);
+ if (!ucr->iobuf)
+ return -ENOMEM;
+
+ usb_set_intfdata(intf, ucr);
+
+ ucr->vendor_id = id->idVendor;
+ ucr->product_id = id->idProduct;
+ ucr->cmd_buf = ucr->rsp_buf = ucr->iobuf;
+
+ mutex_init(&ucr->dev_mutex);
+
+ ucr->pusb_intf = intf;
+
+ /* initialize */
+ ret = rtsx_usb_init_chip(ucr);
+ if (ret)
+ goto out_init_fail;
+
+ /* initialize USB SG transfer timer */
+ setup_timer(&ucr->sg_timer, rtsx_usb_sg_timed_out, (unsigned long) ucr);
+
+ ret = mfd_add_devices(&intf->dev, usb_dev->devnum, rtsx_usb_cells,
+ ARRAY_SIZE(rtsx_usb_cells), NULL, 0, NULL);
+ if (ret)
+ goto out_init_fail;
+
+#ifdef CONFIG_PM
+ intf->needs_remote_wakeup = 1;
+ usb_enable_autosuspend(usb_dev);
+#endif
+
+ return 0;
+
+out_init_fail:
+ usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf,
+ ucr->iobuf_dma);
+ return ret;
+}
+
+static void rtsx_usb_disconnect(struct usb_interface *intf)
+{
+ struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf);
+
+ dev_dbg(&intf->dev, "%s called\n", __func__);
+
+ mfd_remove_devices(&intf->dev);
+
+ usb_set_intfdata(ucr->pusb_intf, NULL);
+ usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf,
+ ucr->iobuf_dma);
+}
+
+#ifdef CONFIG_PM
+static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct rtsx_ucr *ucr =
+ (struct rtsx_ucr *)usb_get_intfdata(intf);
+
+ dev_dbg(&intf->dev, "%s called with pm message 0x%04u\n",
+ __func__, message.event);
+
+ /*
+ * Call to make sure LED is off during suspend to save more power.
+ * It is NOT a permanent state and could be turned on anytime later.
+ * Thus no need to call turn_on when resunming.
+ */
+ mutex_lock(&ucr->dev_mutex);
+ rtsx_usb_turn_off_led(ucr);
+ mutex_unlock(&ucr->dev_mutex);
+
+ return 0;
+}
+
+static int rtsx_usb_resume(struct usb_interface *intf)
+{
+ return 0;
+}
+
+static int rtsx_usb_reset_resume(struct usb_interface *intf)
+{
+ struct rtsx_ucr *ucr =
+ (struct rtsx_ucr *)usb_get_intfdata(intf);
+
+ rtsx_usb_reset_chip(ucr);
+ return 0;
+}
+
+#else /* CONFIG_PM */
+
+#define rtsx_usb_suspend NULL
+#define rtsx_usb_resume NULL
+#define rtsx_usb_reset_resume NULL
+
+#endif /* CONFIG_PM */
+
+
+static int rtsx_usb_pre_reset(struct usb_interface *intf)
+{
+ struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf);
+
+ mutex_lock(&ucr->dev_mutex);
+ return 0;
+}
+
+static int rtsx_usb_post_reset(struct usb_interface *intf)
+{
+ struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf);
+
+ mutex_unlock(&ucr->dev_mutex);
+ return 0;
+}
+
+static struct usb_device_id rtsx_usb_usb_ids[] = {
+ { USB_DEVICE(0x0BDA, 0x0129) },
+ { USB_DEVICE(0x0BDA, 0x0139) },
+ { USB_DEVICE(0x0BDA, 0x0140) },
+ { }
+};
+
+static struct usb_driver rtsx_usb_driver = {
+ .name = "rtsx_usb",
+ .probe = rtsx_usb_probe,
+ .disconnect = rtsx_usb_disconnect,
+ .suspend = rtsx_usb_suspend,
+ .resume = rtsx_usb_resume,
+ .reset_resume = rtsx_usb_reset_resume,
+ .pre_reset = rtsx_usb_pre_reset,
+ .post_reset = rtsx_usb_post_reset,
+ .id_table = rtsx_usb_usb_ids,
+ .supports_autosuspend = 1,
+ .soft_unbind = 1,
+};
+
+module_usb_driver(rtsx_usb_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Roger Tseng <rogerable@realtek.com>");
+MODULE_DESCRIPTION("Realtek USB Card Reader Driver");
diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c
index 49d361a618d..be06d0abbf1 100644
--- a/drivers/mfd/sec-core.c
+++ b/drivers/mfd/sec-core.c
@@ -17,16 +17,22 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
#include <linux/mutex.h>
#include <linux/mfd/core.h>
#include <linux/mfd/samsung/core.h>
#include <linux/mfd/samsung/irq.h>
-#include <linux/mfd/samsung/rtc.h>
+#include <linux/mfd/samsung/s2mpa01.h>
+#include <linux/mfd/samsung/s2mps11.h>
+#include <linux/mfd/samsung/s2mps14.h>
+#include <linux/mfd/samsung/s5m8763.h>
+#include <linux/mfd/samsung/s5m8767.h>
#include <linux/regmap.h>
-static struct mfd_cell s5m8751_devs[] = {
+static const struct mfd_cell s5m8751_devs[] = {
{
.name = "s5m8751-pmic",
}, {
@@ -36,7 +42,7 @@ static struct mfd_cell s5m8751_devs[] = {
},
};
-static struct mfd_cell s5m8763_devs[] = {
+static const struct mfd_cell s5m8763_devs[] = {
{
.name = "s5m8763-pmic",
}, {
@@ -46,60 +52,206 @@ static struct mfd_cell s5m8763_devs[] = {
},
};
-static struct mfd_cell s5m8767_devs[] = {
+static const struct mfd_cell s5m8767_devs[] = {
{
.name = "s5m8767-pmic",
}, {
.name = "s5m-rtc",
- },
+ }, {
+ .name = "s5m8767-clk",
+ .of_compatible = "samsung,s5m8767-clk",
+ }
};
-static struct mfd_cell s2mps11_devs[] = {
+static const struct mfd_cell s2mps11_devs[] = {
{
.name = "s2mps11-pmic",
+ }, {
+ .name = "s2mps11-clk",
+ .of_compatible = "samsung,s2mps11-clk",
+ }
+};
+
+static const struct mfd_cell s2mps14_devs[] = {
+ {
+ .name = "s2mps14-pmic",
+ }, {
+ .name = "s2mps14-rtc",
+ }, {
+ .name = "s2mps14-clk",
+ .of_compatible = "samsung,s2mps14-clk",
+ }
+};
+
+static const struct mfd_cell s2mpa01_devs[] = {
+ {
+ .name = "s2mpa01-pmic",
},
};
-int sec_reg_read(struct sec_pmic_dev *sec_pmic, u8 reg, void *dest)
-{
- return regmap_read(sec_pmic->regmap, reg, dest);
-}
-EXPORT_SYMBOL_GPL(sec_reg_read);
+#ifdef CONFIG_OF
+static const struct of_device_id sec_dt_match[] = {
+ { .compatible = "samsung,s5m8767-pmic",
+ .data = (void *)S5M8767X,
+ }, {
+ .compatible = "samsung,s2mps11-pmic",
+ .data = (void *)S2MPS11X,
+ }, {
+ .compatible = "samsung,s2mps14-pmic",
+ .data = (void *)S2MPS14X,
+ }, {
+ .compatible = "samsung,s2mpa01-pmic",
+ .data = (void *)S2MPA01,
+ }, {
+ /* Sentinel */
+ },
+};
+#endif
-int sec_bulk_read(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf)
+static bool s2mpa01_volatile(struct device *dev, unsigned int reg)
{
- return regmap_bulk_read(sec_pmic->regmap, reg, buf, count);
+ switch (reg) {
+ case S2MPA01_REG_INT1M:
+ case S2MPA01_REG_INT2M:
+ case S2MPA01_REG_INT3M:
+ return false;
+ default:
+ return true;
+ }
}
-EXPORT_SYMBOL_GPL(sec_bulk_read);
-int sec_reg_write(struct sec_pmic_dev *sec_pmic, u8 reg, u8 value)
+static bool s2mps11_volatile(struct device *dev, unsigned int reg)
{
- return regmap_write(sec_pmic->regmap, reg, value);
+ switch (reg) {
+ case S2MPS11_REG_INT1M:
+ case S2MPS11_REG_INT2M:
+ case S2MPS11_REG_INT3M:
+ return false;
+ default:
+ return true;
+ }
}
-EXPORT_SYMBOL_GPL(sec_reg_write);
-int sec_bulk_write(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf)
+static bool s5m8763_volatile(struct device *dev, unsigned int reg)
{
- return regmap_raw_write(sec_pmic->regmap, reg, buf, count);
+ switch (reg) {
+ case S5M8763_REG_IRQM1:
+ case S5M8763_REG_IRQM2:
+ case S5M8763_REG_IRQM3:
+ case S5M8763_REG_IRQM4:
+ return false;
+ default:
+ return true;
+ }
}
-EXPORT_SYMBOL_GPL(sec_bulk_write);
-int sec_reg_update(struct sec_pmic_dev *sec_pmic, u8 reg, u8 val, u8 mask)
-{
- return regmap_update_bits(sec_pmic->regmap, reg, mask, val);
-}
-EXPORT_SYMBOL_GPL(sec_reg_update);
+static const struct regmap_config sec_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static const struct regmap_config s2mpa01_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = S2MPA01_REG_LDO_OVCB4,
+ .volatile_reg = s2mpa01_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct regmap_config s2mps11_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = S2MPS11_REG_L38CTRL,
+ .volatile_reg = s2mps11_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
-static struct regmap_config sec_regmap_config = {
+static const struct regmap_config s2mps14_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
+
+ .max_register = S2MPS14_REG_LDODSCH3,
+ .volatile_reg = s2mps11_volatile,
+ .cache_type = REGCACHE_FLAT,
};
+static const struct regmap_config s5m8763_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = S5M8763_REG_LBCNFG2,
+ .volatile_reg = s5m8763_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct regmap_config s5m8767_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = S5M8767_REG_LDO28CTRL,
+ .volatile_reg = s2mps11_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
+
+#ifdef CONFIG_OF
+/*
+ * Only the common platform data elements for s5m8767 are parsed here from the
+ * device tree. Other sub-modules of s5m8767 such as pmic, rtc , charger and
+ * others have to parse their own platform data elements from device tree.
+ *
+ * The s5m8767 platform data structure is instantiated here and the drivers for
+ * the sub-modules need not instantiate another instance while parsing their
+ * platform data.
+ */
+static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata(
+ struct device *dev)
+{
+ struct sec_platform_data *pd;
+
+ pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+ if (!pd) {
+ dev_err(dev, "could not allocate memory for pdata\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /*
+ * ToDo: the 'wakeup' member in the platform data is more of a linux
+ * specfic information. Hence, there is no binding for that yet and
+ * not parsed here.
+ */
+
+ return pd;
+}
+#else
+static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata(
+ struct device *dev)
+{
+ return NULL;
+}
+#endif
+
+static inline unsigned long sec_i2c_get_driver_data(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+#ifdef CONFIG_OF
+ if (i2c->dev.of_node) {
+ const struct of_device_id *match;
+ match = of_match_node(sec_dt_match, i2c->dev.of_node);
+ return (unsigned long)match->data;
+ }
+#endif
+ return id->driver_data;
+}
+
static int sec_pmic_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct sec_platform_data *pdata = i2c->dev.platform_data;
+ struct sec_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ const struct regmap_config *regmap;
struct sec_pmic_dev *sec_pmic;
+ unsigned long device_type;
int ret;
sec_pmic = devm_kzalloc(&i2c->dev, sizeof(struct sec_pmic_dev),
@@ -111,26 +263,53 @@ static int sec_pmic_probe(struct i2c_client *i2c,
sec_pmic->dev = &i2c->dev;
sec_pmic->i2c = i2c;
sec_pmic->irq = i2c->irq;
- sec_pmic->type = id->driver_data;
-
+ device_type = sec_i2c_get_driver_data(i2c, id);
+
+ if (sec_pmic->dev->of_node) {
+ pdata = sec_pmic_i2c_parse_dt_pdata(sec_pmic->dev);
+ if (IS_ERR(pdata)) {
+ ret = PTR_ERR(pdata);
+ return ret;
+ }
+ pdata->device_type = device_type;
+ }
if (pdata) {
sec_pmic->device_type = pdata->device_type;
sec_pmic->ono = pdata->ono;
sec_pmic->irq_base = pdata->irq_base;
sec_pmic->wakeup = pdata->wakeup;
+ sec_pmic->pdata = pdata;
+ }
+
+ switch (sec_pmic->device_type) {
+ case S2MPA01:
+ regmap = &s2mpa01_regmap_config;
+ break;
+ case S2MPS11X:
+ regmap = &s2mps11_regmap_config;
+ break;
+ case S2MPS14X:
+ regmap = &s2mps14_regmap_config;
+ break;
+ case S5M8763X:
+ regmap = &s5m8763_regmap_config;
+ break;
+ case S5M8767X:
+ regmap = &s5m8767_regmap_config;
+ break;
+ default:
+ regmap = &sec_regmap_config;
+ break;
}
- sec_pmic->regmap = devm_regmap_init_i2c(i2c, &sec_regmap_config);
- if (IS_ERR(sec_pmic->regmap)) {
- ret = PTR_ERR(sec_pmic->regmap);
+ sec_pmic->regmap_pmic = devm_regmap_init_i2c(i2c, regmap);
+ if (IS_ERR(sec_pmic->regmap_pmic)) {
+ ret = PTR_ERR(sec_pmic->regmap_pmic);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret);
return ret;
}
- sec_pmic->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
- i2c_set_clientdata(sec_pmic->rtc, sec_pmic);
-
if (pdata && pdata->cfg_pmic_irq)
pdata->cfg_pmic_irq();
@@ -151,24 +330,32 @@ static int sec_pmic_probe(struct i2c_client *i2c,
ret = mfd_add_devices(sec_pmic->dev, -1, s5m8767_devs,
ARRAY_SIZE(s5m8767_devs), NULL, 0, NULL);
break;
+ case S2MPA01:
+ ret = mfd_add_devices(sec_pmic->dev, -1, s2mpa01_devs,
+ ARRAY_SIZE(s2mpa01_devs), NULL, 0, NULL);
+ break;
case S2MPS11X:
ret = mfd_add_devices(sec_pmic->dev, -1, s2mps11_devs,
ARRAY_SIZE(s2mps11_devs), NULL, 0, NULL);
break;
+ case S2MPS14X:
+ ret = mfd_add_devices(sec_pmic->dev, -1, s2mps14_devs,
+ ARRAY_SIZE(s2mps14_devs), NULL, 0, NULL);
+ break;
default:
/* If this happens the probe function is problem */
BUG();
}
- if (ret < 0)
- goto err;
+ if (ret)
+ goto err_mfd;
+
+ device_init_wakeup(sec_pmic->dev, sec_pmic->wakeup);
return ret;
-err:
- mfd_remove_devices(sec_pmic->dev);
+err_mfd:
sec_irq_exit(sec_pmic);
- i2c_unregister_device(sec_pmic->rtc);
return ret;
}
@@ -178,10 +365,46 @@ static int sec_pmic_remove(struct i2c_client *i2c)
mfd_remove_devices(sec_pmic->dev);
sec_irq_exit(sec_pmic);
- i2c_unregister_device(sec_pmic->rtc);
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int sec_pmic_suspend(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(sec_pmic->irq);
+ /*
+ * PMIC IRQ must be disabled during suspend for RTC alarm
+ * to work properly.
+ * When device is woken up from suspend, an
+ * interrupt occurs before resuming I2C bus controller.
+ * The interrupt is handled by regmap_irq_thread which tries
+ * to read RTC registers. This read fails (I2C is still
+ * suspended) and RTC Alarm interrupt is disabled.
+ */
+ disable_irq(sec_pmic->irq);
+
+ return 0;
+}
+
+static int sec_pmic_resume(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(sec_pmic->irq);
+ enable_irq(sec_pmic->irq);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(sec_pmic_pm_ops, sec_pmic_suspend, sec_pmic_resume);
+
static const struct i2c_device_id sec_pmic_id[] = {
{ "sec_pmic", 0 },
{ }
@@ -192,6 +415,8 @@ static struct i2c_driver sec_pmic_driver = {
.driver = {
.name = "sec_pmic",
.owner = THIS_MODULE,
+ .pm = &sec_pmic_pm_ops,
+ .of_match_table = of_match_ptr(sec_dt_match),
},
.probe = sec_pmic_probe,
.remove = sec_pmic_remove,
diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c
index c901fa50fea..654e2c1dbf7 100644
--- a/drivers/mfd/sec-irq.c
+++ b/drivers/mfd/sec-irq.c
@@ -1,7 +1,7 @@
/*
* sec-irq.c
*
- * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd
* http://www.samsung.com
*
* This program is free software; you can redistribute it and/or modify it
@@ -19,224 +19,295 @@
#include <linux/mfd/samsung/core.h>
#include <linux/mfd/samsung/irq.h>
#include <linux/mfd/samsung/s2mps11.h>
+#include <linux/mfd/samsung/s2mps14.h>
#include <linux/mfd/samsung/s5m8763.h>
#include <linux/mfd/samsung/s5m8767.h>
-static struct regmap_irq s2mps11_irqs[] = {
+static const struct regmap_irq s2mps11_irqs[] = {
[S2MPS11_IRQ_PWRONF] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S2MPS11_IRQ_PWRONF_MASK,
},
[S2MPS11_IRQ_PWRONR] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S2MPS11_IRQ_PWRONR_MASK,
},
[S2MPS11_IRQ_JIGONBF] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S2MPS11_IRQ_JIGONBF_MASK,
},
[S2MPS11_IRQ_JIGONBR] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S2MPS11_IRQ_JIGONBR_MASK,
},
[S2MPS11_IRQ_ACOKBF] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S2MPS11_IRQ_ACOKBF_MASK,
},
[S2MPS11_IRQ_ACOKBR] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S2MPS11_IRQ_ACOKBR_MASK,
},
[S2MPS11_IRQ_PWRON1S] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S2MPS11_IRQ_PWRON1S_MASK,
},
[S2MPS11_IRQ_MRB] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S2MPS11_IRQ_MRB_MASK,
},
[S2MPS11_IRQ_RTC60S] = {
- .reg_offset = 2,
+ .reg_offset = 1,
.mask = S2MPS11_IRQ_RTC60S_MASK,
},
+ [S2MPS11_IRQ_RTCA0] = {
+ .reg_offset = 1,
+ .mask = S2MPS11_IRQ_RTCA0_MASK,
+ },
[S2MPS11_IRQ_RTCA1] = {
- .reg_offset = 2,
+ .reg_offset = 1,
.mask = S2MPS11_IRQ_RTCA1_MASK,
},
- [S2MPS11_IRQ_RTCA2] = {
- .reg_offset = 2,
- .mask = S2MPS11_IRQ_RTCA2_MASK,
- },
[S2MPS11_IRQ_SMPL] = {
- .reg_offset = 2,
+ .reg_offset = 1,
.mask = S2MPS11_IRQ_SMPL_MASK,
},
[S2MPS11_IRQ_RTC1S] = {
- .reg_offset = 2,
+ .reg_offset = 1,
.mask = S2MPS11_IRQ_RTC1S_MASK,
},
[S2MPS11_IRQ_WTSR] = {
- .reg_offset = 2,
+ .reg_offset = 1,
.mask = S2MPS11_IRQ_WTSR_MASK,
},
[S2MPS11_IRQ_INT120C] = {
- .reg_offset = 3,
+ .reg_offset = 2,
.mask = S2MPS11_IRQ_INT120C_MASK,
},
[S2MPS11_IRQ_INT140C] = {
- .reg_offset = 3,
+ .reg_offset = 2,
.mask = S2MPS11_IRQ_INT140C_MASK,
},
};
+static const struct regmap_irq s2mps14_irqs[] = {
+ [S2MPS14_IRQ_PWRONF] = {
+ .reg_offset = 0,
+ .mask = S2MPS11_IRQ_PWRONF_MASK,
+ },
+ [S2MPS14_IRQ_PWRONR] = {
+ .reg_offset = 0,
+ .mask = S2MPS11_IRQ_PWRONR_MASK,
+ },
+ [S2MPS14_IRQ_JIGONBF] = {
+ .reg_offset = 0,
+ .mask = S2MPS11_IRQ_JIGONBF_MASK,
+ },
+ [S2MPS14_IRQ_JIGONBR] = {
+ .reg_offset = 0,
+ .mask = S2MPS11_IRQ_JIGONBR_MASK,
+ },
+ [S2MPS14_IRQ_ACOKBF] = {
+ .reg_offset = 0,
+ .mask = S2MPS11_IRQ_ACOKBF_MASK,
+ },
+ [S2MPS14_IRQ_ACOKBR] = {
+ .reg_offset = 0,
+ .mask = S2MPS11_IRQ_ACOKBR_MASK,
+ },
+ [S2MPS14_IRQ_PWRON1S] = {
+ .reg_offset = 0,
+ .mask = S2MPS11_IRQ_PWRON1S_MASK,
+ },
+ [S2MPS14_IRQ_MRB] = {
+ .reg_offset = 0,
+ .mask = S2MPS11_IRQ_MRB_MASK,
+ },
+ [S2MPS14_IRQ_RTC60S] = {
+ .reg_offset = 1,
+ .mask = S2MPS11_IRQ_RTC60S_MASK,
+ },
+ [S2MPS14_IRQ_RTCA1] = {
+ .reg_offset = 1,
+ .mask = S2MPS11_IRQ_RTCA1_MASK,
+ },
+ [S2MPS14_IRQ_RTCA0] = {
+ .reg_offset = 1,
+ .mask = S2MPS11_IRQ_RTCA0_MASK,
+ },
+ [S2MPS14_IRQ_SMPL] = {
+ .reg_offset = 1,
+ .mask = S2MPS11_IRQ_SMPL_MASK,
+ },
+ [S2MPS14_IRQ_RTC1S] = {
+ .reg_offset = 1,
+ .mask = S2MPS11_IRQ_RTC1S_MASK,
+ },
+ [S2MPS14_IRQ_WTSR] = {
+ .reg_offset = 1,
+ .mask = S2MPS11_IRQ_WTSR_MASK,
+ },
+ [S2MPS14_IRQ_INT120C] = {
+ .reg_offset = 2,
+ .mask = S2MPS11_IRQ_INT120C_MASK,
+ },
+ [S2MPS14_IRQ_INT140C] = {
+ .reg_offset = 2,
+ .mask = S2MPS11_IRQ_INT140C_MASK,
+ },
+ [S2MPS14_IRQ_TSD] = {
+ .reg_offset = 2,
+ .mask = S2MPS14_IRQ_TSD_MASK,
+ },
+};
-static struct regmap_irq s5m8767_irqs[] = {
+static const struct regmap_irq s5m8767_irqs[] = {
[S5M8767_IRQ_PWRR] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S5M8767_IRQ_PWRR_MASK,
},
[S5M8767_IRQ_PWRF] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S5M8767_IRQ_PWRF_MASK,
},
[S5M8767_IRQ_PWR1S] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S5M8767_IRQ_PWR1S_MASK,
},
[S5M8767_IRQ_JIGR] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S5M8767_IRQ_JIGR_MASK,
},
[S5M8767_IRQ_JIGF] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S5M8767_IRQ_JIGF_MASK,
},
[S5M8767_IRQ_LOWBAT2] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S5M8767_IRQ_LOWBAT2_MASK,
},
[S5M8767_IRQ_LOWBAT1] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S5M8767_IRQ_LOWBAT1_MASK,
},
[S5M8767_IRQ_MRB] = {
- .reg_offset = 2,
+ .reg_offset = 1,
.mask = S5M8767_IRQ_MRB_MASK,
},
[S5M8767_IRQ_DVSOK2] = {
- .reg_offset = 2,
+ .reg_offset = 1,
.mask = S5M8767_IRQ_DVSOK2_MASK,
},
[S5M8767_IRQ_DVSOK3] = {
- .reg_offset = 2,
+ .reg_offset = 1,
.mask = S5M8767_IRQ_DVSOK3_MASK,
},
[S5M8767_IRQ_DVSOK4] = {
- .reg_offset = 2,
+ .reg_offset = 1,
.mask = S5M8767_IRQ_DVSOK4_MASK,
},
[S5M8767_IRQ_RTC60S] = {
- .reg_offset = 3,
+ .reg_offset = 2,
.mask = S5M8767_IRQ_RTC60S_MASK,
},
[S5M8767_IRQ_RTCA1] = {
- .reg_offset = 3,
+ .reg_offset = 2,
.mask = S5M8767_IRQ_RTCA1_MASK,
},
[S5M8767_IRQ_RTCA2] = {
- .reg_offset = 3,
+ .reg_offset = 2,
.mask = S5M8767_IRQ_RTCA2_MASK,
},
[S5M8767_IRQ_SMPL] = {
- .reg_offset = 3,
+ .reg_offset = 2,
.mask = S5M8767_IRQ_SMPL_MASK,
},
[S5M8767_IRQ_RTC1S] = {
- .reg_offset = 3,
+ .reg_offset = 2,
.mask = S5M8767_IRQ_RTC1S_MASK,
},
[S5M8767_IRQ_WTSR] = {
- .reg_offset = 3,
+ .reg_offset = 2,
.mask = S5M8767_IRQ_WTSR_MASK,
},
};
-static struct regmap_irq s5m8763_irqs[] = {
+static const struct regmap_irq s5m8763_irqs[] = {
[S5M8763_IRQ_DCINF] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S5M8763_IRQ_DCINF_MASK,
},
[S5M8763_IRQ_DCINR] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S5M8763_IRQ_DCINR_MASK,
},
[S5M8763_IRQ_JIGF] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S5M8763_IRQ_JIGF_MASK,
},
[S5M8763_IRQ_JIGR] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S5M8763_IRQ_JIGR_MASK,
},
[S5M8763_IRQ_PWRONF] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S5M8763_IRQ_PWRONF_MASK,
},
[S5M8763_IRQ_PWRONR] = {
- .reg_offset = 1,
+ .reg_offset = 0,
.mask = S5M8763_IRQ_PWRONR_MASK,
},
[S5M8763_IRQ_WTSREVNT] = {
- .reg_offset = 2,
+ .reg_offset = 1,
.mask = S5M8763_IRQ_WTSREVNT_MASK,
},
[S5M8763_IRQ_SMPLEVNT] = {
- .reg_offset = 2,
+ .reg_offset = 1,
.mask = S5M8763_IRQ_SMPLEVNT_MASK,
},
[S5M8763_IRQ_ALARM1] = {
- .reg_offset = 2,
+ .reg_offset = 1,
.mask = S5M8763_IRQ_ALARM1_MASK,
},
[S5M8763_IRQ_ALARM0] = {
- .reg_offset = 2,
+ .reg_offset = 1,
.mask = S5M8763_IRQ_ALARM0_MASK,
},
[S5M8763_IRQ_ONKEY1S] = {
- .reg_offset = 3,
+ .reg_offset = 2,
.mask = S5M8763_IRQ_ONKEY1S_MASK,
},
[S5M8763_IRQ_TOPOFFR] = {
- .reg_offset = 3,
+ .reg_offset = 2,
.mask = S5M8763_IRQ_TOPOFFR_MASK,
},
[S5M8763_IRQ_DCINOVPR] = {
- .reg_offset = 3,
+ .reg_offset = 2,
.mask = S5M8763_IRQ_DCINOVPR_MASK,
},
[S5M8763_IRQ_CHGRSTF] = {
- .reg_offset = 3,
+ .reg_offset = 2,
.mask = S5M8763_IRQ_CHGRSTF_MASK,
},
[S5M8763_IRQ_DONER] = {
- .reg_offset = 3,
+ .reg_offset = 2,
.mask = S5M8763_IRQ_DONER_MASK,
},
[S5M8763_IRQ_CHGFAULT] = {
- .reg_offset = 3,
+ .reg_offset = 2,
.mask = S5M8763_IRQ_CHGFAULT_MASK,
},
[S5M8763_IRQ_LOBAT1] = {
- .reg_offset = 4,
+ .reg_offset = 3,
.mask = S5M8763_IRQ_LOBAT1_MASK,
},
[S5M8763_IRQ_LOBAT2] = {
- .reg_offset = 4,
+ .reg_offset = 3,
.mask = S5M8763_IRQ_LOBAT2_MASK,
},
};
-static struct regmap_irq_chip s2mps11_irq_chip = {
+static const struct regmap_irq_chip s2mps11_irq_chip = {
.name = "s2mps11",
.irqs = s2mps11_irqs,
.num_irqs = ARRAY_SIZE(s2mps11_irqs),
@@ -246,7 +317,17 @@ static struct regmap_irq_chip s2mps11_irq_chip = {
.ack_base = S2MPS11_REG_INT1,
};
-static struct regmap_irq_chip s5m8767_irq_chip = {
+static const struct regmap_irq_chip s2mps14_irq_chip = {
+ .name = "s2mps14",
+ .irqs = s2mps14_irqs,
+ .num_irqs = ARRAY_SIZE(s2mps14_irqs),
+ .num_regs = 3,
+ .status_base = S2MPS14_REG_INT1,
+ .mask_base = S2MPS14_REG_INT1M,
+ .ack_base = S2MPS14_REG_INT1,
+};
+
+static const struct regmap_irq_chip s5m8767_irq_chip = {
.name = "s5m8767",
.irqs = s5m8767_irqs,
.num_irqs = ARRAY_SIZE(s5m8767_irqs),
@@ -256,7 +337,7 @@ static struct regmap_irq_chip s5m8767_irq_chip = {
.ack_base = S5M8767_REG_INT1,
};
-static struct regmap_irq_chip s5m8763_irq_chip = {
+static const struct regmap_irq_chip s5m8763_irq_chip = {
.name = "s5m8763",
.irqs = s5m8763_irqs,
.num_irqs = ARRAY_SIZE(s5m8763_irqs),
@@ -280,25 +361,31 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic)
switch (type) {
case S5M8763X:
- ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq,
+ ret = regmap_add_irq_chip(sec_pmic->regmap_pmic, sec_pmic->irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
sec_pmic->irq_base, &s5m8763_irq_chip,
&sec_pmic->irq_data);
break;
case S5M8767X:
- ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq,
+ ret = regmap_add_irq_chip(sec_pmic->regmap_pmic, sec_pmic->irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
sec_pmic->irq_base, &s5m8767_irq_chip,
&sec_pmic->irq_data);
break;
case S2MPS11X:
- ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq,
+ ret = regmap_add_irq_chip(sec_pmic->regmap_pmic, sec_pmic->irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
sec_pmic->irq_base, &s2mps11_irq_chip,
&sec_pmic->irq_data);
break;
+ case S2MPS14X:
+ ret = regmap_add_irq_chip(sec_pmic->regmap_pmic, sec_pmic->irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ sec_pmic->irq_base, &s2mps14_irq_chip,
+ &sec_pmic->irq_data);
+ break;
default:
- dev_err(sec_pmic->dev, "Unknown device type %d\n",
+ dev_err(sec_pmic->dev, "Unknown device type %lu\n",
sec_pmic->device_type);
return -EINVAL;
}
diff --git a/drivers/mfd/si476x-cmd.c b/drivers/mfd/si476x-cmd.c
new file mode 100644
index 00000000000..6f1ef63086c
--- /dev/null
+++ b/drivers/mfd/si476x-cmd.c
@@ -0,0 +1,1555 @@
+/*
+ * drivers/mfd/si476x-cmd.c -- Subroutines implementing command
+ * protocol of si476x series of chips
+ *
+ * Copyright (C) 2012 Innovative Converged Devices(ICD)
+ * Copyright (C) 2013 Andrey Smirnov
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.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; version 2 of the License.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/atomic.h>
+#include <linux/i2c.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/videodev2.h>
+
+#include <linux/mfd/si476x-core.h>
+
+#include <asm/unaligned.h>
+
+#define msb(x) ((u8)((u16) x >> 8))
+#define lsb(x) ((u8)((u16) x & 0x00FF))
+
+
+
+#define CMD_POWER_UP 0x01
+#define CMD_POWER_UP_A10_NRESP 1
+#define CMD_POWER_UP_A10_NARGS 5
+
+#define CMD_POWER_UP_A20_NRESP 1
+#define CMD_POWER_UP_A20_NARGS 5
+
+#define POWER_UP_DELAY_MS 110
+
+#define CMD_POWER_DOWN 0x11
+#define CMD_POWER_DOWN_A10_NRESP 1
+
+#define CMD_POWER_DOWN_A20_NRESP 1
+#define CMD_POWER_DOWN_A20_NARGS 1
+
+#define CMD_FUNC_INFO 0x12
+#define CMD_FUNC_INFO_NRESP 7
+
+#define CMD_SET_PROPERTY 0x13
+#define CMD_SET_PROPERTY_NARGS 5
+#define CMD_SET_PROPERTY_NRESP 1
+
+#define CMD_GET_PROPERTY 0x14
+#define CMD_GET_PROPERTY_NARGS 3
+#define CMD_GET_PROPERTY_NRESP 4
+
+#define CMD_AGC_STATUS 0x17
+#define CMD_AGC_STATUS_NRESP_A10 2
+#define CMD_AGC_STATUS_NRESP_A20 6
+
+#define PIN_CFG_BYTE(x) (0x7F & (x))
+#define CMD_DIG_AUDIO_PIN_CFG 0x18
+#define CMD_DIG_AUDIO_PIN_CFG_NARGS 4
+#define CMD_DIG_AUDIO_PIN_CFG_NRESP 5
+
+#define CMD_ZIF_PIN_CFG 0x19
+#define CMD_ZIF_PIN_CFG_NARGS 4
+#define CMD_ZIF_PIN_CFG_NRESP 5
+
+#define CMD_IC_LINK_GPO_CTL_PIN_CFG 0x1A
+#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS 4
+#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP 5
+
+#define CMD_ANA_AUDIO_PIN_CFG 0x1B
+#define CMD_ANA_AUDIO_PIN_CFG_NARGS 1
+#define CMD_ANA_AUDIO_PIN_CFG_NRESP 2
+
+#define CMD_INTB_PIN_CFG 0x1C
+#define CMD_INTB_PIN_CFG_NARGS 2
+#define CMD_INTB_PIN_CFG_A10_NRESP 6
+#define CMD_INTB_PIN_CFG_A20_NRESP 3
+
+#define CMD_FM_TUNE_FREQ 0x30
+#define CMD_FM_TUNE_FREQ_A10_NARGS 5
+#define CMD_FM_TUNE_FREQ_A20_NARGS 3
+#define CMD_FM_TUNE_FREQ_NRESP 1
+
+#define CMD_FM_RSQ_STATUS 0x32
+
+#define CMD_FM_RSQ_STATUS_A10_NARGS 1
+#define CMD_FM_RSQ_STATUS_A10_NRESP 17
+#define CMD_FM_RSQ_STATUS_A30_NARGS 1
+#define CMD_FM_RSQ_STATUS_A30_NRESP 23
+
+
+#define CMD_FM_SEEK_START 0x31
+#define CMD_FM_SEEK_START_NARGS 1
+#define CMD_FM_SEEK_START_NRESP 1
+
+#define CMD_FM_RDS_STATUS 0x36
+#define CMD_FM_RDS_STATUS_NARGS 1
+#define CMD_FM_RDS_STATUS_NRESP 16
+
+#define CMD_FM_RDS_BLOCKCOUNT 0x37
+#define CMD_FM_RDS_BLOCKCOUNT_NARGS 1
+#define CMD_FM_RDS_BLOCKCOUNT_NRESP 8
+
+#define CMD_FM_PHASE_DIVERSITY 0x38
+#define CMD_FM_PHASE_DIVERSITY_NARGS 1
+#define CMD_FM_PHASE_DIVERSITY_NRESP 1
+
+#define CMD_FM_PHASE_DIV_STATUS 0x39
+#define CMD_FM_PHASE_DIV_STATUS_NRESP 2
+
+#define CMD_AM_TUNE_FREQ 0x40
+#define CMD_AM_TUNE_FREQ_NARGS 3
+#define CMD_AM_TUNE_FREQ_NRESP 1
+
+#define CMD_AM_RSQ_STATUS 0x42
+#define CMD_AM_RSQ_STATUS_NARGS 1
+#define CMD_AM_RSQ_STATUS_NRESP 13
+
+#define CMD_AM_SEEK_START 0x41
+#define CMD_AM_SEEK_START_NARGS 1
+#define CMD_AM_SEEK_START_NRESP 1
+
+
+#define CMD_AM_ACF_STATUS 0x45
+#define CMD_AM_ACF_STATUS_NRESP 6
+#define CMD_AM_ACF_STATUS_NARGS 1
+
+#define CMD_FM_ACF_STATUS 0x35
+#define CMD_FM_ACF_STATUS_NRESP 8
+#define CMD_FM_ACF_STATUS_NARGS 1
+
+#define CMD_MAX_ARGS_COUNT (10)
+
+
+enum si476x_acf_status_report_bits {
+ SI476X_ACF_BLEND_INT = (1 << 4),
+ SI476X_ACF_HIBLEND_INT = (1 << 3),
+ SI476X_ACF_HICUT_INT = (1 << 2),
+ SI476X_ACF_CHBW_INT = (1 << 1),
+ SI476X_ACF_SOFTMUTE_INT = (1 << 0),
+
+ SI476X_ACF_SMUTE = (1 << 0),
+ SI476X_ACF_SMATTN = 0x1f,
+ SI476X_ACF_PILOT = (1 << 7),
+ SI476X_ACF_STBLEND = ~SI476X_ACF_PILOT,
+};
+
+enum si476x_agc_status_report_bits {
+ SI476X_AGC_MXHI = (1 << 5),
+ SI476X_AGC_MXLO = (1 << 4),
+ SI476X_AGC_LNAHI = (1 << 3),
+ SI476X_AGC_LNALO = (1 << 2),
+};
+
+enum si476x_errors {
+ SI476X_ERR_BAD_COMMAND = 0x10,
+ SI476X_ERR_BAD_ARG1 = 0x11,
+ SI476X_ERR_BAD_ARG2 = 0x12,
+ SI476X_ERR_BAD_ARG3 = 0x13,
+ SI476X_ERR_BAD_ARG4 = 0x14,
+ SI476X_ERR_BUSY = 0x18,
+ SI476X_ERR_BAD_INTERNAL_MEMORY = 0x20,
+ SI476X_ERR_BAD_PATCH = 0x30,
+ SI476X_ERR_BAD_BOOT_MODE = 0x31,
+ SI476X_ERR_BAD_PROPERTY = 0x40,
+};
+
+static int si476x_core_parse_and_nag_about_error(struct si476x_core *core)
+{
+ int err;
+ char *cause;
+ u8 buffer[2];
+
+ if (core->revision != SI476X_REVISION_A10) {
+ err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV,
+ buffer, sizeof(buffer));
+ if (err == sizeof(buffer)) {
+ switch (buffer[1]) {
+ case SI476X_ERR_BAD_COMMAND:
+ cause = "Bad command";
+ err = -EINVAL;
+ break;
+ case SI476X_ERR_BAD_ARG1:
+ cause = "Bad argument #1";
+ err = -EINVAL;
+ break;
+ case SI476X_ERR_BAD_ARG2:
+ cause = "Bad argument #2";
+ err = -EINVAL;
+ break;
+ case SI476X_ERR_BAD_ARG3:
+ cause = "Bad argument #3";
+ err = -EINVAL;
+ break;
+ case SI476X_ERR_BAD_ARG4:
+ cause = "Bad argument #4";
+ err = -EINVAL;
+ break;
+ case SI476X_ERR_BUSY:
+ cause = "Chip is busy";
+ err = -EBUSY;
+ break;
+ case SI476X_ERR_BAD_INTERNAL_MEMORY:
+ cause = "Bad internal memory";
+ err = -EIO;
+ break;
+ case SI476X_ERR_BAD_PATCH:
+ cause = "Bad patch";
+ err = -EINVAL;
+ break;
+ case SI476X_ERR_BAD_BOOT_MODE:
+ cause = "Bad boot mode";
+ err = -EINVAL;
+ break;
+ case SI476X_ERR_BAD_PROPERTY:
+ cause = "Bad property";
+ err = -EINVAL;
+ break;
+ default:
+ cause = "Unknown";
+ err = -EIO;
+ }
+
+ dev_err(&core->client->dev,
+ "[Chip error status]: %s\n", cause);
+ } else {
+ dev_err(&core->client->dev,
+ "Failed to fetch error code\n");
+ err = (err >= 0) ? -EIO : err;
+ }
+ } else {
+ err = -EIO;
+ }
+
+ return err;
+}
+
+/**
+ * si476x_core_send_command() - sends a command to si476x and waits its
+ * response
+ * @core: si476x_device structure for the device we are
+ * communicating with
+ * @command: command id
+ * @args: command arguments we are sending
+ * @argn: actual size of @args
+ * @response: buffer to place the expected response from the device
+ * @respn: actual size of @response
+ * @usecs: amount of time to wait before reading the response (in
+ * usecs)
+ *
+ * Function returns 0 on succsess and negative error code on
+ * failure
+ */
+static int si476x_core_send_command(struct si476x_core *core,
+ const u8 command,
+ const u8 args[],
+ const int argn,
+ u8 resp[],
+ const int respn,
+ const int usecs)
+{
+ struct i2c_client *client = core->client;
+ int err;
+ u8 data[CMD_MAX_ARGS_COUNT + 1];
+
+ if (argn > CMD_MAX_ARGS_COUNT) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ if (!client->adapter) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ /* First send the command and its arguments */
+ data[0] = command;
+ memcpy(&data[1], args, argn);
+ dev_dbg(&client->dev, "Command:\n %*ph\n", argn + 1, data);
+
+ err = si476x_core_i2c_xfer(core, SI476X_I2C_SEND,
+ (char *) data, argn + 1);
+ if (err != argn + 1) {
+ dev_err(&core->client->dev,
+ "Error while sending command 0x%02x\n",
+ command);
+ err = (err >= 0) ? -EIO : err;
+ goto exit;
+ }
+ /* Set CTS to zero only after the command is send to avoid
+ * possible racing conditions when working in polling mode */
+ atomic_set(&core->cts, 0);
+
+ /* if (unlikely(command == CMD_POWER_DOWN) */
+ if (!wait_event_timeout(core->command,
+ atomic_read(&core->cts),
+ usecs_to_jiffies(usecs) + 1))
+ dev_warn(&core->client->dev,
+ "(%s) [CMD 0x%02x] Answer timeout.\n",
+ __func__, command);
+
+ /*
+ When working in polling mode, for some reason the tuner will
+ report CTS bit as being set in the first status byte read,
+ but all the consequtive ones will return zeros until the
+ tuner is actually completed the POWER_UP command. To
+ workaround that we wait for second CTS to be reported
+ */
+ if (unlikely(!core->client->irq && command == CMD_POWER_UP)) {
+ if (!wait_event_timeout(core->command,
+ atomic_read(&core->cts),
+ usecs_to_jiffies(usecs) + 1))
+ dev_warn(&core->client->dev,
+ "(%s) Power up took too much time.\n",
+ __func__);
+ }
+
+ /* Then get the response */
+ err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, resp, respn);
+ if (err != respn) {
+ dev_err(&core->client->dev,
+ "Error while reading response for command 0x%02x\n",
+ command);
+ err = (err >= 0) ? -EIO : err;
+ goto exit;
+ }
+ dev_dbg(&client->dev, "Response:\n %*ph\n", respn, resp);
+
+ err = 0;
+
+ if (resp[0] & SI476X_ERR) {
+ dev_err(&core->client->dev,
+ "[CMD 0x%02x] Chip set error flag\n", command);
+ err = si476x_core_parse_and_nag_about_error(core);
+ goto exit;
+ }
+
+ if (!(resp[0] & SI476X_CTS))
+ err = -EBUSY;
+exit:
+ return err;
+}
+
+static int si476x_cmd_clear_stc(struct si476x_core *core)
+{
+ int err;
+ struct si476x_rsq_status_args args = {
+ .primary = false,
+ .rsqack = false,
+ .attune = false,
+ .cancel = false,
+ .stcack = true,
+ };
+
+ switch (core->power_up_parameters.func) {
+ case SI476X_FUNC_FM_RECEIVER:
+ err = si476x_core_cmd_fm_rsq_status(core, &args, NULL);
+ break;
+ case SI476X_FUNC_AM_RECEIVER:
+ err = si476x_core_cmd_am_rsq_status(core, &args, NULL);
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+static int si476x_cmd_tune_seek_freq(struct si476x_core *core,
+ uint8_t cmd,
+ const uint8_t args[], size_t argn,
+ uint8_t *resp, size_t respn)
+{
+ int err;
+
+
+ atomic_set(&core->stc, 0);
+ err = si476x_core_send_command(core, cmd, args, argn, resp, respn,
+ SI476X_TIMEOUT_TUNE);
+ if (!err) {
+ wait_event_killable(core->tuning,
+ atomic_read(&core->stc));
+ si476x_cmd_clear_stc(core);
+ }
+
+ return err;
+}
+
+/**
+ * si476x_cmd_func_info() - send 'FUNC_INFO' command to the device
+ * @core: device to send the command to
+ * @info: struct si476x_func_info to fill all the information
+ * returned by the command
+ *
+ * The command requests the firmware and patch version for currently
+ * loaded firmware (dependent on the function of the device FM/AM/WB)
+ *
+ * Function returns 0 on succsess and negative error code on
+ * failure
+ */
+int si476x_core_cmd_func_info(struct si476x_core *core,
+ struct si476x_func_info *info)
+{
+ int err;
+ u8 resp[CMD_FUNC_INFO_NRESP];
+
+ err = si476x_core_send_command(core, CMD_FUNC_INFO,
+ NULL, 0,
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+
+ info->firmware.major = resp[1];
+ info->firmware.minor[0] = resp[2];
+ info->firmware.minor[1] = resp[3];
+
+ info->patch_id = ((u16) resp[4] << 8) | resp[5];
+ info->func = resp[6];
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_func_info);
+
+/**
+ * si476x_cmd_set_property() - send 'SET_PROPERTY' command to the device
+ * @core: device to send the command to
+ * @property: property address
+ * @value: property value
+ *
+ * Function returns 0 on succsess and negative error code on
+ * failure
+ */
+int si476x_core_cmd_set_property(struct si476x_core *core,
+ u16 property, u16 value)
+{
+ u8 resp[CMD_SET_PROPERTY_NRESP];
+ const u8 args[CMD_SET_PROPERTY_NARGS] = {
+ 0x00,
+ msb(property),
+ lsb(property),
+ msb(value),
+ lsb(value),
+ };
+
+ return si476x_core_send_command(core, CMD_SET_PROPERTY,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_set_property);
+
+/**
+ * si476x_cmd_get_property() - send 'GET_PROPERTY' command to the device
+ * @core: device to send the command to
+ * @property: property address
+ *
+ * Function return the value of property as u16 on success or a
+ * negative error on failure
+ */
+int si476x_core_cmd_get_property(struct si476x_core *core, u16 property)
+{
+ int err;
+ u8 resp[CMD_GET_PROPERTY_NRESP];
+ const u8 args[CMD_GET_PROPERTY_NARGS] = {
+ 0x00,
+ msb(property),
+ lsb(property),
+ };
+
+ err = si476x_core_send_command(core, CMD_GET_PROPERTY,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+ if (err < 0)
+ return err;
+ else
+ return get_unaligned_be16(resp + 2);
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property);
+
+/**
+ * si476x_cmd_dig_audio_pin_cfg() - send 'DIG_AUDIO_PIN_CFG' command to
+ * the device
+ * @core: device to send the command to
+ * @dclk: DCLK pin function configuration:
+ * #SI476X_DCLK_NOOP - do not modify the behaviour
+ * #SI476X_DCLK_TRISTATE - put the pin in tristate condition,
+ * enable 1MOhm pulldown
+ * #SI476X_DCLK_DAUDIO - set the pin to be a part of digital
+ * audio interface
+ * @dfs: DFS pin function configuration:
+ * #SI476X_DFS_NOOP - do not modify the behaviour
+ * #SI476X_DFS_TRISTATE - put the pin in tristate condition,
+ * enable 1MOhm pulldown
+ * SI476X_DFS_DAUDIO - set the pin to be a part of digital
+ * audio interface
+ * @dout - DOUT pin function configuration:
+ * SI476X_DOUT_NOOP - do not modify the behaviour
+ * SI476X_DOUT_TRISTATE - put the pin in tristate condition,
+ * enable 1MOhm pulldown
+ * SI476X_DOUT_I2S_OUTPUT - set this pin to be digital out on I2S
+ * port 1
+ * SI476X_DOUT_I2S_INPUT - set this pin to be digital in on I2S
+ * port 1
+ * @xout - XOUT pin function configuration:
+ * SI476X_XOUT_NOOP - do not modify the behaviour
+ * SI476X_XOUT_TRISTATE - put the pin in tristate condition,
+ * enable 1MOhm pulldown
+ * SI476X_XOUT_I2S_INPUT - set this pin to be digital in on I2S
+ * port 1
+ * SI476X_XOUT_MODE_SELECT - set this pin to be the input that
+ * selects the mode of the I2S audio
+ * combiner (analog or HD)
+ * [SI4761/63/65/67 Only]
+ *
+ * Function returns 0 on success and negative error code on failure
+ */
+int si476x_core_cmd_dig_audio_pin_cfg(struct si476x_core *core,
+ enum si476x_dclk_config dclk,
+ enum si476x_dfs_config dfs,
+ enum si476x_dout_config dout,
+ enum si476x_xout_config xout)
+{
+ u8 resp[CMD_DIG_AUDIO_PIN_CFG_NRESP];
+ const u8 args[CMD_DIG_AUDIO_PIN_CFG_NARGS] = {
+ PIN_CFG_BYTE(dclk),
+ PIN_CFG_BYTE(dfs),
+ PIN_CFG_BYTE(dout),
+ PIN_CFG_BYTE(xout),
+ };
+
+ return si476x_core_send_command(core, CMD_DIG_AUDIO_PIN_CFG,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg);
+
+/**
+ * si476x_cmd_zif_pin_cfg - send 'ZIF_PIN_CFG_COMMAND'
+ * @core - device to send the command to
+ * @iqclk - IQCL pin function configuration:
+ * SI476X_IQCLK_NOOP - do not modify the behaviour
+ * SI476X_IQCLK_TRISTATE - put the pin in tristate condition,
+ * enable 1MOhm pulldown
+ * SI476X_IQCLK_IQ - set pin to be a part of I/Q interace
+ * in master mode
+ * @iqfs - IQFS pin function configuration:
+ * SI476X_IQFS_NOOP - do not modify the behaviour
+ * SI476X_IQFS_TRISTATE - put the pin in tristate condition,
+ * enable 1MOhm pulldown
+ * SI476X_IQFS_IQ - set pin to be a part of I/Q interace
+ * in master mode
+ * @iout - IOUT pin function configuration:
+ * SI476X_IOUT_NOOP - do not modify the behaviour
+ * SI476X_IOUT_TRISTATE - put the pin in tristate condition,
+ * enable 1MOhm pulldown
+ * SI476X_IOUT_OUTPUT - set pin to be I out
+ * @qout - QOUT pin function configuration:
+ * SI476X_QOUT_NOOP - do not modify the behaviour
+ * SI476X_QOUT_TRISTATE - put the pin in tristate condition,
+ * enable 1MOhm pulldown
+ * SI476X_QOUT_OUTPUT - set pin to be Q out
+ *
+ * Function returns 0 on success and negative error code on failure
+ */
+int si476x_core_cmd_zif_pin_cfg(struct si476x_core *core,
+ enum si476x_iqclk_config iqclk,
+ enum si476x_iqfs_config iqfs,
+ enum si476x_iout_config iout,
+ enum si476x_qout_config qout)
+{
+ u8 resp[CMD_ZIF_PIN_CFG_NRESP];
+ const u8 args[CMD_ZIF_PIN_CFG_NARGS] = {
+ PIN_CFG_BYTE(iqclk),
+ PIN_CFG_BYTE(iqfs),
+ PIN_CFG_BYTE(iout),
+ PIN_CFG_BYTE(qout),
+ };
+
+ return si476x_core_send_command(core, CMD_ZIF_PIN_CFG,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg);
+
+/**
+ * si476x_cmd_ic_link_gpo_ctl_pin_cfg - send
+ * 'IC_LINK_GPIO_CTL_PIN_CFG' comand to the device
+ * @core - device to send the command to
+ * @icin - ICIN pin function configuration:
+ * SI476X_ICIN_NOOP - do not modify the behaviour
+ * SI476X_ICIN_TRISTATE - put the pin in tristate condition,
+ * enable 1MOhm pulldown
+ * SI476X_ICIN_GPO1_HIGH - set pin to be an output, drive it high
+ * SI476X_ICIN_GPO1_LOW - set pin to be an output, drive it low
+ * SI476X_ICIN_IC_LINK - set the pin to be a part of Inter-Chip link
+ * @icip - ICIP pin function configuration:
+ * SI476X_ICIP_NOOP - do not modify the behaviour
+ * SI476X_ICIP_TRISTATE - put the pin in tristate condition,
+ * enable 1MOhm pulldown
+ * SI476X_ICIP_GPO1_HIGH - set pin to be an output, drive it high
+ * SI476X_ICIP_GPO1_LOW - set pin to be an output, drive it low
+ * SI476X_ICIP_IC_LINK - set the pin to be a part of Inter-Chip link
+ * @icon - ICON pin function configuration:
+ * SI476X_ICON_NOOP - do not modify the behaviour
+ * SI476X_ICON_TRISTATE - put the pin in tristate condition,
+ * enable 1MOhm pulldown
+ * SI476X_ICON_I2S - set the pin to be a part of audio
+ * interface in slave mode (DCLK)
+ * SI476X_ICON_IC_LINK - set the pin to be a part of Inter-Chip link
+ * @icop - ICOP pin function configuration:
+ * SI476X_ICOP_NOOP - do not modify the behaviour
+ * SI476X_ICOP_TRISTATE - put the pin in tristate condition,
+ * enable 1MOhm pulldown
+ * SI476X_ICOP_I2S - set the pin to be a part of audio
+ * interface in slave mode (DOUT)
+ * [Si4761/63/65/67 Only]
+ * SI476X_ICOP_IC_LINK - set the pin to be a part of Inter-Chip link
+ *
+ * Function returns 0 on success and negative error code on failure
+ */
+int si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *core,
+ enum si476x_icin_config icin,
+ enum si476x_icip_config icip,
+ enum si476x_icon_config icon,
+ enum si476x_icop_config icop)
+{
+ u8 resp[CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP];
+ const u8 args[CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS] = {
+ PIN_CFG_BYTE(icin),
+ PIN_CFG_BYTE(icip),
+ PIN_CFG_BYTE(icon),
+ PIN_CFG_BYTE(icop),
+ };
+
+ return si476x_core_send_command(core, CMD_IC_LINK_GPO_CTL_PIN_CFG,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg);
+
+/**
+ * si476x_cmd_ana_audio_pin_cfg - send 'ANA_AUDIO_PIN_CFG' to the
+ * device
+ * @core - device to send the command to
+ * @lrout - LROUT pin function configuration:
+ * SI476X_LROUT_NOOP - do not modify the behaviour
+ * SI476X_LROUT_TRISTATE - put the pin in tristate condition,
+ * enable 1MOhm pulldown
+ * SI476X_LROUT_AUDIO - set pin to be audio output
+ * SI476X_LROUT_MPX - set pin to be MPX output
+ *
+ * Function returns 0 on success and negative error code on failure
+ */
+int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *core,
+ enum si476x_lrout_config lrout)
+{
+ u8 resp[CMD_ANA_AUDIO_PIN_CFG_NRESP];
+ const u8 args[CMD_ANA_AUDIO_PIN_CFG_NARGS] = {
+ PIN_CFG_BYTE(lrout),
+ };
+
+ return si476x_core_send_command(core, CMD_ANA_AUDIO_PIN_CFG,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg);
+
+
+/**
+ * si476x_cmd_intb_pin_cfg - send 'INTB_PIN_CFG' command to the device
+ * @core - device to send the command to
+ * @intb - INTB pin function configuration:
+ * SI476X_INTB_NOOP - do not modify the behaviour
+ * SI476X_INTB_TRISTATE - put the pin in tristate condition,
+ * enable 1MOhm pulldown
+ * SI476X_INTB_DAUDIO - set pin to be a part of digital
+ * audio interface in slave mode
+ * SI476X_INTB_IRQ - set pin to be an interrupt request line
+ * @a1 - A1 pin function configuration:
+ * SI476X_A1_NOOP - do not modify the behaviour
+ * SI476X_A1_TRISTATE - put the pin in tristate condition,
+ * enable 1MOhm pulldown
+ * SI476X_A1_IRQ - set pin to be an interrupt request line
+ *
+ * Function returns 0 on success and negative error code on failure
+ */
+static int si476x_core_cmd_intb_pin_cfg_a10(struct si476x_core *core,
+ enum si476x_intb_config intb,
+ enum si476x_a1_config a1)
+{
+ u8 resp[CMD_INTB_PIN_CFG_A10_NRESP];
+ const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
+ PIN_CFG_BYTE(intb),
+ PIN_CFG_BYTE(a1),
+ };
+
+ return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+}
+
+static int si476x_core_cmd_intb_pin_cfg_a20(struct si476x_core *core,
+ enum si476x_intb_config intb,
+ enum si476x_a1_config a1)
+{
+ u8 resp[CMD_INTB_PIN_CFG_A20_NRESP];
+ const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
+ PIN_CFG_BYTE(intb),
+ PIN_CFG_BYTE(a1),
+ };
+
+ return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+}
+
+
+
+/**
+ * si476x_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the
+ * device
+ * @core - device to send the command to
+ * @rsqack - if set command clears RSQINT, SNRINT, SNRLINT, RSSIHINT,
+ * RSSSILINT, BLENDINT, MULTHINT and MULTLINT
+ * @attune - when set the values in the status report are the values
+ * that were calculated at tune
+ * @cancel - abort ongoing seek/tune opertation
+ * @stcack - clear the STCINT bin in status register
+ * @report - all signal quality information retured by the command
+ * (if NULL then the output of the command is ignored)
+ *
+ * Function returns 0 on success and negative error code on failure
+ */
+int si476x_core_cmd_am_rsq_status(struct si476x_core *core,
+ struct si476x_rsq_status_args *rsqargs,
+ struct si476x_rsq_status_report *report)
+{
+ int err;
+ u8 resp[CMD_AM_RSQ_STATUS_NRESP];
+ const u8 args[CMD_AM_RSQ_STATUS_NARGS] = {
+ rsqargs->rsqack << 3 | rsqargs->attune << 2 |
+ rsqargs->cancel << 1 | rsqargs->stcack,
+ };
+
+ err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+ /*
+ * Besides getting received signal quality information this
+ * command can be used to just acknowledge different interrupt
+ * flags in those cases it is useless to copy and parse
+ * received data so user can pass NULL, and thus avoid
+ * unnecessary copying.
+ */
+ if (!report)
+ return err;
+
+ report->snrhint = 0x08 & resp[1];
+ report->snrlint = 0x04 & resp[1];
+ report->rssihint = 0x02 & resp[1];
+ report->rssilint = 0x01 & resp[1];
+
+ report->bltf = 0x80 & resp[2];
+ report->snr_ready = 0x20 & resp[2];
+ report->rssiready = 0x08 & resp[2];
+ report->afcrl = 0x02 & resp[2];
+ report->valid = 0x01 & resp[2];
+
+ report->readfreq = get_unaligned_be16(resp + 3);
+ report->freqoff = resp[5];
+ report->rssi = resp[6];
+ report->snr = resp[7];
+ report->lassi = resp[9];
+ report->hassi = resp[10];
+ report->mult = resp[11];
+ report->dev = resp[12];
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status);
+
+int si476x_core_cmd_fm_acf_status(struct si476x_core *core,
+ struct si476x_acf_status_report *report)
+{
+ int err;
+ u8 resp[CMD_FM_ACF_STATUS_NRESP];
+ const u8 args[CMD_FM_ACF_STATUS_NARGS] = {
+ 0x0,
+ };
+
+ if (!report)
+ return -EINVAL;
+
+ err = si476x_core_send_command(core, CMD_FM_ACF_STATUS,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+ if (err < 0)
+ return err;
+
+ report->blend_int = resp[1] & SI476X_ACF_BLEND_INT;
+ report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT;
+ report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT;
+ report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT;
+ report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT;
+ report->smute = resp[2] & SI476X_ACF_SMUTE;
+ report->smattn = resp[3] & SI476X_ACF_SMATTN;
+ report->chbw = resp[4];
+ report->hicut = resp[5];
+ report->hiblend = resp[6];
+ report->pilot = resp[7] & SI476X_ACF_PILOT;
+ report->stblend = resp[7] & SI476X_ACF_STBLEND;
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status);
+
+int si476x_core_cmd_am_acf_status(struct si476x_core *core,
+ struct si476x_acf_status_report *report)
+{
+ int err;
+ u8 resp[CMD_AM_ACF_STATUS_NRESP];
+ const u8 args[CMD_AM_ACF_STATUS_NARGS] = {
+ 0x0,
+ };
+
+ if (!report)
+ return -EINVAL;
+
+ err = si476x_core_send_command(core, CMD_AM_ACF_STATUS,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+ if (err < 0)
+ return err;
+
+ report->blend_int = resp[1] & SI476X_ACF_BLEND_INT;
+ report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT;
+ report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT;
+ report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT;
+ report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT;
+ report->smute = resp[2] & SI476X_ACF_SMUTE;
+ report->smattn = resp[3] & SI476X_ACF_SMATTN;
+ report->chbw = resp[4];
+ report->hicut = resp[5];
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status);
+
+
+/**
+ * si476x_cmd_fm_seek_start - send 'FM_SEEK_START' command to the
+ * device
+ * @core - device to send the command to
+ * @seekup - if set the direction of the search is 'up'
+ * @wrap - if set seek wraps when hitting band limit
+ *
+ * This function begins search for a valid station. The station is
+ * considered valid when 'FM_VALID_SNR_THRESHOLD' and
+ * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
+ * are met.
+} *
+ * Function returns 0 on success and negative error code on failure
+ */
+int si476x_core_cmd_fm_seek_start(struct si476x_core *core,
+ bool seekup, bool wrap)
+{
+ u8 resp[CMD_FM_SEEK_START_NRESP];
+ const u8 args[CMD_FM_SEEK_START_NARGS] = {
+ seekup << 3 | wrap << 2,
+ };
+
+ return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START,
+ args, sizeof(args),
+ resp, sizeof(resp));
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start);
+
+/**
+ * si476x_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the
+ * device
+ * @core - device to send the command to
+ * @status_only - if set the data is not removed from RDSFIFO,
+ * RDSFIFOUSED is not decremented and data in all the
+ * rest RDS data contains the last valid info received
+ * @mtfifo if set the command clears RDS receive FIFO
+ * @intack if set the command clards the RDSINT bit.
+ *
+ * Function returns 0 on success and negative error code on failure
+ */
+int si476x_core_cmd_fm_rds_status(struct si476x_core *core,
+ bool status_only,
+ bool mtfifo,
+ bool intack,
+ struct si476x_rds_status_report *report)
+{
+ int err;
+ u8 resp[CMD_FM_RDS_STATUS_NRESP];
+ const u8 args[CMD_FM_RDS_STATUS_NARGS] = {
+ status_only << 2 | mtfifo << 1 | intack,
+ };
+
+ err = si476x_core_send_command(core, CMD_FM_RDS_STATUS,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+ /*
+ * Besides getting RDS status information this command can be
+ * used to just acknowledge different interrupt flags in those
+ * cases it is useless to copy and parse received data so user
+ * can pass NULL, and thus avoid unnecessary copying.
+ */
+ if (err < 0 || report == NULL)
+ return err;
+
+ report->rdstpptyint = 0x10 & resp[1];
+ report->rdspiint = 0x08 & resp[1];
+ report->rdssyncint = 0x02 & resp[1];
+ report->rdsfifoint = 0x01 & resp[1];
+
+ report->tpptyvalid = 0x10 & resp[2];
+ report->pivalid = 0x08 & resp[2];
+ report->rdssync = 0x02 & resp[2];
+ report->rdsfifolost = 0x01 & resp[2];
+
+ report->tp = 0x20 & resp[3];
+ report->pty = 0x1f & resp[3];
+
+ report->pi = get_unaligned_be16(resp + 4);
+ report->rdsfifoused = resp[6];
+
+ report->ble[V4L2_RDS_BLOCK_A] = 0xc0 & resp[7];
+ report->ble[V4L2_RDS_BLOCK_B] = 0x30 & resp[7];
+ report->ble[V4L2_RDS_BLOCK_C] = 0x0c & resp[7];
+ report->ble[V4L2_RDS_BLOCK_D] = 0x03 & resp[7];
+
+ report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A;
+ report->rds[V4L2_RDS_BLOCK_A].msb = resp[8];
+ report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9];
+
+ report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B;
+ report->rds[V4L2_RDS_BLOCK_B].msb = resp[10];
+ report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11];
+
+ report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C;
+ report->rds[V4L2_RDS_BLOCK_C].msb = resp[12];
+ report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13];
+
+ report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D;
+ report->rds[V4L2_RDS_BLOCK_D].msb = resp[14];
+ report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15];
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status);
+
+int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core,
+ bool clear,
+ struct si476x_rds_blockcount_report *report)
+{
+ int err;
+ u8 resp[CMD_FM_RDS_BLOCKCOUNT_NRESP];
+ const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = {
+ clear,
+ };
+
+ if (!report)
+ return -EINVAL;
+
+ err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+
+ if (!err) {
+ report->expected = get_unaligned_be16(resp + 2);
+ report->received = get_unaligned_be16(resp + 4);
+ report->uncorrectable = get_unaligned_be16(resp + 6);
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount);
+
+int si476x_core_cmd_fm_phase_diversity(struct si476x_core *core,
+ enum si476x_phase_diversity_mode mode)
+{
+ u8 resp[CMD_FM_PHASE_DIVERSITY_NRESP];
+ const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = {
+ mode & 0x07,
+ };
+
+ return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity);
+/**
+ * si476x_core_cmd_fm_phase_div_status() - get the phase diversity
+ * status
+ *
+ * @core: si476x device
+ *
+ * NOTE caller must hold core lock
+ *
+ * Function returns the value of the status bit in case of success and
+ * negative error code in case of failre.
+ */
+int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core)
+{
+ int err;
+ u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP];
+
+ err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS,
+ NULL, 0,
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+
+ return (err < 0) ? err : resp[1];
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status);
+
+
+/**
+ * si476x_cmd_am_seek_start - send 'FM_SEEK_START' command to the
+ * device
+ * @core - device to send the command to
+ * @seekup - if set the direction of the search is 'up'
+ * @wrap - if set seek wraps when hitting band limit
+ *
+ * This function begins search for a valid station. The station is
+ * considered valid when 'FM_VALID_SNR_THRESHOLD' and
+ * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
+ * are met.
+ *
+ * Function returns 0 on success and negative error code on failure
+ */
+int si476x_core_cmd_am_seek_start(struct si476x_core *core,
+ bool seekup, bool wrap)
+{
+ u8 resp[CMD_AM_SEEK_START_NRESP];
+ const u8 args[CMD_AM_SEEK_START_NARGS] = {
+ seekup << 3 | wrap << 2,
+ };
+
+ return si476x_cmd_tune_seek_freq(core, CMD_AM_SEEK_START,
+ args, sizeof(args),
+ resp, sizeof(resp));
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start);
+
+
+
+static int si476x_core_cmd_power_up_a10(struct si476x_core *core,
+ struct si476x_power_up_args *puargs)
+{
+ u8 resp[CMD_POWER_UP_A10_NRESP];
+ const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
+ const bool ctsen = (core->client->irq != 0);
+ const u8 args[CMD_POWER_UP_A10_NARGS] = {
+ 0xF7, /* Reserved, always 0xF7 */
+ 0x3F & puargs->xcload, /* First two bits are reserved to be
+ * zeros */
+ ctsen << 7 | intsel << 6 | 0x07, /* Last five bits
+ * are reserved to
+ * be written as 0x7 */
+ puargs->func << 4 | puargs->freq,
+ 0x11, /* Reserved, always 0x11 */
+ };
+
+ return si476x_core_send_command(core, CMD_POWER_UP,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_TIMEOUT_POWER_UP);
+}
+
+static int si476x_core_cmd_power_up_a20(struct si476x_core *core,
+ struct si476x_power_up_args *puargs)
+{
+ u8 resp[CMD_POWER_UP_A20_NRESP];
+ const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
+ const bool ctsen = (core->client->irq != 0);
+ const u8 args[CMD_POWER_UP_A20_NARGS] = {
+ puargs->ibias6x << 7 | puargs->xstart,
+ 0x3F & puargs->xcload, /* First two bits are reserved to be
+ * zeros */
+ ctsen << 7 | intsel << 6 | puargs->fastboot << 5 |
+ puargs->xbiashc << 3 | puargs->xbias,
+ puargs->func << 4 | puargs->freq,
+ 0x10 | puargs->xmode,
+ };
+
+ return si476x_core_send_command(core, CMD_POWER_UP,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_TIMEOUT_POWER_UP);
+}
+
+static int si476x_core_cmd_power_down_a10(struct si476x_core *core,
+ struct si476x_power_down_args *pdargs)
+{
+ u8 resp[CMD_POWER_DOWN_A10_NRESP];
+
+ return si476x_core_send_command(core, CMD_POWER_DOWN,
+ NULL, 0,
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+}
+
+static int si476x_core_cmd_power_down_a20(struct si476x_core *core,
+ struct si476x_power_down_args *pdargs)
+{
+ u8 resp[CMD_POWER_DOWN_A20_NRESP];
+ const u8 args[CMD_POWER_DOWN_A20_NARGS] = {
+ pdargs->xosc,
+ };
+ return si476x_core_send_command(core, CMD_POWER_DOWN,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+}
+
+static int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core,
+ struct si476x_tune_freq_args *tuneargs)
+{
+
+ const int am_freq = tuneargs->freq;
+ u8 resp[CMD_AM_TUNE_FREQ_NRESP];
+ const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
+ (tuneargs->hd << 6),
+ msb(am_freq),
+ lsb(am_freq),
+ };
+
+ return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args,
+ sizeof(args),
+ resp, sizeof(resp));
+}
+
+static int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core,
+ struct si476x_tune_freq_args *tuneargs)
+{
+ const int am_freq = tuneargs->freq;
+ u8 resp[CMD_AM_TUNE_FREQ_NRESP];
+ const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
+ (tuneargs->zifsr << 6) | (tuneargs->injside & 0x03),
+ msb(am_freq),
+ lsb(am_freq),
+ };
+
+ return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ,
+ args, sizeof(args),
+ resp, sizeof(resp));
+}
+
+static int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core,
+ struct si476x_rsq_status_args *rsqargs,
+ struct si476x_rsq_status_report *report)
+{
+ int err;
+ u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP];
+ const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = {
+ rsqargs->rsqack << 3 | rsqargs->attune << 2 |
+ rsqargs->cancel << 1 | rsqargs->stcack,
+ };
+
+ err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+ /*
+ * Besides getting received signal quality information this
+ * command can be used to just acknowledge different interrupt
+ * flags in those cases it is useless to copy and parse
+ * received data so user can pass NULL, and thus avoid
+ * unnecessary copying.
+ */
+ if (err < 0 || report == NULL)
+ return err;
+
+ report->multhint = 0x80 & resp[1];
+ report->multlint = 0x40 & resp[1];
+ report->snrhint = 0x08 & resp[1];
+ report->snrlint = 0x04 & resp[1];
+ report->rssihint = 0x02 & resp[1];
+ report->rssilint = 0x01 & resp[1];
+
+ report->bltf = 0x80 & resp[2];
+ report->snr_ready = 0x20 & resp[2];
+ report->rssiready = 0x08 & resp[2];
+ report->afcrl = 0x02 & resp[2];
+ report->valid = 0x01 & resp[2];
+
+ report->readfreq = get_unaligned_be16(resp + 3);
+ report->freqoff = resp[5];
+ report->rssi = resp[6];
+ report->snr = resp[7];
+ report->lassi = resp[9];
+ report->hassi = resp[10];
+ report->mult = resp[11];
+ report->dev = resp[12];
+ report->readantcap = get_unaligned_be16(resp + 13);
+ report->assi = resp[15];
+ report->usn = resp[16];
+
+ return err;
+}
+
+static int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core,
+ struct si476x_rsq_status_args *rsqargs,
+ struct si476x_rsq_status_report *report)
+{
+ int err;
+ u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP];
+ const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
+ rsqargs->primary << 4 | rsqargs->rsqack << 3 |
+ rsqargs->attune << 2 | rsqargs->cancel << 1 |
+ rsqargs->stcack,
+ };
+
+ err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+ /*
+ * Besides getting received signal quality information this
+ * command can be used to just acknowledge different interrupt
+ * flags in those cases it is useless to copy and parse
+ * received data so user can pass NULL, and thus avoid
+ * unnecessary copying.
+ */
+ if (err < 0 || report == NULL)
+ return err;
+
+ report->multhint = 0x80 & resp[1];
+ report->multlint = 0x40 & resp[1];
+ report->snrhint = 0x08 & resp[1];
+ report->snrlint = 0x04 & resp[1];
+ report->rssihint = 0x02 & resp[1];
+ report->rssilint = 0x01 & resp[1];
+
+ report->bltf = 0x80 & resp[2];
+ report->snr_ready = 0x20 & resp[2];
+ report->rssiready = 0x08 & resp[2];
+ report->afcrl = 0x02 & resp[2];
+ report->valid = 0x01 & resp[2];
+
+ report->readfreq = get_unaligned_be16(resp + 3);
+ report->freqoff = resp[5];
+ report->rssi = resp[6];
+ report->snr = resp[7];
+ report->lassi = resp[9];
+ report->hassi = resp[10];
+ report->mult = resp[11];
+ report->dev = resp[12];
+ report->readantcap = get_unaligned_be16(resp + 13);
+ report->assi = resp[15];
+ report->usn = resp[16];
+
+ return err;
+}
+
+
+static int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core,
+ struct si476x_rsq_status_args *rsqargs,
+ struct si476x_rsq_status_report *report)
+{
+ int err;
+ u8 resp[CMD_FM_RSQ_STATUS_A30_NRESP];
+ const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
+ rsqargs->primary << 4 | rsqargs->rsqack << 3 |
+ rsqargs->attune << 2 | rsqargs->cancel << 1 |
+ rsqargs->stcack,
+ };
+
+ err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
+ args, ARRAY_SIZE(args),
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+ /*
+ * Besides getting received signal quality information this
+ * command can be used to just acknowledge different interrupt
+ * flags in those cases it is useless to copy and parse
+ * received data so user can pass NULL, and thus avoid
+ * unnecessary copying.
+ */
+ if (err < 0 || report == NULL)
+ return err;
+
+ report->multhint = 0x80 & resp[1];
+ report->multlint = 0x40 & resp[1];
+ report->snrhint = 0x08 & resp[1];
+ report->snrlint = 0x04 & resp[1];
+ report->rssihint = 0x02 & resp[1];
+ report->rssilint = 0x01 & resp[1];
+
+ report->bltf = 0x80 & resp[2];
+ report->snr_ready = 0x20 & resp[2];
+ report->rssiready = 0x08 & resp[2];
+ report->injside = 0x04 & resp[2];
+ report->afcrl = 0x02 & resp[2];
+ report->valid = 0x01 & resp[2];
+
+ report->readfreq = get_unaligned_be16(resp + 3);
+ report->freqoff = resp[5];
+ report->rssi = resp[6];
+ report->snr = resp[7];
+ report->issi = resp[8];
+ report->lassi = resp[9];
+ report->hassi = resp[10];
+ report->mult = resp[11];
+ report->dev = resp[12];
+ report->readantcap = get_unaligned_be16(resp + 13);
+ report->assi = resp[15];
+ report->usn = resp[16];
+
+ report->pilotdev = resp[17];
+ report->rdsdev = resp[18];
+ report->assidev = resp[19];
+ report->strongdev = resp[20];
+ report->rdspi = get_unaligned_be16(resp + 21);
+
+ return err;
+}
+
+static int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core,
+ struct si476x_tune_freq_args *tuneargs)
+{
+ u8 resp[CMD_FM_TUNE_FREQ_NRESP];
+ const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = {
+ (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
+ | (tuneargs->smoothmetrics << 2),
+ msb(tuneargs->freq),
+ lsb(tuneargs->freq),
+ msb(tuneargs->antcap),
+ lsb(tuneargs->antcap)
+ };
+
+ return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
+ args, sizeof(args),
+ resp, sizeof(resp));
+}
+
+static int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core,
+ struct si476x_tune_freq_args *tuneargs)
+{
+ u8 resp[CMD_FM_TUNE_FREQ_NRESP];
+ const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = {
+ (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
+ | (tuneargs->smoothmetrics << 2) | (tuneargs->injside),
+ msb(tuneargs->freq),
+ lsb(tuneargs->freq),
+ };
+
+ return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
+ args, sizeof(args),
+ resp, sizeof(resp));
+}
+
+static int si476x_core_cmd_agc_status_a20(struct si476x_core *core,
+ struct si476x_agc_status_report *report)
+{
+ int err;
+ u8 resp[CMD_AGC_STATUS_NRESP_A20];
+
+ if (!report)
+ return -EINVAL;
+
+ err = si476x_core_send_command(core, CMD_AGC_STATUS,
+ NULL, 0,
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+ if (err < 0)
+ return err;
+
+ report->mxhi = resp[1] & SI476X_AGC_MXHI;
+ report->mxlo = resp[1] & SI476X_AGC_MXLO;
+ report->lnahi = resp[1] & SI476X_AGC_LNAHI;
+ report->lnalo = resp[1] & SI476X_AGC_LNALO;
+ report->fmagc1 = resp[2];
+ report->fmagc2 = resp[3];
+ report->pgagain = resp[4];
+ report->fmwblang = resp[5];
+
+ return err;
+}
+
+static int si476x_core_cmd_agc_status_a10(struct si476x_core *core,
+ struct si476x_agc_status_report *report)
+{
+ int err;
+ u8 resp[CMD_AGC_STATUS_NRESP_A10];
+
+ if (!report)
+ return -EINVAL;
+
+ err = si476x_core_send_command(core, CMD_AGC_STATUS,
+ NULL, 0,
+ resp, ARRAY_SIZE(resp),
+ SI476X_DEFAULT_TIMEOUT);
+ if (err < 0)
+ return err;
+
+ report->mxhi = resp[1] & SI476X_AGC_MXHI;
+ report->mxlo = resp[1] & SI476X_AGC_MXLO;
+ report->lnahi = resp[1] & SI476X_AGC_LNAHI;
+ report->lnalo = resp[1] & SI476X_AGC_LNALO;
+
+ return err;
+}
+
+typedef int (*tune_freq_func_t) (struct si476x_core *core,
+ struct si476x_tune_freq_args *tuneargs);
+
+static struct {
+ int (*power_up) (struct si476x_core *,
+ struct si476x_power_up_args *);
+ int (*power_down) (struct si476x_core *,
+ struct si476x_power_down_args *);
+
+ tune_freq_func_t fm_tune_freq;
+ tune_freq_func_t am_tune_freq;
+
+ int (*fm_rsq_status)(struct si476x_core *,
+ struct si476x_rsq_status_args *,
+ struct si476x_rsq_status_report *);
+
+ int (*agc_status)(struct si476x_core *,
+ struct si476x_agc_status_report *);
+ int (*intb_pin_cfg)(struct si476x_core *core,
+ enum si476x_intb_config intb,
+ enum si476x_a1_config a1);
+} si476x_cmds_vtable[] = {
+ [SI476X_REVISION_A10] = {
+ .power_up = si476x_core_cmd_power_up_a10,
+ .power_down = si476x_core_cmd_power_down_a10,
+ .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a10,
+ .am_tune_freq = si476x_core_cmd_am_tune_freq_a10,
+ .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a10,
+ .agc_status = si476x_core_cmd_agc_status_a10,
+ .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a10,
+ },
+ [SI476X_REVISION_A20] = {
+ .power_up = si476x_core_cmd_power_up_a20,
+ .power_down = si476x_core_cmd_power_down_a20,
+ .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20,
+ .am_tune_freq = si476x_core_cmd_am_tune_freq_a20,
+ .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a20,
+ .agc_status = si476x_core_cmd_agc_status_a20,
+ .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20,
+ },
+ [SI476X_REVISION_A30] = {
+ .power_up = si476x_core_cmd_power_up_a20,
+ .power_down = si476x_core_cmd_power_down_a20,
+ .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20,
+ .am_tune_freq = si476x_core_cmd_am_tune_freq_a20,
+ .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a30,
+ .agc_status = si476x_core_cmd_agc_status_a20,
+ .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20,
+ },
+};
+
+int si476x_core_cmd_power_up(struct si476x_core *core,
+ struct si476x_power_up_args *args)
+{
+ BUG_ON(core->revision > SI476X_REVISION_A30 ||
+ core->revision == -1);
+ return si476x_cmds_vtable[core->revision].power_up(core, args);
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up);
+
+int si476x_core_cmd_power_down(struct si476x_core *core,
+ struct si476x_power_down_args *args)
+{
+ BUG_ON(core->revision > SI476X_REVISION_A30 ||
+ core->revision == -1);
+ return si476x_cmds_vtable[core->revision].power_down(core, args);
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down);
+
+int si476x_core_cmd_fm_tune_freq(struct si476x_core *core,
+ struct si476x_tune_freq_args *args)
+{
+ BUG_ON(core->revision > SI476X_REVISION_A30 ||
+ core->revision == -1);
+ return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args);
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq);
+
+int si476x_core_cmd_am_tune_freq(struct si476x_core *core,
+ struct si476x_tune_freq_args *args)
+{
+ BUG_ON(core->revision > SI476X_REVISION_A30 ||
+ core->revision == -1);
+ return si476x_cmds_vtable[core->revision].am_tune_freq(core, args);
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq);
+
+int si476x_core_cmd_fm_rsq_status(struct si476x_core *core,
+ struct si476x_rsq_status_args *args,
+ struct si476x_rsq_status_report *report)
+
+{
+ BUG_ON(core->revision > SI476X_REVISION_A30 ||
+ core->revision == -1);
+ return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args,
+ report);
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status);
+
+int si476x_core_cmd_agc_status(struct si476x_core *core,
+ struct si476x_agc_status_report *report)
+
+{
+ BUG_ON(core->revision > SI476X_REVISION_A30 ||
+ core->revision == -1);
+ return si476x_cmds_vtable[core->revision].agc_status(core, report);
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status);
+
+int si476x_core_cmd_intb_pin_cfg(struct si476x_core *core,
+ enum si476x_intb_config intb,
+ enum si476x_a1_config a1)
+{
+ BUG_ON(core->revision > SI476X_REVISION_A30 ||
+ core->revision == -1);
+
+ return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1);
+}
+EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
+MODULE_DESCRIPTION("API for command exchange for si476x");
diff --git a/drivers/mfd/si476x-i2c.c b/drivers/mfd/si476x-i2c.c
new file mode 100644
index 00000000000..0e4a76daf18
--- /dev/null
+++ b/drivers/mfd/si476x-i2c.c
@@ -0,0 +1,886 @@
+/*
+ * drivers/mfd/si476x-i2c.c -- Core device driver for si476x MFD
+ * device
+ *
+ * Copyright (C) 2012 Innovative Converged Devices(ICD)
+ * Copyright (C) 2013 Andrey Smirnov
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.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; version 2 of the License.
+ *
+ * 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.
+ *
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+
+#include <linux/mfd/si476x-core.h>
+
+#define SI476X_MAX_IO_ERRORS 10
+#define SI476X_DRIVER_RDS_FIFO_DEPTH 128
+
+/**
+ * si476x_core_config_pinmux() - pin function configuration function
+ *
+ * @core: Core device structure
+ *
+ * Configure the functions of the pins of the radio chip.
+ *
+ * The function returns zero in case of succes or negative error code
+ * otherwise.
+ */
+static int si476x_core_config_pinmux(struct si476x_core *core)
+{
+ int err;
+ dev_dbg(&core->client->dev, "Configuring pinmux\n");
+ err = si476x_core_cmd_dig_audio_pin_cfg(core,
+ core->pinmux.dclk,
+ core->pinmux.dfs,
+ core->pinmux.dout,
+ core->pinmux.xout);
+ if (err < 0) {
+ dev_err(&core->client->dev,
+ "Failed to configure digital audio pins(err = %d)\n",
+ err);
+ return err;
+ }
+
+ err = si476x_core_cmd_zif_pin_cfg(core,
+ core->pinmux.iqclk,
+ core->pinmux.iqfs,
+ core->pinmux.iout,
+ core->pinmux.qout);
+ if (err < 0) {
+ dev_err(&core->client->dev,
+ "Failed to configure ZIF pins(err = %d)\n",
+ err);
+ return err;
+ }
+
+ err = si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(core,
+ core->pinmux.icin,
+ core->pinmux.icip,
+ core->pinmux.icon,
+ core->pinmux.icop);
+ if (err < 0) {
+ dev_err(&core->client->dev,
+ "Failed to configure IC-Link/GPO pins(err = %d)\n",
+ err);
+ return err;
+ }
+
+ err = si476x_core_cmd_ana_audio_pin_cfg(core,
+ core->pinmux.lrout);
+ if (err < 0) {
+ dev_err(&core->client->dev,
+ "Failed to configure analog audio pins(err = %d)\n",
+ err);
+ return err;
+ }
+
+ err = si476x_core_cmd_intb_pin_cfg(core,
+ core->pinmux.intb,
+ core->pinmux.a1);
+ if (err < 0) {
+ dev_err(&core->client->dev,
+ "Failed to configure interrupt pins(err = %d)\n",
+ err);
+ return err;
+ }
+
+ return 0;
+}
+
+static inline void si476x_core_schedule_polling_work(struct si476x_core *core)
+{
+ schedule_delayed_work(&core->status_monitor,
+ usecs_to_jiffies(SI476X_STATUS_POLL_US));
+}
+
+/**
+ * si476x_core_start() - early chip startup function
+ * @core: Core device structure
+ * @soft: When set, this flag forces "soft" startup, where "soft"
+ * power down is the one done by sending appropriate command instead
+ * of using reset pin of the tuner
+ *
+ * Perform required startup sequence to correctly power
+ * up the chip and perform initial configuration. It does the
+ * following sequence of actions:
+ * 1. Claims and enables the power supplies VD and VIO1 required
+ * for I2C interface of the chip operation.
+ * 2. Waits for 100us, pulls the reset line up, enables irq,
+ * waits for another 100us as it is specified by the
+ * datasheet.
+ * 3. Sends 'POWER_UP' command to the device with all provided
+ * information about power-up parameters.
+ * 4. Configures, pin multiplexor, disables digital audio and
+ * configures interrupt sources.
+ *
+ * The function returns zero in case of succes or negative error code
+ * otherwise.
+ */
+int si476x_core_start(struct si476x_core *core, bool soft)
+{
+ struct i2c_client *client = core->client;
+ int err;
+
+ if (!soft) {
+ if (gpio_is_valid(core->gpio_reset))
+ gpio_set_value_cansleep(core->gpio_reset, 1);
+
+ if (client->irq)
+ enable_irq(client->irq);
+
+ udelay(100);
+
+ if (!client->irq) {
+ atomic_set(&core->is_alive, 1);
+ si476x_core_schedule_polling_work(core);
+ }
+ } else {
+ if (client->irq)
+ enable_irq(client->irq);
+ else {
+ atomic_set(&core->is_alive, 1);
+ si476x_core_schedule_polling_work(core);
+ }
+ }
+
+ err = si476x_core_cmd_power_up(core,
+ &core->power_up_parameters);
+
+ if (err < 0) {
+ dev_err(&core->client->dev,
+ "Power up failure(err = %d)\n",
+ err);
+ goto disable_irq;
+ }
+
+ if (client->irq)
+ atomic_set(&core->is_alive, 1);
+
+ err = si476x_core_config_pinmux(core);
+ if (err < 0) {
+ dev_err(&core->client->dev,
+ "Failed to configure pinmux(err = %d)\n",
+ err);
+ goto disable_irq;
+ }
+
+ if (client->irq) {
+ err = regmap_write(core->regmap,
+ SI476X_PROP_INT_CTL_ENABLE,
+ SI476X_RDSIEN |
+ SI476X_STCIEN |
+ SI476X_CTSIEN);
+ if (err < 0) {
+ dev_err(&core->client->dev,
+ "Failed to configure interrupt sources"
+ "(err = %d)\n", err);
+ goto disable_irq;
+ }
+ }
+
+ return 0;
+
+disable_irq:
+ if (err == -ENODEV)
+ atomic_set(&core->is_alive, 0);
+
+ if (client->irq)
+ disable_irq(client->irq);
+ else
+ cancel_delayed_work_sync(&core->status_monitor);
+
+ if (gpio_is_valid(core->gpio_reset))
+ gpio_set_value_cansleep(core->gpio_reset, 0);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(si476x_core_start);
+
+/**
+ * si476x_core_stop() - chip power-down function
+ * @core: Core device structure
+ * @soft: When set, function sends a POWER_DOWN command instead of
+ * bringing reset line low
+ *
+ * Power down the chip by performing following actions:
+ * 1. Disable IRQ or stop the polling worker
+ * 2. Send the POWER_DOWN command if the power down is soft or bring
+ * reset line low if not.
+ *
+ * The function returns zero in case of succes or negative error code
+ * otherwise.
+ */
+int si476x_core_stop(struct si476x_core *core, bool soft)
+{
+ int err = 0;
+ atomic_set(&core->is_alive, 0);
+
+ if (soft) {
+ /* TODO: This probably shoud be a configurable option,
+ * so it is possible to have the chips keep their
+ * oscillators running
+ */
+ struct si476x_power_down_args args = {
+ .xosc = false,
+ };
+ err = si476x_core_cmd_power_down(core, &args);
+ }
+
+ /* We couldn't disable those before
+ * 'si476x_core_cmd_power_down' since we expect to get CTS
+ * interrupt */
+ if (core->client->irq)
+ disable_irq(core->client->irq);
+ else
+ cancel_delayed_work_sync(&core->status_monitor);
+
+ if (!soft) {
+ if (gpio_is_valid(core->gpio_reset))
+ gpio_set_value_cansleep(core->gpio_reset, 0);
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(si476x_core_stop);
+
+/**
+ * si476x_core_set_power_state() - set the level at which the power is
+ * supplied for the chip.
+ * @core: Core device structure
+ * @next_state: enum si476x_power_state describing power state to
+ * switch to.
+ *
+ * Switch on all the required power supplies
+ *
+ * This function returns 0 in case of suvccess and negative error code
+ * otherwise.
+ */
+int si476x_core_set_power_state(struct si476x_core *core,
+ enum si476x_power_state next_state)
+{
+ /*
+ It is not clear form the datasheet if it is possible to
+ work with device if not all power domains are operational.
+ So for now the power-up policy is "power-up all the things!"
+ */
+ int err = 0;
+
+ if (core->power_state == SI476X_POWER_INCONSISTENT) {
+ dev_err(&core->client->dev,
+ "The device in inconsistent power state\n");
+ return -EINVAL;
+ }
+
+ if (next_state != core->power_state) {
+ switch (next_state) {
+ case SI476X_POWER_UP_FULL:
+ err = regulator_bulk_enable(ARRAY_SIZE(core->supplies),
+ core->supplies);
+ if (err < 0) {
+ core->power_state = SI476X_POWER_INCONSISTENT;
+ break;
+ }
+ /*
+ * Startup timing diagram recommends to have a
+ * 100 us delay between enabling of the power
+ * supplies and turning the tuner on.
+ */
+ udelay(100);
+
+ err = si476x_core_start(core, false);
+ if (err < 0)
+ goto disable_regulators;
+
+ core->power_state = next_state;
+ break;
+
+ case SI476X_POWER_DOWN:
+ core->power_state = next_state;
+ err = si476x_core_stop(core, false);
+ if (err < 0)
+ core->power_state = SI476X_POWER_INCONSISTENT;
+disable_regulators:
+ err = regulator_bulk_disable(ARRAY_SIZE(core->supplies),
+ core->supplies);
+ if (err < 0)
+ core->power_state = SI476X_POWER_INCONSISTENT;
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(si476x_core_set_power_state);
+
+/**
+ * si476x_core_report_drainer_stop() - mark the completion of the RDS
+ * buffer drain porcess by the worker.
+ *
+ * @core: Core device structure
+ */
+static inline void si476x_core_report_drainer_stop(struct si476x_core *core)
+{
+ mutex_lock(&core->rds_drainer_status_lock);
+ core->rds_drainer_is_working = false;
+ mutex_unlock(&core->rds_drainer_status_lock);
+}
+
+/**
+ * si476x_core_start_rds_drainer_once() - start RDS drainer worker if
+ * ther is none working, do nothing otherwise
+ *
+ * @core: Datastructure corresponding to the chip.
+ */
+static inline void si476x_core_start_rds_drainer_once(struct si476x_core *core)
+{
+ mutex_lock(&core->rds_drainer_status_lock);
+ if (!core->rds_drainer_is_working) {
+ core->rds_drainer_is_working = true;
+ schedule_work(&core->rds_fifo_drainer);
+ }
+ mutex_unlock(&core->rds_drainer_status_lock);
+}
+/**
+ * si476x_drain_rds_fifo() - RDS buffer drainer.
+ * @work: struct work_struct being ppassed to the function by the
+ * kernel.
+ *
+ * Drain the contents of the RDS FIFO of
+ */
+static void si476x_core_drain_rds_fifo(struct work_struct *work)
+{
+ int err;
+
+ struct si476x_core *core = container_of(work, struct si476x_core,
+ rds_fifo_drainer);
+
+ struct si476x_rds_status_report report;
+
+ si476x_core_lock(core);
+ err = si476x_core_cmd_fm_rds_status(core, true, false, false, &report);
+ if (!err) {
+ int i = report.rdsfifoused;
+ dev_dbg(&core->client->dev,
+ "%d elements in RDS FIFO. Draining.\n", i);
+ for (; i > 0; --i) {
+ err = si476x_core_cmd_fm_rds_status(core, false, false,
+ (i == 1), &report);
+ if (err < 0)
+ goto unlock;
+
+ kfifo_in(&core->rds_fifo, report.rds,
+ sizeof(report.rds));
+ dev_dbg(&core->client->dev, "RDS data:\n %*ph\n",
+ (int)sizeof(report.rds), report.rds);
+ }
+ dev_dbg(&core->client->dev, "Drrrrained!\n");
+ wake_up_interruptible(&core->rds_read_queue);
+ }
+
+unlock:
+ si476x_core_unlock(core);
+ si476x_core_report_drainer_stop(core);
+}
+
+/**
+ * si476x_core_pronounce_dead()
+ *
+ * @core: Core device structure
+ *
+ * Mark the device as being dead and wake up all potentially waiting
+ * threads of execution.
+ *
+ */
+static void si476x_core_pronounce_dead(struct si476x_core *core)
+{
+ dev_info(&core->client->dev, "Core device is dead.\n");
+
+ atomic_set(&core->is_alive, 0);
+
+ /* Wake up al possible waiting processes */
+ wake_up_interruptible(&core->rds_read_queue);
+
+ atomic_set(&core->cts, 1);
+ wake_up(&core->command);
+
+ atomic_set(&core->stc, 1);
+ wake_up(&core->tuning);
+}
+
+/**
+ * si476x_core_i2c_xfer()
+ *
+ * @core: Core device structure
+ * @type: Transfer type
+ * @buf: Transfer buffer for/with data
+ * @count: Transfer buffer size
+ *
+ * Perfrom and I2C transfer(either read or write) and keep a counter
+ * of I/O errors. If the error counter rises above the threshold
+ * pronounce device dead.
+ *
+ * The function returns zero on succes or negative error code on
+ * failure.
+ */
+int si476x_core_i2c_xfer(struct si476x_core *core,
+ enum si476x_i2c_type type,
+ char *buf, int count)
+{
+ static int io_errors_count;
+ int err;
+ if (type == SI476X_I2C_SEND)
+ err = i2c_master_send(core->client, buf, count);
+ else
+ err = i2c_master_recv(core->client, buf, count);
+
+ if (err < 0) {
+ if (io_errors_count++ > SI476X_MAX_IO_ERRORS)
+ si476x_core_pronounce_dead(core);
+ } else {
+ io_errors_count = 0;
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(si476x_core_i2c_xfer);
+
+/**
+ * si476x_get_status()
+ * @core: Core device structure
+ *
+ * Get the status byte of the core device by berforming one byte I2C
+ * read.
+ *
+ * The function returns a status value or a negative error code on
+ * error.
+ */
+static int si476x_core_get_status(struct si476x_core *core)
+{
+ u8 response;
+ int err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV,
+ &response, sizeof(response));
+
+ return (err < 0) ? err : response;
+}
+
+/**
+ * si476x_get_and_signal_status() - IRQ dispatcher
+ * @core: Core device structure
+ *
+ * Dispatch the arrived interrupt request based on the value of the
+ * status byte reported by the tuner.
+ *
+ */
+static void si476x_core_get_and_signal_status(struct si476x_core *core)
+{
+ int status = si476x_core_get_status(core);
+ if (status < 0) {
+ dev_err(&core->client->dev, "Failed to get status\n");
+ return;
+ }
+
+ if (status & SI476X_CTS) {
+ /* Unfortunately completions could not be used for
+ * signalling CTS since this flag cannot be cleared
+ * in status byte, and therefore once it becomes true
+ * multiple calls to 'complete' would cause the
+ * commands following the current one to be completed
+ * before they actually are */
+ dev_dbg(&core->client->dev, "[interrupt] CTSINT\n");
+ atomic_set(&core->cts, 1);
+ wake_up(&core->command);
+ }
+
+ if (status & SI476X_FM_RDS_INT) {
+ dev_dbg(&core->client->dev, "[interrupt] RDSINT\n");
+ si476x_core_start_rds_drainer_once(core);
+ }
+
+ if (status & SI476X_STC_INT) {
+ dev_dbg(&core->client->dev, "[interrupt] STCINT\n");
+ atomic_set(&core->stc, 1);
+ wake_up(&core->tuning);
+ }
+}
+
+static void si476x_core_poll_loop(struct work_struct *work)
+{
+ struct si476x_core *core = SI476X_WORK_TO_CORE(work);
+
+ si476x_core_get_and_signal_status(core);
+
+ if (atomic_read(&core->is_alive))
+ si476x_core_schedule_polling_work(core);
+}
+
+static irqreturn_t si476x_core_interrupt(int irq, void *dev)
+{
+ struct si476x_core *core = dev;
+
+ si476x_core_get_and_signal_status(core);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * si476x_firmware_version_to_revision()
+ * @core: Core device structure
+ * @major: Firmware major number
+ * @minor1: Firmware first minor number
+ * @minor2: Firmware second minor number
+ *
+ * Convert a chip's firmware version number into an offset that later
+ * will be used to as offset in "vtable" of tuner functions
+ *
+ * This function returns a positive offset in case of success and a -1
+ * in case of failure.
+ */
+static int si476x_core_fwver_to_revision(struct si476x_core *core,
+ int func, int major,
+ int minor1, int minor2)
+{
+ switch (func) {
+ case SI476X_FUNC_FM_RECEIVER:
+ switch (major) {
+ case 5:
+ return SI476X_REVISION_A10;
+ case 8:
+ return SI476X_REVISION_A20;
+ case 10:
+ return SI476X_REVISION_A30;
+ default:
+ goto unknown_revision;
+ }
+ case SI476X_FUNC_AM_RECEIVER:
+ switch (major) {
+ case 5:
+ return SI476X_REVISION_A10;
+ case 7:
+ return SI476X_REVISION_A20;
+ case 9:
+ return SI476X_REVISION_A30;
+ default:
+ goto unknown_revision;
+ }
+ case SI476X_FUNC_WB_RECEIVER:
+ switch (major) {
+ case 3:
+ return SI476X_REVISION_A10;
+ case 5:
+ return SI476X_REVISION_A20;
+ case 7:
+ return SI476X_REVISION_A30;
+ default:
+ goto unknown_revision;
+ }
+ case SI476X_FUNC_BOOTLOADER:
+ default: /* FALLTHROUG */
+ BUG();
+ return -1;
+ }
+
+unknown_revision:
+ dev_err(&core->client->dev,
+ "Unsupported version of the firmware: %d.%d.%d, "
+ "reverting to A10 comptible functions\n",
+ major, minor1, minor2);
+
+ return SI476X_REVISION_A10;
+}
+
+/**
+ * si476x_get_revision_info()
+ * @core: Core device structure
+ *
+ * Get the firmware version number of the device. It is done in
+ * following three steps:
+ * 1. Power-up the device
+ * 2. Send the 'FUNC_INFO' command
+ * 3. Powering the device down.
+ *
+ * The function return zero on success and a negative error code on
+ * failure.
+ */
+static int si476x_core_get_revision_info(struct si476x_core *core)
+{
+ int rval;
+ struct si476x_func_info info;
+
+ si476x_core_lock(core);
+ rval = si476x_core_set_power_state(core, SI476X_POWER_UP_FULL);
+ if (rval < 0)
+ goto exit;
+
+ rval = si476x_core_cmd_func_info(core, &info);
+ if (rval < 0)
+ goto power_down;
+
+ core->revision = si476x_core_fwver_to_revision(core, info.func,
+ info.firmware.major,
+ info.firmware.minor[0],
+ info.firmware.minor[1]);
+power_down:
+ si476x_core_set_power_state(core, SI476X_POWER_DOWN);
+exit:
+ si476x_core_unlock(core);
+
+ return rval;
+}
+
+bool si476x_core_has_am(struct si476x_core *core)
+{
+ return core->chip_id == SI476X_CHIP_SI4761 ||
+ core->chip_id == SI476X_CHIP_SI4764;
+}
+EXPORT_SYMBOL_GPL(si476x_core_has_am);
+
+bool si476x_core_has_diversity(struct si476x_core *core)
+{
+ return core->chip_id == SI476X_CHIP_SI4764;
+}
+EXPORT_SYMBOL_GPL(si476x_core_has_diversity);
+
+bool si476x_core_is_a_secondary_tuner(struct si476x_core *core)
+{
+ return si476x_core_has_diversity(core) &&
+ (core->diversity_mode == SI476X_PHDIV_SECONDARY_ANTENNA ||
+ core->diversity_mode == SI476X_PHDIV_SECONDARY_COMBINING);
+}
+EXPORT_SYMBOL_GPL(si476x_core_is_a_secondary_tuner);
+
+bool si476x_core_is_a_primary_tuner(struct si476x_core *core)
+{
+ return si476x_core_has_diversity(core) &&
+ (core->diversity_mode == SI476X_PHDIV_PRIMARY_ANTENNA ||
+ core->diversity_mode == SI476X_PHDIV_PRIMARY_COMBINING);
+}
+EXPORT_SYMBOL_GPL(si476x_core_is_a_primary_tuner);
+
+bool si476x_core_is_in_am_receiver_mode(struct si476x_core *core)
+{
+ return si476x_core_has_am(core) &&
+ (core->power_up_parameters.func == SI476X_FUNC_AM_RECEIVER);
+}
+EXPORT_SYMBOL_GPL(si476x_core_is_in_am_receiver_mode);
+
+bool si476x_core_is_powered_up(struct si476x_core *core)
+{
+ return core->power_state == SI476X_POWER_UP_FULL;
+}
+EXPORT_SYMBOL_GPL(si476x_core_is_powered_up);
+
+static int si476x_core_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rval;
+ struct si476x_core *core;
+ struct si476x_platform_data *pdata;
+ struct mfd_cell *cell;
+ int cell_num;
+
+ core = devm_kzalloc(&client->dev, sizeof(*core), GFP_KERNEL);
+ if (!core) {
+ dev_err(&client->dev,
+ "failed to allocate 'struct si476x_core'\n");
+ return -ENOMEM;
+ }
+ core->client = client;
+
+ core->regmap = devm_regmap_init_si476x(core);
+ if (IS_ERR(core->regmap)) {
+ rval = PTR_ERR(core->regmap);
+ dev_err(&client->dev,
+ "Failed to allocate register map: %d\n",
+ rval);
+ return rval;
+ }
+
+ i2c_set_clientdata(client, core);
+
+ atomic_set(&core->is_alive, 0);
+ core->power_state = SI476X_POWER_DOWN;
+
+ pdata = dev_get_platdata(&client->dev);
+ if (pdata) {
+ memcpy(&core->power_up_parameters,
+ &pdata->power_up_parameters,
+ sizeof(core->power_up_parameters));
+
+ core->gpio_reset = -1;
+ if (gpio_is_valid(pdata->gpio_reset)) {
+ rval = gpio_request(pdata->gpio_reset, "si476x reset");
+ if (rval) {
+ dev_err(&client->dev,
+ "Failed to request gpio: %d\n", rval);
+ return rval;
+ }
+ core->gpio_reset = pdata->gpio_reset;
+ gpio_direction_output(core->gpio_reset, 0);
+ }
+
+ core->diversity_mode = pdata->diversity_mode;
+ memcpy(&core->pinmux, &pdata->pinmux,
+ sizeof(struct si476x_pinmux));
+ } else {
+ dev_err(&client->dev, "No platform data provided\n");
+ return -EINVAL;
+ }
+
+ core->supplies[0].supply = "vd";
+ core->supplies[1].supply = "va";
+ core->supplies[2].supply = "vio1";
+ core->supplies[3].supply = "vio2";
+
+ rval = devm_regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(core->supplies),
+ core->supplies);
+ if (rval) {
+ dev_err(&client->dev, "Failet to gett all of the regulators\n");
+ goto free_gpio;
+ }
+
+ mutex_init(&core->cmd_lock);
+ init_waitqueue_head(&core->command);
+ init_waitqueue_head(&core->tuning);
+
+ rval = kfifo_alloc(&core->rds_fifo,
+ SI476X_DRIVER_RDS_FIFO_DEPTH *
+ sizeof(struct v4l2_rds_data),
+ GFP_KERNEL);
+ if (rval) {
+ dev_err(&client->dev, "Could not alloate the FIFO\n");
+ goto free_gpio;
+ }
+ mutex_init(&core->rds_drainer_status_lock);
+ init_waitqueue_head(&core->rds_read_queue);
+ INIT_WORK(&core->rds_fifo_drainer, si476x_core_drain_rds_fifo);
+
+ if (client->irq) {
+ rval = devm_request_threaded_irq(&client->dev,
+ client->irq, NULL,
+ si476x_core_interrupt,
+ IRQF_TRIGGER_FALLING,
+ client->name, core);
+ if (rval < 0) {
+ dev_err(&client->dev, "Could not request IRQ %d\n",
+ client->irq);
+ goto free_kfifo;
+ }
+ disable_irq(client->irq);
+ dev_dbg(&client->dev, "IRQ requested.\n");
+
+ core->rds_fifo_depth = 20;
+ } else {
+ INIT_DELAYED_WORK(&core->status_monitor,
+ si476x_core_poll_loop);
+ dev_info(&client->dev,
+ "No IRQ number specified, will use polling\n");
+
+ core->rds_fifo_depth = 5;
+ }
+
+ core->chip_id = id->driver_data;
+
+ rval = si476x_core_get_revision_info(core);
+ if (rval < 0) {
+ rval = -ENODEV;
+ goto free_kfifo;
+ }
+
+ cell_num = 0;
+
+ cell = &core->cells[SI476X_RADIO_CELL];
+ cell->name = "si476x-radio";
+ cell_num++;
+
+#ifdef CONFIG_SND_SOC_SI476X
+ if ((core->chip_id == SI476X_CHIP_SI4761 ||
+ core->chip_id == SI476X_CHIP_SI4764) &&
+ core->pinmux.dclk == SI476X_DCLK_DAUDIO &&
+ core->pinmux.dfs == SI476X_DFS_DAUDIO &&
+ core->pinmux.dout == SI476X_DOUT_I2S_OUTPUT &&
+ core->pinmux.xout == SI476X_XOUT_TRISTATE) {
+ cell = &core->cells[SI476X_CODEC_CELL];
+ cell->name = "si476x-codec";
+ cell_num++;
+ }
+#endif
+ rval = mfd_add_devices(&client->dev,
+ (client->adapter->nr << 8) + client->addr,
+ core->cells, cell_num,
+ NULL, 0, NULL);
+ if (!rval)
+ return 0;
+
+free_kfifo:
+ kfifo_free(&core->rds_fifo);
+
+free_gpio:
+ if (gpio_is_valid(core->gpio_reset))
+ gpio_free(core->gpio_reset);
+
+ return rval;
+}
+
+static int si476x_core_remove(struct i2c_client *client)
+{
+ struct si476x_core *core = i2c_get_clientdata(client);
+
+ si476x_core_pronounce_dead(core);
+ mfd_remove_devices(&client->dev);
+
+ if (client->irq)
+ disable_irq(client->irq);
+ else
+ cancel_delayed_work_sync(&core->status_monitor);
+
+ kfifo_free(&core->rds_fifo);
+
+ if (gpio_is_valid(core->gpio_reset))
+ gpio_free(core->gpio_reset);
+
+ return 0;
+}
+
+
+static const struct i2c_device_id si476x_id[] = {
+ { "si4761", SI476X_CHIP_SI4761 },
+ { "si4764", SI476X_CHIP_SI4764 },
+ { "si4768", SI476X_CHIP_SI4768 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, si476x_id);
+
+static struct i2c_driver si476x_core_driver = {
+ .driver = {
+ .name = "si476x-core",
+ .owner = THIS_MODULE,
+ },
+ .probe = si476x_core_probe,
+ .remove = si476x_core_remove,
+ .id_table = si476x_id,
+};
+module_i2c_driver(si476x_core_driver);
+
+
+MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
+MODULE_DESCRIPTION("Si4761/64/68 AM/FM MFD core device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/si476x-prop.c b/drivers/mfd/si476x-prop.c
new file mode 100644
index 00000000000..cfeffa6e15d
--- /dev/null
+++ b/drivers/mfd/si476x-prop.c
@@ -0,0 +1,241 @@
+/*
+ * drivers/mfd/si476x-prop.c -- Subroutines to access
+ * properties of si476x chips
+ *
+ * Copyright (C) 2012 Innovative Converged Devices(ICD)
+ * Copyright (C) 2013 Andrey Smirnov
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.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; version 2 of the License.
+ *
+ * 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.
+ */
+#include <linux/module.h>
+
+#include <linux/mfd/si476x-core.h>
+
+struct si476x_property_range {
+ u16 low, high;
+};
+
+static bool si476x_core_element_is_in_array(u16 element,
+ const u16 array[],
+ size_t size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (element == array[i])
+ return true;
+
+ return false;
+}
+
+static bool si476x_core_element_is_in_range(u16 element,
+ const struct si476x_property_range range[],
+ size_t size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (element <= range[i].high && element >= range[i].low)
+ return true;
+
+ return false;
+}
+
+static bool si476x_core_is_valid_property_a10(struct si476x_core *core,
+ u16 property)
+{
+ static const u16 valid_properties[] = {
+ 0x0000,
+ 0x0500, 0x0501,
+ 0x0600,
+ 0x0709, 0x070C, 0x070D, 0x70E, 0x710,
+ 0x0718,
+ 0x1207, 0x1208,
+ 0x2007,
+ 0x2300,
+ };
+
+ static const struct si476x_property_range valid_ranges[] = {
+ { 0x0200, 0x0203 },
+ { 0x0300, 0x0303 },
+ { 0x0400, 0x0404 },
+ { 0x0700, 0x0707 },
+ { 0x1100, 0x1102 },
+ { 0x1200, 0x1204 },
+ { 0x1300, 0x1306 },
+ { 0x2000, 0x2005 },
+ { 0x2100, 0x2104 },
+ { 0x2106, 0x2106 },
+ { 0x2200, 0x220E },
+ { 0x3100, 0x3104 },
+ { 0x3207, 0x320F },
+ { 0x3300, 0x3304 },
+ { 0x3500, 0x3517 },
+ { 0x3600, 0x3617 },
+ { 0x3700, 0x3717 },
+ { 0x4000, 0x4003 },
+ };
+
+ return si476x_core_element_is_in_range(property, valid_ranges,
+ ARRAY_SIZE(valid_ranges)) ||
+ si476x_core_element_is_in_array(property, valid_properties,
+ ARRAY_SIZE(valid_properties));
+}
+
+static bool si476x_core_is_valid_property_a20(struct si476x_core *core,
+ u16 property)
+{
+ static const u16 valid_properties[] = {
+ 0x071B,
+ 0x1006,
+ 0x2210,
+ 0x3401,
+ };
+
+ static const struct si476x_property_range valid_ranges[] = {
+ { 0x2215, 0x2219 },
+ };
+
+ return si476x_core_is_valid_property_a10(core, property) ||
+ si476x_core_element_is_in_range(property, valid_ranges,
+ ARRAY_SIZE(valid_ranges)) ||
+ si476x_core_element_is_in_array(property, valid_properties,
+ ARRAY_SIZE(valid_properties));
+}
+
+static bool si476x_core_is_valid_property_a30(struct si476x_core *core,
+ u16 property)
+{
+ static const u16 valid_properties[] = {
+ 0x071C, 0x071D,
+ 0x1007, 0x1008,
+ 0x220F, 0x2214,
+ 0x2301,
+ 0x3105, 0x3106,
+ 0x3402,
+ };
+
+ static const struct si476x_property_range valid_ranges[] = {
+ { 0x0405, 0x0411 },
+ { 0x2008, 0x200B },
+ { 0x2220, 0x2223 },
+ { 0x3100, 0x3106 },
+ };
+
+ return si476x_core_is_valid_property_a20(core, property) ||
+ si476x_core_element_is_in_range(property, valid_ranges,
+ ARRAY_SIZE(valid_ranges)) ||
+ si476x_core_element_is_in_array(property, valid_properties,
+ ARRAY_SIZE(valid_properties));
+}
+
+typedef bool (*valid_property_pred_t) (struct si476x_core *, u16);
+
+static bool si476x_core_is_valid_property(struct si476x_core *core,
+ u16 property)
+{
+ static const valid_property_pred_t is_valid_property[] = {
+ [SI476X_REVISION_A10] = si476x_core_is_valid_property_a10,
+ [SI476X_REVISION_A20] = si476x_core_is_valid_property_a20,
+ [SI476X_REVISION_A30] = si476x_core_is_valid_property_a30,
+ };
+
+ BUG_ON(core->revision > SI476X_REVISION_A30 ||
+ core->revision == -1);
+ return is_valid_property[core->revision](core, property);
+}
+
+
+static bool si476x_core_is_readonly_property(struct si476x_core *core,
+ u16 property)
+{
+ BUG_ON(core->revision > SI476X_REVISION_A30 ||
+ core->revision == -1);
+
+ switch (core->revision) {
+ case SI476X_REVISION_A10:
+ return (property == 0x3200);
+ case SI476X_REVISION_A20:
+ return (property == 0x1006 ||
+ property == 0x2210 ||
+ property == 0x3200);
+ case SI476X_REVISION_A30:
+ return false;
+ }
+
+ return false;
+}
+
+static bool si476x_core_regmap_readable_register(struct device *dev,
+ unsigned int reg)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct si476x_core *core = i2c_get_clientdata(client);
+
+ return si476x_core_is_valid_property(core, (u16) reg);
+
+}
+
+static bool si476x_core_regmap_writable_register(struct device *dev,
+ unsigned int reg)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct si476x_core *core = i2c_get_clientdata(client);
+
+ return si476x_core_is_valid_property(core, (u16) reg) &&
+ !si476x_core_is_readonly_property(core, (u16) reg);
+}
+
+
+static int si476x_core_regmap_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ return si476x_core_cmd_set_property(context, reg, val);
+}
+
+static int si476x_core_regmap_read(void *context, unsigned int reg,
+ unsigned *val)
+{
+ struct si476x_core *core = context;
+ int err;
+
+ err = si476x_core_cmd_get_property(core, reg);
+ if (err < 0)
+ return err;
+
+ *val = err;
+
+ return 0;
+}
+
+
+static const struct regmap_config si476x_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 16,
+
+ .max_register = 0x4003,
+
+ .writeable_reg = si476x_core_regmap_writable_register,
+ .readable_reg = si476x_core_regmap_readable_register,
+
+ .reg_read = si476x_core_regmap_read,
+ .reg_write = si476x_core_regmap_write,
+
+ .cache_type = REGCACHE_RBTREE,
+};
+
+struct regmap *devm_regmap_init_si476x(struct si476x_core *core)
+{
+ return devm_regmap_init(&core->client->dev, NULL,
+ core, &si476x_regmap_config);
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init_si476x);
diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c
index d927dd49acb..81e6d0932bf 100644
--- a/drivers/mfd/sm501.c
+++ b/drivers/mfd/sm501.c
@@ -840,7 +840,7 @@ static int sm501_register_uart(struct sm501_devdata *sm, int devices)
if (!pdev)
return -ENOMEM;
- uart_data = pdev->dev.platform_data;
+ uart_data = dev_get_platdata(&pdev->dev);
if (devices & SM501_USE_UART0) {
sm501_setup_uart_data(sm, uart_data++, 0x30000);
@@ -1014,7 +1014,7 @@ static struct gpio_chip gpio_chip_template = {
.get = sm501_gpio_get,
};
-static int __devinit sm501_gpio_register_chip(struct sm501_devdata *sm,
+static int sm501_gpio_register_chip(struct sm501_devdata *sm,
struct sm501_gpio *gpio,
struct sm501_gpio_chip *chip)
{
@@ -1042,7 +1042,7 @@ static int __devinit sm501_gpio_register_chip(struct sm501_devdata *sm,
return gpiochip_add(gchip);
}
-static int __devinit sm501_register_gpio(struct sm501_devdata *sm)
+static int sm501_register_gpio(struct sm501_devdata *sm)
{
struct sm501_gpio *gpio = &sm->gpio;
resource_size_t iobase = sm->io_res->start + SM501_GPIO;
@@ -1167,7 +1167,7 @@ static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm,
if (!pdev)
return -ENOMEM;
- icd = pdev->dev.platform_data;
+ icd = dev_get_platdata(&pdev->dev);
/* We keep the pin_sda and pin_scl fields relative in case the
* same platform data is passed to >1 SM501.
@@ -1232,7 +1232,7 @@ static ssize_t sm501_dbg_regs(struct device *dev,
}
-static DEVICE_ATTR(dbg_regs, 0666, sm501_dbg_regs, NULL);
+static DEVICE_ATTR(dbg_regs, 0444, sm501_dbg_regs, NULL);
/* sm501_init_reg
*
@@ -1313,7 +1313,7 @@ static unsigned int sm501_mem_local[] = {
* Common init code for an SM501
*/
-static int __devinit sm501_init_dev(struct sm501_devdata *sm)
+static int sm501_init_dev(struct sm501_devdata *sm)
{
struct sm501_initdata *idata;
struct sm501_platdata *pdata;
@@ -1389,7 +1389,7 @@ static int __devinit sm501_init_dev(struct sm501_devdata *sm)
return 0;
}
-static int __devinit sm501_plat_probe(struct platform_device *dev)
+static int sm501_plat_probe(struct platform_device *dev)
{
struct sm501_devdata *sm;
int ret;
@@ -1403,7 +1403,7 @@ static int __devinit sm501_plat_probe(struct platform_device *dev)
sm->dev = &dev->dev;
sm->pdev_id = dev->id;
- sm->platdata = dev->dev.platform_data;
+ sm->platdata = dev_get_platdata(&dev->dev);
ret = platform_get_irq(dev, 0);
if (ret < 0) {
@@ -1578,7 +1578,7 @@ static struct sm501_platdata sm501_pci_platdata = {
.gpio_base = -1,
};
-static int __devinit sm501_pci_probe(struct pci_dev *dev,
+static int sm501_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
struct sm501_devdata *sm;
@@ -1660,7 +1660,6 @@ static int __devinit sm501_pci_probe(struct pci_dev *dev,
err3:
pci_disable_device(dev);
err2:
- pci_set_drvdata(dev, NULL);
kfree(sm);
err1:
return err;
@@ -1685,7 +1684,7 @@ static void sm501_dev_remove(struct sm501_devdata *sm)
sm501_gpio_remove(sm);
}
-static void __devexit sm501_pci_remove(struct pci_dev *dev)
+static void sm501_pci_remove(struct pci_dev *dev)
{
struct sm501_devdata *sm = pci_get_drvdata(dev);
@@ -1695,7 +1694,6 @@ static void __devexit sm501_pci_remove(struct pci_dev *dev)
release_resource(sm->regs_claim);
kfree(sm->regs_claim);
- pci_set_drvdata(dev, NULL);
pci_disable_device(dev);
}
@@ -1712,7 +1710,7 @@ static int sm501_plat_remove(struct platform_device *dev)
return 0;
}
-static DEFINE_PCI_DEVICE_TABLE(sm501_pci_tbl) = {
+static const struct pci_device_id sm501_pci_tbl[] = {
{ 0x126f, 0x0501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0, },
};
@@ -1723,12 +1721,12 @@ static struct pci_driver sm501_pci_driver = {
.name = "sm501",
.id_table = sm501_pci_tbl,
.probe = sm501_pci_probe,
- .remove = __devexit_p(sm501_pci_remove),
+ .remove = sm501_pci_remove,
};
MODULE_ALIAS("platform:sm501");
-static struct of_device_id __devinitdata of_sm501_match_tbl[] = {
+static const struct of_device_id of_sm501_match_tbl[] = {
{ .compatible = "smi,sm501", },
{ /* end */ }
};
diff --git a/drivers/mfd/smsc-ece1099.c b/drivers/mfd/smsc-ece1099.c
index 24ae3d8421c..90112d4cc90 100644
--- a/drivers/mfd/smsc-ece1099.c
+++ b/drivers/mfd/smsc-ece1099.c
@@ -13,7 +13,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
diff --git a/drivers/mfd/ssbi.c b/drivers/mfd/ssbi.c
new file mode 100644
index 00000000000..b78942ed4c6
--- /dev/null
+++ b/drivers/mfd/ssbi.c
@@ -0,0 +1,339 @@
+/* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010, Google Inc.
+ *
+ * Original authors: Code Aurora Forum
+ *
+ * Author: Dima Zavin <dima@android.com>
+ * - Largely rewritten from original to not be an i2c driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/ssbi.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+/* SSBI 2.0 controller registers */
+#define SSBI2_CMD 0x0008
+#define SSBI2_RD 0x0010
+#define SSBI2_STATUS 0x0014
+#define SSBI2_MODE2 0x001C
+
+/* SSBI_CMD fields */
+#define SSBI_CMD_RDWRN (1 << 24)
+
+/* SSBI_STATUS fields */
+#define SSBI_STATUS_RD_READY (1 << 2)
+#define SSBI_STATUS_READY (1 << 1)
+#define SSBI_STATUS_MCHN_BUSY (1 << 0)
+
+/* SSBI_MODE2 fields */
+#define SSBI_MODE2_REG_ADDR_15_8_SHFT 0x04
+#define SSBI_MODE2_REG_ADDR_15_8_MASK (0x7f << SSBI_MODE2_REG_ADDR_15_8_SHFT)
+
+#define SET_SSBI_MODE2_REG_ADDR_15_8(MD, AD) \
+ (((MD) & 0x0F) | ((((AD) >> 8) << SSBI_MODE2_REG_ADDR_15_8_SHFT) & \
+ SSBI_MODE2_REG_ADDR_15_8_MASK))
+
+/* SSBI PMIC Arbiter command registers */
+#define SSBI_PA_CMD 0x0000
+#define SSBI_PA_RD_STATUS 0x0004
+
+/* SSBI_PA_CMD fields */
+#define SSBI_PA_CMD_RDWRN (1 << 24)
+#define SSBI_PA_CMD_ADDR_MASK 0x7fff /* REG_ADDR_7_0, REG_ADDR_8_14*/
+
+/* SSBI_PA_RD_STATUS fields */
+#define SSBI_PA_RD_STATUS_TRANS_DONE (1 << 27)
+#define SSBI_PA_RD_STATUS_TRANS_DENIED (1 << 26)
+
+#define SSBI_TIMEOUT_US 100
+
+enum ssbi_controller_type {
+ MSM_SBI_CTRL_SSBI = 0,
+ MSM_SBI_CTRL_SSBI2,
+ MSM_SBI_CTRL_PMIC_ARBITER,
+};
+
+struct ssbi {
+ struct device *slave;
+ void __iomem *base;
+ spinlock_t lock;
+ enum ssbi_controller_type controller_type;
+ int (*read)(struct ssbi *, u16 addr, u8 *buf, int len);
+ int (*write)(struct ssbi *, u16 addr, const u8 *buf, int len);
+};
+
+#define to_ssbi(dev) platform_get_drvdata(to_platform_device(dev))
+
+static inline u32 ssbi_readl(struct ssbi *ssbi, u32 reg)
+{
+ return readl(ssbi->base + reg);
+}
+
+static inline void ssbi_writel(struct ssbi *ssbi, u32 val, u32 reg)
+{
+ writel(val, ssbi->base + reg);
+}
+
+/*
+ * Via private exchange with one of the original authors, the hardware
+ * should generally finish a transaction in about 5us. The worst
+ * case, is when using the arbiter and both other CPUs have just
+ * started trying to use the SSBI bus will result in a time of about
+ * 20us. It should never take longer than this.
+ *
+ * As such, this wait merely spins, with a udelay.
+ */
+static int ssbi_wait_mask(struct ssbi *ssbi, u32 set_mask, u32 clr_mask)
+{
+ u32 timeout = SSBI_TIMEOUT_US;
+ u32 val;
+
+ while (timeout--) {
+ val = ssbi_readl(ssbi, SSBI2_STATUS);
+ if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0))
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int
+ssbi_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len)
+{
+ u32 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16);
+ int ret = 0;
+
+ if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
+ u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
+ mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
+ ssbi_writel(ssbi, mode2, SSBI2_MODE2);
+ }
+
+ while (len) {
+ ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
+ if (ret)
+ goto err;
+
+ ssbi_writel(ssbi, cmd, SSBI2_CMD);
+ ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0);
+ if (ret)
+ goto err;
+ *buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff;
+ len--;
+ }
+
+err:
+ return ret;
+}
+
+static int
+ssbi_write_bytes(struct ssbi *ssbi, u16 addr, const u8 *buf, int len)
+{
+ int ret = 0;
+
+ if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
+ u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
+ mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
+ ssbi_writel(ssbi, mode2, SSBI2_MODE2);
+ }
+
+ while (len) {
+ ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
+ if (ret)
+ goto err;
+
+ ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf, SSBI2_CMD);
+ ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY);
+ if (ret)
+ goto err;
+ buf++;
+ len--;
+ }
+
+err:
+ return ret;
+}
+
+/*
+ * See ssbi_wait_mask for an explanation of the time and the
+ * busywait.
+ */
+static inline int
+ssbi_pa_transfer(struct ssbi *ssbi, u32 cmd, u8 *data)
+{
+ u32 timeout = SSBI_TIMEOUT_US;
+ u32 rd_status = 0;
+
+ ssbi_writel(ssbi, cmd, SSBI_PA_CMD);
+
+ while (timeout--) {
+ rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS);
+
+ if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED)
+ return -EPERM;
+
+ if (rd_status & SSBI_PA_RD_STATUS_TRANS_DONE) {
+ if (data)
+ *data = rd_status & 0xff;
+ return 0;
+ }
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int
+ssbi_pa_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len)
+{
+ u32 cmd;
+ int ret = 0;
+
+ cmd = SSBI_PA_CMD_RDWRN | (addr & SSBI_PA_CMD_ADDR_MASK) << 8;
+
+ while (len) {
+ ret = ssbi_pa_transfer(ssbi, cmd, buf);
+ if (ret)
+ goto err;
+ buf++;
+ len--;
+ }
+
+err:
+ return ret;
+}
+
+static int
+ssbi_pa_write_bytes(struct ssbi *ssbi, u16 addr, const u8 *buf, int len)
+{
+ u32 cmd;
+ int ret = 0;
+
+ while (len) {
+ cmd = (addr & SSBI_PA_CMD_ADDR_MASK) << 8 | *buf;
+ ret = ssbi_pa_transfer(ssbi, cmd, NULL);
+ if (ret)
+ goto err;
+ buf++;
+ len--;
+ }
+
+err:
+ return ret;
+}
+
+int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len)
+{
+ struct ssbi *ssbi = to_ssbi(dev);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&ssbi->lock, flags);
+ ret = ssbi->read(ssbi, addr, buf, len);
+ spin_unlock_irqrestore(&ssbi->lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ssbi_read);
+
+int ssbi_write(struct device *dev, u16 addr, const u8 *buf, int len)
+{
+ struct ssbi *ssbi = to_ssbi(dev);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&ssbi->lock, flags);
+ ret = ssbi->write(ssbi, addr, buf, len);
+ spin_unlock_irqrestore(&ssbi->lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ssbi_write);
+
+static int ssbi_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *mem_res;
+ struct ssbi *ssbi;
+ const char *type;
+
+ ssbi = devm_kzalloc(&pdev->dev, sizeof(*ssbi), GFP_KERNEL);
+ if (!ssbi)
+ return -ENOMEM;
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ssbi->base = devm_ioremap_resource(&pdev->dev, mem_res);
+ if (IS_ERR(ssbi->base))
+ return PTR_ERR(ssbi->base);
+
+ platform_set_drvdata(pdev, ssbi);
+
+ type = of_get_property(np, "qcom,controller-type", NULL);
+ if (type == NULL) {
+ dev_err(&pdev->dev, "Missing qcom,controller-type property\n");
+ return -EINVAL;
+ }
+ dev_info(&pdev->dev, "SSBI controller type: '%s'\n", type);
+ if (strcmp(type, "ssbi") == 0)
+ ssbi->controller_type = MSM_SBI_CTRL_SSBI;
+ else if (strcmp(type, "ssbi2") == 0)
+ ssbi->controller_type = MSM_SBI_CTRL_SSBI2;
+ else if (strcmp(type, "pmic-arbiter") == 0)
+ ssbi->controller_type = MSM_SBI_CTRL_PMIC_ARBITER;
+ else {
+ dev_err(&pdev->dev, "Unknown qcom,controller-type\n");
+ return -EINVAL;
+ }
+
+ if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) {
+ ssbi->read = ssbi_pa_read_bytes;
+ ssbi->write = ssbi_pa_write_bytes;
+ } else {
+ ssbi->read = ssbi_read_bytes;
+ ssbi->write = ssbi_write_bytes;
+ }
+
+ spin_lock_init(&ssbi->lock);
+
+ return of_platform_populate(np, NULL, NULL, &pdev->dev);
+}
+
+static const struct of_device_id ssbi_match_table[] = {
+ { .compatible = "qcom,ssbi" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ssbi_match_table);
+
+static struct platform_driver ssbi_driver = {
+ .probe = ssbi_probe,
+ .driver = {
+ .name = "ssbi",
+ .owner = THIS_MODULE,
+ .of_match_table = ssbi_match_table,
+ },
+};
+module_platform_driver(ssbi_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:ssbi");
+MODULE_AUTHOR("Dima Zavin <dima@android.com>");
diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c
index d35da6820be..5b72db07d9d 100644
--- a/drivers/mfd/sta2x11-mfd.c
+++ b/drivers/mfd/sta2x11-mfd.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2009-2011 Wind River Systems, Inc.
- * Copyright (c) 2011 ST Microelectronics (Alessandro Rubini)
+ * Copyright (c) 2011 ST Microelectronics (Alessandro Rubini, Davide Ciminaghi)
*
* 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
@@ -27,21 +27,28 @@
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/pci.h>
-#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/platform_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/sta2x11-mfd.h>
+#include <linux/regmap.h>
#include <asm/sta2x11.h>
+static inline int __reg_within_range(unsigned int r,
+ unsigned int start,
+ unsigned int end)
+{
+ return ((r >= start) && (r <= end));
+}
+
/* This describes STA2X11 MFD chip for us, we may have several */
struct sta2x11_mfd {
struct sta2x11_instance *instance;
- spinlock_t lock;
+ struct regmap *regmap[sta2x11_n_mfd_plat_devs];
+ spinlock_t lock[sta2x11_n_mfd_plat_devs];
struct list_head list;
- void __iomem *sctl_regs;
- void __iomem *apbreg_regs;
+ void __iomem *regs[sta2x11_n_mfd_plat_devs];
};
static LIST_HEAD(sta2x11_mfd_list);
@@ -69,8 +76,9 @@ static struct sta2x11_mfd *sta2x11_mfd_find(struct pci_dev *pdev)
return NULL;
}
-static int __devinit sta2x11_mfd_add(struct pci_dev *pdev, gfp_t flags)
+static int sta2x11_mfd_add(struct pci_dev *pdev, gfp_t flags)
{
+ int i;
struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev);
struct sta2x11_instance *instance;
@@ -83,178 +91,283 @@ static int __devinit sta2x11_mfd_add(struct pci_dev *pdev, gfp_t flags)
if (!mfd)
return -ENOMEM;
INIT_LIST_HEAD(&mfd->list);
- spin_lock_init(&mfd->lock);
+ for (i = 0; i < ARRAY_SIZE(mfd->lock); i++)
+ spin_lock_init(&mfd->lock[i]);
mfd->instance = instance;
list_add(&mfd->list, &sta2x11_mfd_list);
return 0;
}
-static int __devexit mfd_remove(struct pci_dev *pdev)
-{
- struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev);
-
- if (!mfd)
- return -ENODEV;
- list_del(&mfd->list);
- kfree(mfd);
- return 0;
-}
-
-/* These two functions are exported and are not expected to fail */
-u32 sta2x11_sctl_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val)
+/* This function is exported and is not expected to fail */
+u32 __sta2x11_mfd_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val,
+ enum sta2x11_mfd_plat_dev index)
{
struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev);
u32 r;
unsigned long flags;
+ void __iomem *regs;
if (!mfd) {
dev_warn(&pdev->dev, ": can't access sctl regs\n");
return 0;
}
- if (!mfd->sctl_regs) {
+
+ regs = mfd->regs[index];
+ if (!regs) {
dev_warn(&pdev->dev, ": system ctl not initialized\n");
return 0;
}
- spin_lock_irqsave(&mfd->lock, flags);
- r = readl(mfd->sctl_regs + reg);
+ spin_lock_irqsave(&mfd->lock[index], flags);
+ r = readl(regs + reg);
r &= ~mask;
r |= val;
if (mask)
- writel(r, mfd->sctl_regs + reg);
- spin_unlock_irqrestore(&mfd->lock, flags);
+ writel(r, regs + reg);
+ spin_unlock_irqrestore(&mfd->lock[index], flags);
return r;
}
-EXPORT_SYMBOL(sta2x11_sctl_mask);
+EXPORT_SYMBOL(__sta2x11_mfd_mask);
-u32 sta2x11_apbreg_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val)
+int sta2x11_mfd_get_regs_data(struct platform_device *dev,
+ enum sta2x11_mfd_plat_dev index,
+ void __iomem **regs,
+ spinlock_t **lock)
{
- struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev);
- u32 r;
- unsigned long flags;
+ struct pci_dev *pdev = *(struct pci_dev **)dev_get_platdata(&dev->dev);
+ struct sta2x11_mfd *mfd;
- if (!mfd) {
- dev_warn(&pdev->dev, ": can't access apb regs\n");
- return 0;
- }
- if (!mfd->apbreg_regs) {
- dev_warn(&pdev->dev, ": apb bridge not initialized\n");
- return 0;
- }
- spin_lock_irqsave(&mfd->lock, flags);
- r = readl(mfd->apbreg_regs + reg);
- r &= ~mask;
- r |= val;
- if (mask)
- writel(r, mfd->apbreg_regs + reg);
- spin_unlock_irqrestore(&mfd->lock, flags);
- return r;
+ if (!pdev)
+ return -ENODEV;
+ mfd = sta2x11_mfd_find(pdev);
+ if (!mfd)
+ return -ENODEV;
+ if (index >= sta2x11_n_mfd_plat_devs)
+ return -ENODEV;
+ *regs = mfd->regs[index];
+ *lock = &mfd->lock[index];
+ pr_debug("%s %d *regs = %p\n", __func__, __LINE__, *regs);
+ return *regs ? 0 : -ENODEV;
}
-EXPORT_SYMBOL(sta2x11_apbreg_mask);
-
-/* Two debugfs files, for our registers (FIXME: one instance only) */
-#define REG(regname) {.name = #regname, .offset = SCTL_ ## regname}
-static struct debugfs_reg32 sta2x11_sctl_regs[] = {
- REG(SCCTL), REG(ARMCFG), REG(SCPLLCTL), REG(SCPLLFCTRL),
- REG(SCRESFRACT), REG(SCRESCTRL1), REG(SCRESXTRL2), REG(SCPEREN0),
- REG(SCPEREN1), REG(SCPEREN2), REG(SCGRST), REG(SCPCIPMCR1),
- REG(SCPCIPMCR2), REG(SCPCIPMSR1), REG(SCPCIPMSR2), REG(SCPCIPMSR3),
- REG(SCINTREN), REG(SCRISR), REG(SCCLKSTAT0), REG(SCCLKSTAT1),
- REG(SCCLKSTAT2), REG(SCRSTSTA),
-};
-#undef REG
+EXPORT_SYMBOL(sta2x11_mfd_get_regs_data);
-static struct debugfs_regset32 sctl_regset = {
- .regs = sta2x11_sctl_regs,
- .nregs = ARRAY_SIZE(sta2x11_sctl_regs),
-};
+/*
+ * Special sta2x11-mfd regmap lock/unlock functions
+ */
-#define REG(regname) {.name = #regname, .offset = regname}
-static struct debugfs_reg32 sta2x11_apbreg_regs[] = {
- REG(APBREG_BSR), REG(APBREG_PAER), REG(APBREG_PWAC), REG(APBREG_PRAC),
- REG(APBREG_PCG), REG(APBREG_PUR), REG(APBREG_EMU_PCG),
+static void sta2x11_regmap_lock(void *__lock)
+{
+ spinlock_t *lock = __lock;
+ spin_lock(lock);
+}
+
+static void sta2x11_regmap_unlock(void *__lock)
+{
+ spinlock_t *lock = __lock;
+ spin_unlock(lock);
+}
+
+/* OTP (one time programmable registers do not require locking */
+static void sta2x11_regmap_nolock(void *__lock)
+{
+}
+
+static const char *sta2x11_mfd_names[sta2x11_n_mfd_plat_devs] = {
+ [sta2x11_sctl] = STA2X11_MFD_SCTL_NAME,
+ [sta2x11_apbreg] = STA2X11_MFD_APBREG_NAME,
+ [sta2x11_apb_soc_regs] = STA2X11_MFD_APB_SOC_REGS_NAME,
+ [sta2x11_scr] = STA2X11_MFD_SCR_NAME,
};
-#undef REG
-static struct debugfs_regset32 apbreg_regset = {
- .regs = sta2x11_apbreg_regs,
- .nregs = ARRAY_SIZE(sta2x11_apbreg_regs),
+static bool sta2x11_sctl_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return !__reg_within_range(reg, SCTL_SCPCIECSBRST, SCTL_SCRSTSTA);
+}
+
+static struct regmap_config sta2x11_sctl_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .lock = sta2x11_regmap_lock,
+ .unlock = sta2x11_regmap_unlock,
+ .max_register = SCTL_SCRSTSTA,
+ .writeable_reg = sta2x11_sctl_writeable_reg,
};
-static struct dentry *sta2x11_sctl_debugfs;
-static struct dentry *sta2x11_apbreg_debugfs;
+static bool sta2x11_scr_readable_reg(struct device *dev, unsigned int reg)
+{
+ return (reg == STA2X11_SECR_CR) ||
+ __reg_within_range(reg, STA2X11_SECR_FVR0, STA2X11_SECR_FVR1);
+}
-/* Probe for the two platform devices */
-static int sta2x11_sctl_probe(struct platform_device *dev)
+static bool sta2x11_scr_writeable_reg(struct device *dev, unsigned int reg)
{
- struct pci_dev **pdev;
- struct sta2x11_mfd *mfd;
- struct resource *res;
+ return false;
+}
- pdev = dev->dev.platform_data;
- mfd = sta2x11_mfd_find(*pdev);
- if (!mfd)
- return -ENODEV;
+static struct regmap_config sta2x11_scr_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .lock = sta2x11_regmap_nolock,
+ .unlock = sta2x11_regmap_nolock,
+ .max_register = STA2X11_SECR_FVR1,
+ .readable_reg = sta2x11_scr_readable_reg,
+ .writeable_reg = sta2x11_scr_writeable_reg,
+};
- res = platform_get_resource(dev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENOMEM;
+static bool sta2x11_apbreg_readable_reg(struct device *dev, unsigned int reg)
+{
+ /* Two blocks (CAN and MLB, SARAC) 0x100 bytes apart */
+ if (reg >= APBREG_BSR_SARAC)
+ reg -= APBREG_BSR_SARAC;
+ switch (reg) {
+ case APBREG_BSR:
+ case APBREG_PAER:
+ case APBREG_PWAC:
+ case APBREG_PRAC:
+ case APBREG_PCG:
+ case APBREG_PUR:
+ case APBREG_EMU_PCG:
+ return true;
+ default:
+ return false;
+ }
+}
- if (!request_mem_region(res->start, resource_size(res),
- "sta2x11-sctl"))
- return -EBUSY;
+static bool sta2x11_apbreg_writeable_reg(struct device *dev, unsigned int reg)
+{
+ if (reg >= APBREG_BSR_SARAC)
+ reg -= APBREG_BSR_SARAC;
+ if (!sta2x11_apbreg_readable_reg(dev, reg))
+ return false;
+ return reg != APBREG_PAER;
+}
- mfd->sctl_regs = ioremap(res->start, resource_size(res));
- if (!mfd->sctl_regs) {
- release_mem_region(res->start, resource_size(res));
- return -ENOMEM;
+static struct regmap_config sta2x11_apbreg_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .lock = sta2x11_regmap_lock,
+ .unlock = sta2x11_regmap_unlock,
+ .max_register = APBREG_EMU_PCG_SARAC,
+ .readable_reg = sta2x11_apbreg_readable_reg,
+ .writeable_reg = sta2x11_apbreg_writeable_reg,
+};
+
+static bool sta2x11_apb_soc_regs_readable_reg(struct device *dev,
+ unsigned int reg)
+{
+ return reg <= PCIE_SoC_INT_ROUTER_STATUS3_REG ||
+ __reg_within_range(reg, DMA_IP_CTRL_REG, SPARE3_RESERVED) ||
+ __reg_within_range(reg, MASTER_LOCK_REG,
+ SYSTEM_CONFIG_STATUS_REG) ||
+ reg == MSP_CLK_CTRL_REG ||
+ __reg_within_range(reg, COMPENSATION_REG1, TEST_CTL_REG);
+}
+
+static bool sta2x11_apb_soc_regs_writeable_reg(struct device *dev,
+ unsigned int reg)
+{
+ if (!sta2x11_apb_soc_regs_readable_reg(dev, reg))
+ return false;
+ switch (reg) {
+ case PCIE_COMMON_CLOCK_CONFIG_0_4_0:
+ case SYSTEM_CONFIG_STATUS_REG:
+ case COMPENSATION_REG1:
+ case PCIE_SoC_INT_ROUTER_STATUS0_REG...PCIE_SoC_INT_ROUTER_STATUS3_REG:
+ case PCIE_PM_STATUS_0_PORT_0_4...PCIE_PM_STATUS_7_0_EP4:
+ return false;
+ default:
+ return true;
}
- sctl_regset.base = mfd->sctl_regs;
- sta2x11_sctl_debugfs = debugfs_create_regset32("sta2x11-sctl",
- S_IFREG | S_IRUGO,
- NULL, &sctl_regset);
- return 0;
}
-static int sta2x11_apbreg_probe(struct platform_device *dev)
+static struct regmap_config sta2x11_apb_soc_regs_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .lock = sta2x11_regmap_lock,
+ .unlock = sta2x11_regmap_unlock,
+ .max_register = TEST_CTL_REG,
+ .readable_reg = sta2x11_apb_soc_regs_readable_reg,
+ .writeable_reg = sta2x11_apb_soc_regs_writeable_reg,
+};
+
+static struct regmap_config *
+sta2x11_mfd_regmap_configs[sta2x11_n_mfd_plat_devs] = {
+ [sta2x11_sctl] = &sta2x11_sctl_regmap_config,
+ [sta2x11_apbreg] = &sta2x11_apbreg_regmap_config,
+ [sta2x11_apb_soc_regs] = &sta2x11_apb_soc_regs_regmap_config,
+ [sta2x11_scr] = &sta2x11_scr_regmap_config,
+};
+
+/* Probe for the four platform devices */
+
+static int sta2x11_mfd_platform_probe(struct platform_device *dev,
+ enum sta2x11_mfd_plat_dev index)
{
struct pci_dev **pdev;
struct sta2x11_mfd *mfd;
struct resource *res;
+ const char *name = sta2x11_mfd_names[index];
+ struct regmap_config *regmap_config = sta2x11_mfd_regmap_configs[index];
- pdev = dev->dev.platform_data;
- dev_dbg(&dev->dev, "%s: pdata is %p\n", __func__, pdev);
- dev_dbg(&dev->dev, "%s: *pdata is %p\n", __func__, *pdev);
-
+ pdev = dev_get_platdata(&dev->dev);
mfd = sta2x11_mfd_find(*pdev);
if (!mfd)
return -ENODEV;
+ if (!regmap_config)
+ return -ENODEV;
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!res)
return -ENOMEM;
- if (!request_mem_region(res->start, resource_size(res),
- "sta2x11-apbreg"))
+ if (!request_mem_region(res->start, resource_size(res), name))
return -EBUSY;
- mfd->apbreg_regs = ioremap(res->start, resource_size(res));
- if (!mfd->apbreg_regs) {
+ mfd->regs[index] = ioremap(res->start, resource_size(res));
+ if (!mfd->regs[index]) {
release_mem_region(res->start, resource_size(res));
return -ENOMEM;
}
- dev_dbg(&dev->dev, "%s: regbase %p\n", __func__, mfd->apbreg_regs);
+ regmap_config->lock_arg = &mfd->lock;
+ /*
+ No caching, registers could be reached both via regmap and via
+ void __iomem *
+ */
+ regmap_config->cache_type = REGCACHE_NONE;
+ mfd->regmap[index] = devm_regmap_init_mmio(&dev->dev, mfd->regs[index],
+ regmap_config);
+ WARN_ON(IS_ERR(mfd->regmap[index]));
- apbreg_regset.base = mfd->apbreg_regs;
- sta2x11_apbreg_debugfs = debugfs_create_regset32("sta2x11-apbreg",
- S_IFREG | S_IRUGO,
- NULL, &apbreg_regset);
return 0;
}
-/* The two platform drivers */
+static int sta2x11_sctl_probe(struct platform_device *dev)
+{
+ return sta2x11_mfd_platform_probe(dev, sta2x11_sctl);
+}
+
+static int sta2x11_apbreg_probe(struct platform_device *dev)
+{
+ return sta2x11_mfd_platform_probe(dev, sta2x11_apbreg);
+}
+
+static int sta2x11_apb_soc_regs_probe(struct platform_device *dev)
+{
+ return sta2x11_mfd_platform_probe(dev, sta2x11_apb_soc_regs);
+}
+
+static int sta2x11_scr_probe(struct platform_device *dev)
+{
+ return sta2x11_mfd_platform_probe(dev, sta2x11_scr);
+}
+
+/* The three platform drivers */
static struct platform_driver sta2x11_sctl_platform_driver = {
.driver = {
- .name = "sta2x11-sctl",
+ .name = STA2X11_MFD_SCTL_NAME,
.owner = THIS_MODULE,
},
.probe = sta2x11_sctl_probe,
@@ -268,7 +381,7 @@ static int __init sta2x11_sctl_init(void)
static struct platform_driver sta2x11_platform_driver = {
.driver = {
- .name = "sta2x11-apbreg",
+ .name = STA2X11_MFD_APBREG_NAME,
.owner = THIS_MODULE,
},
.probe = sta2x11_apbreg_probe,
@@ -280,13 +393,44 @@ static int __init sta2x11_apbreg_init(void)
return platform_driver_register(&sta2x11_platform_driver);
}
+static struct platform_driver sta2x11_apb_soc_regs_platform_driver = {
+ .driver = {
+ .name = STA2X11_MFD_APB_SOC_REGS_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = sta2x11_apb_soc_regs_probe,
+};
+
+static int __init sta2x11_apb_soc_regs_init(void)
+{
+ pr_info("%s\n", __func__);
+ return platform_driver_register(&sta2x11_apb_soc_regs_platform_driver);
+}
+
+static struct platform_driver sta2x11_scr_platform_driver = {
+ .driver = {
+ .name = STA2X11_MFD_SCR_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = sta2x11_scr_probe,
+};
+
+static int __init sta2x11_scr_init(void)
+{
+ pr_info("%s\n", __func__);
+ return platform_driver_register(&sta2x11_scr_platform_driver);
+}
+
+
/*
- * What follows is the PCI device that hosts the above two pdevs.
+ * What follows are the PCI devices that host the above pdevs.
* Each logic block is 4kB and they are all consecutive: we use this info.
*/
-/* Bar 0 */
-enum bar0_cells {
+/* Mfd 0 device */
+
+/* Mfd 0, Bar 0 */
+enum mfd0_bar0_cells {
STA2X11_GPIO_0 = 0,
STA2X11_GPIO_1,
STA2X11_GPIO_2,
@@ -295,8 +439,8 @@ enum bar0_cells {
STA2X11_SCR,
STA2X11_TIME,
};
-/* Bar 1 */
-enum bar1_cells {
+/* Mfd 0 , Bar 1 */
+enum mfd0_bar1_cells {
STA2X11_APBREG = 0,
};
#define CELL_4K(_name, _cell) { \
@@ -305,42 +449,73 @@ enum bar1_cells {
.flags = IORESOURCE_MEM, \
}
-static const __devinitconst struct resource gpio_resources[] = {
+static const struct resource gpio_resources[] = {
{
- .name = "sta2x11_gpio", /* 4 consecutive cells, 1 driver */
+ /* 4 consecutive cells, 1 driver */
+ .name = STA2X11_MFD_GPIO_NAME,
.start = 0,
.end = (4 * 4096) - 1,
.flags = IORESOURCE_MEM,
}
};
-static const __devinitconst struct resource sctl_resources[] = {
- CELL_4K("sta2x11-sctl", STA2X11_SCTL),
+static const struct resource sctl_resources[] = {
+ CELL_4K(STA2X11_MFD_SCTL_NAME, STA2X11_SCTL),
};
-static const __devinitconst struct resource scr_resources[] = {
- CELL_4K("sta2x11-scr", STA2X11_SCR),
+static const struct resource scr_resources[] = {
+ CELL_4K(STA2X11_MFD_SCR_NAME, STA2X11_SCR),
};
-static const __devinitconst struct resource time_resources[] = {
- CELL_4K("sta2x11-time", STA2X11_TIME),
+static const struct resource time_resources[] = {
+ CELL_4K(STA2X11_MFD_TIME_NAME, STA2X11_TIME),
};
-static const __devinitconst struct resource apbreg_resources[] = {
- CELL_4K("sta2x11-apbreg", STA2X11_APBREG),
+static const struct resource apbreg_resources[] = {
+ CELL_4K(STA2X11_MFD_APBREG_NAME, STA2X11_APBREG),
};
#define DEV(_name, _r) \
{ .name = _name, .num_resources = ARRAY_SIZE(_r), .resources = _r, }
-static __devinitdata struct mfd_cell sta2x11_mfd_bar0[] = {
- DEV("sta2x11-gpio", gpio_resources), /* offset 0: we add pdata later */
- DEV("sta2x11-sctl", sctl_resources),
- DEV("sta2x11-scr", scr_resources),
- DEV("sta2x11-time", time_resources),
+static struct mfd_cell sta2x11_mfd0_bar0[] = {
+ /* offset 0: we add pdata later */
+ DEV(STA2X11_MFD_GPIO_NAME, gpio_resources),
+ DEV(STA2X11_MFD_SCTL_NAME, sctl_resources),
+ DEV(STA2X11_MFD_SCR_NAME, scr_resources),
+ DEV(STA2X11_MFD_TIME_NAME, time_resources),
+};
+
+static struct mfd_cell sta2x11_mfd0_bar1[] = {
+ DEV(STA2X11_MFD_APBREG_NAME, apbreg_resources),
+};
+
+/* Mfd 1 devices */
+
+/* Mfd 1, Bar 0 */
+enum mfd1_bar0_cells {
+ STA2X11_VIC = 0,
};
-static __devinitdata struct mfd_cell sta2x11_mfd_bar1[] = {
- DEV("sta2x11-apbreg", apbreg_resources),
+/* Mfd 1, Bar 1 */
+enum mfd1_bar1_cells {
+ STA2X11_APB_SOC_REGS = 0,
};
+static const struct resource vic_resources[] = {
+ CELL_4K(STA2X11_MFD_VIC_NAME, STA2X11_VIC),
+};
+
+static const struct resource apb_soc_regs_resources[] = {
+ CELL_4K(STA2X11_MFD_APB_SOC_REGS_NAME, STA2X11_APB_SOC_REGS),
+};
+
+static struct mfd_cell sta2x11_mfd1_bar0[] = {
+ DEV(STA2X11_MFD_VIC_NAME, vic_resources),
+};
+
+static struct mfd_cell sta2x11_mfd1_bar1[] = {
+ DEV(STA2X11_MFD_APB_SOC_REGS_NAME, apb_soc_regs_resources),
+};
+
+
static int sta2x11_mfd_suspend(struct pci_dev *pdev, pm_message_t state)
{
pci_save_state(pdev);
@@ -354,7 +529,7 @@ static int sta2x11_mfd_resume(struct pci_dev *pdev)
{
int err;
- pci_set_power_state(pdev, 0);
+ pci_set_power_state(pdev, PCI_D0);
err = pci_enable_device(pdev);
if (err)
return err;
@@ -363,11 +538,63 @@ static int sta2x11_mfd_resume(struct pci_dev *pdev)
return 0;
}
-static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev,
- const struct pci_device_id *pci_id)
+struct sta2x11_mfd_bar_setup_data {
+ struct mfd_cell *cells;
+ int ncells;
+};
+
+struct sta2x11_mfd_setup_data {
+ struct sta2x11_mfd_bar_setup_data bars[2];
+};
+
+#define STA2X11_MFD0 0
+#define STA2X11_MFD1 1
+
+static struct sta2x11_mfd_setup_data mfd_setup_data[] = {
+ /* Mfd 0: gpio, sctl, scr, timers / apbregs */
+ [STA2X11_MFD0] = {
+ .bars = {
+ [0] = {
+ .cells = sta2x11_mfd0_bar0,
+ .ncells = ARRAY_SIZE(sta2x11_mfd0_bar0),
+ },
+ [1] = {
+ .cells = sta2x11_mfd0_bar1,
+ .ncells = ARRAY_SIZE(sta2x11_mfd0_bar1),
+ },
+ },
+ },
+ /* Mfd 1: vic / apb-soc-regs */
+ [STA2X11_MFD1] = {
+ .bars = {
+ [0] = {
+ .cells = sta2x11_mfd1_bar0,
+ .ncells = ARRAY_SIZE(sta2x11_mfd1_bar0),
+ },
+ [1] = {
+ .cells = sta2x11_mfd1_bar1,
+ .ncells = ARRAY_SIZE(sta2x11_mfd1_bar1),
+ },
+ },
+ },
+};
+
+static void sta2x11_mfd_setup(struct pci_dev *pdev,
+ struct sta2x11_mfd_setup_data *sd)
+{
+ int i, j;
+ for (i = 0; i < ARRAY_SIZE(sd->bars); i++)
+ for (j = 0; j < sd->bars[i].ncells; j++) {
+ sd->bars[i].cells[j].pdata_size = sizeof(pdev);
+ sd->bars[i].cells[j].platform_data = &pdev;
+ }
+}
+
+static int sta2x11_mfd_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
{
int err, i;
- struct sta2x11_gpio_pdata *gpio_data;
+ struct sta2x11_mfd_setup_data *setup_data;
dev_info(&pdev->dev, "%s\n", __func__);
@@ -381,46 +608,29 @@ static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev,
if (err)
dev_info(&pdev->dev, "Enable msi failed\n");
- /* Read gpio config data as pci device's platform data */
- gpio_data = dev_get_platdata(&pdev->dev);
- if (!gpio_data)
- dev_warn(&pdev->dev, "no gpio configuration\n");
-
- dev_dbg(&pdev->dev, "%s, gpio_data = %p (%p)\n", __func__,
- gpio_data, &gpio_data);
- dev_dbg(&pdev->dev, "%s, pdev = %p (%p)\n", __func__,
- pdev, &pdev);
+ setup_data = pci_id->device == PCI_DEVICE_ID_STMICRO_GPIO ?
+ &mfd_setup_data[STA2X11_MFD0] :
+ &mfd_setup_data[STA2X11_MFD1];
/* platform data is the pci device for all of them */
- for (i = 0; i < ARRAY_SIZE(sta2x11_mfd_bar0); i++) {
- sta2x11_mfd_bar0[i].pdata_size = sizeof(pdev);
- sta2x11_mfd_bar0[i].platform_data = &pdev;
- }
- sta2x11_mfd_bar1[0].pdata_size = sizeof(pdev);
- sta2x11_mfd_bar1[0].platform_data = &pdev;
+ sta2x11_mfd_setup(pdev, setup_data);
/* Record this pdev before mfd_add_devices: their probe looks for it */
- sta2x11_mfd_add(pdev, GFP_ATOMIC);
-
-
- err = mfd_add_devices(&pdev->dev, -1,
- sta2x11_mfd_bar0,
- ARRAY_SIZE(sta2x11_mfd_bar0),
- &pdev->resource[0],
- 0, NULL);
- if (err) {
- dev_err(&pdev->dev, "mfd_add_devices[0] failed: %d\n", err);
- goto err_disable;
- }
-
- err = mfd_add_devices(&pdev->dev, -1,
- sta2x11_mfd_bar1,
- ARRAY_SIZE(sta2x11_mfd_bar1),
- &pdev->resource[1],
- 0, NULL);
- if (err) {
- dev_err(&pdev->dev, "mfd_add_devices[1] failed: %d\n", err);
- goto err_disable;
+ if (!sta2x11_mfd_find(pdev))
+ sta2x11_mfd_add(pdev, GFP_ATOMIC);
+
+ /* Just 2 bars for all mfd's at present */
+ for (i = 0; i < 2; i++) {
+ err = mfd_add_devices(&pdev->dev, -1,
+ setup_data->bars[i].cells,
+ setup_data->bars[i].ncells,
+ &pdev->resource[i],
+ 0, NULL);
+ if (err) {
+ dev_err(&pdev->dev,
+ "mfd_add_devices[%d] failed: %d\n", i, err);
+ goto err_disable;
+ }
}
return 0;
@@ -432,8 +642,9 @@ err_disable:
return err;
}
-static DEFINE_PCI_DEVICE_TABLE(sta2x11_mfd_tbl) = {
+static const struct pci_device_id sta2x11_mfd_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_GPIO)},
+ {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIC)},
{0,},
};
@@ -459,6 +670,8 @@ static int __init sta2x11_mfd_init(void)
*/
subsys_initcall(sta2x11_apbreg_init);
subsys_initcall(sta2x11_sctl_init);
+subsys_initcall(sta2x11_apb_soc_regs_init);
+subsys_initcall(sta2x11_scr_init);
rootfs_initcall(sta2x11_mfd_init);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c
index 947a06a1845..a45f9c0a330 100644
--- a/drivers/mfd/stmpe-i2c.c
+++ b/drivers/mfd/stmpe-i2c.c
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
+#include <linux/of_device.h>
#include "stmpe.h"
static int i2c_reg_read(struct stmpe *stmpe, u8 reg)
@@ -52,18 +53,44 @@ static struct stmpe_client_info i2c_ci = {
.write_block = i2c_block_write,
};
-static int __devinit
+static const struct of_device_id stmpe_of_match[] = {
+ { .compatible = "st,stmpe610", .data = (void *)STMPE610, },
+ { .compatible = "st,stmpe801", .data = (void *)STMPE801, },
+ { .compatible = "st,stmpe811", .data = (void *)STMPE811, },
+ { .compatible = "st,stmpe1601", .data = (void *)STMPE1601, },
+ { .compatible = "st,stmpe1801", .data = (void *)STMPE1801, },
+ { .compatible = "st,stmpe2401", .data = (void *)STMPE2401, },
+ { .compatible = "st,stmpe2403", .data = (void *)STMPE2403, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stmpe_of_match);
+
+static int
stmpe_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
{
+ int partnum;
+ const struct of_device_id *of_id;
+
i2c_ci.data = (void *)id;
i2c_ci.irq = i2c->irq;
i2c_ci.client = i2c;
i2c_ci.dev = &i2c->dev;
- return stmpe_probe(&i2c_ci, id->driver_data);
+ of_id = of_match_device(stmpe_of_match, &i2c->dev);
+ if (!of_id) {
+ /*
+ * This happens when the I2C ID matches the node name
+ * but no real compatible string has been given.
+ */
+ dev_info(&i2c->dev, "matching on node name, compatible is preferred\n");
+ partnum = id->driver_data;
+ } else
+ partnum = (int)of_id->data;
+
+ return stmpe_probe(&i2c_ci, partnum);
}
-static int __devexit stmpe_i2c_remove(struct i2c_client *i2c)
+static int stmpe_i2c_remove(struct i2c_client *i2c)
{
struct stmpe *stmpe = dev_get_drvdata(&i2c->dev);
@@ -75,6 +102,7 @@ static const struct i2c_device_id stmpe_i2c_id[] = {
{ "stmpe801", STMPE801 },
{ "stmpe811", STMPE811 },
{ "stmpe1601", STMPE1601 },
+ { "stmpe1801", STMPE1801 },
{ "stmpe2401", STMPE2401 },
{ "stmpe2403", STMPE2403 },
{ }
@@ -82,13 +110,16 @@ static const struct i2c_device_id stmpe_i2c_id[] = {
MODULE_DEVICE_TABLE(i2c, stmpe_id);
static struct i2c_driver stmpe_i2c_driver = {
- .driver.name = "stmpe-i2c",
- .driver.owner = THIS_MODULE,
+ .driver = {
+ .name = "stmpe-i2c",
+ .owner = THIS_MODULE,
#ifdef CONFIG_PM
- .driver.pm = &stmpe_dev_pm_ops,
+ .pm = &stmpe_dev_pm_ops,
#endif
+ .of_match_table = stmpe_of_match,
+ },
.probe = stmpe_i2c_probe,
- .remove = __devexit_p(stmpe_i2c_remove),
+ .remove = stmpe_i2c_remove,
.id_table = stmpe_i2c_id,
};
diff --git a/drivers/mfd/stmpe-spi.c b/drivers/mfd/stmpe-spi.c
index 9edfe864cc0..a81badbaa91 100644
--- a/drivers/mfd/stmpe-spi.c
+++ b/drivers/mfd/stmpe-spi.c
@@ -82,7 +82,7 @@ static struct stmpe_client_info spi_ci = {
.init = spi_init,
};
-static int __devinit
+static int
stmpe_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
@@ -101,9 +101,9 @@ stmpe_spi_probe(struct spi_device *spi)
return stmpe_probe(&spi_ci, id->driver_data);
}
-static int __devexit stmpe_spi_remove(struct spi_device *spi)
+static int stmpe_spi_remove(struct spi_device *spi)
{
- struct stmpe *stmpe = dev_get_drvdata(&spi->dev);
+ struct stmpe *stmpe = spi_get_drvdata(spi);
return stmpe_remove(stmpe);
}
@@ -128,7 +128,7 @@ static struct spi_driver stmpe_spi_driver = {
#endif
},
.probe = stmpe_spi_probe,
- .remove = __devexit_p(stmpe_spi_remove),
+ .remove = stmpe_spi_remove,
.id_table = stmpe_spi_id,
};
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index c94f521f392..3b6bfa7184a 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -7,14 +7,20 @@
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
*/
+#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/mfd/core.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
#include "stmpe.h"
static int __stmpe_enable(struct stmpe *stmpe, unsigned int blocks)
@@ -292,14 +298,16 @@ static struct resource stmpe_gpio_resources[] = {
},
};
-static struct mfd_cell stmpe_gpio_cell = {
+static const struct mfd_cell stmpe_gpio_cell = {
.name = "stmpe-gpio",
+ .of_compatible = "st,stmpe-gpio",
.resources = stmpe_gpio_resources,
.num_resources = ARRAY_SIZE(stmpe_gpio_resources),
};
-static struct mfd_cell stmpe_gpio_cell_noirq = {
+static const struct mfd_cell stmpe_gpio_cell_noirq = {
.name = "stmpe-gpio",
+ .of_compatible = "st,stmpe-gpio",
/* gpio cell resources consist of an irq only so no resources here */
};
@@ -310,20 +318,17 @@ static struct mfd_cell stmpe_gpio_cell_noirq = {
static struct resource stmpe_keypad_resources[] = {
{
.name = "KEYPAD",
- .start = 0,
- .end = 0,
.flags = IORESOURCE_IRQ,
},
{
.name = "KEYPAD_OVER",
- .start = 1,
- .end = 1,
.flags = IORESOURCE_IRQ,
},
};
-static struct mfd_cell stmpe_keypad_cell = {
+static const struct mfd_cell stmpe_keypad_cell = {
.name = "stmpe-keypad",
+ .of_compatible = "st,stmpe-keypad",
.resources = stmpe_keypad_resources,
.num_resources = ARRAY_SIZE(stmpe_keypad_resources),
};
@@ -397,20 +402,17 @@ static struct stmpe_variant_info stmpe801_noirq = {
static struct resource stmpe_ts_resources[] = {
{
.name = "TOUCH_DET",
- .start = 0,
- .end = 0,
.flags = IORESOURCE_IRQ,
},
{
.name = "FIFO_TH",
- .start = 1,
- .end = 1,
.flags = IORESOURCE_IRQ,
},
};
-static struct mfd_cell stmpe_ts_cell = {
+static const struct mfd_cell stmpe_ts_cell = {
.name = "stmpe-ts",
+ .of_compatible = "st,stmpe-ts",
.resources = stmpe_ts_resources,
.num_resources = ARRAY_SIZE(stmpe_ts_resources),
};
@@ -526,12 +528,12 @@ static const u8 stmpe1601_regs[] = {
static struct stmpe_variant_block stmpe1601_blocks[] = {
{
.cell = &stmpe_gpio_cell,
- .irq = STMPE24XX_IRQ_GPIOC,
+ .irq = STMPE1601_IRQ_GPIOC,
.block = STMPE_BLOCK_GPIO,
},
{
.cell = &stmpe_keypad_cell,
- .irq = STMPE24XX_IRQ_KEYPAD,
+ .irq = STMPE1601_IRQ_KEYPAD,
.block = STMPE_BLOCK_KEYPAD,
},
};
@@ -604,9 +606,18 @@ static int stmpe1601_enable(struct stmpe *stmpe, unsigned int blocks,
if (blocks & STMPE_BLOCK_GPIO)
mask |= STMPE1601_SYS_CTRL_ENABLE_GPIO;
+ else
+ mask &= ~STMPE1601_SYS_CTRL_ENABLE_GPIO;
if (blocks & STMPE_BLOCK_KEYPAD)
mask |= STMPE1601_SYS_CTRL_ENABLE_KPC;
+ else
+ mask &= ~STMPE1601_SYS_CTRL_ENABLE_KPC;
+
+ if (blocks & STMPE_BLOCK_PWM)
+ mask |= STMPE1601_SYS_CTRL_ENABLE_SPWM;
+ else
+ mask &= ~STMPE1601_SYS_CTRL_ENABLE_SPWM;
return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL, mask,
enable ? mask : 0);
@@ -643,6 +654,88 @@ static struct stmpe_variant_info stmpe1601 = {
};
/*
+ * STMPE1801
+ */
+static const u8 stmpe1801_regs[] = {
+ [STMPE_IDX_CHIP_ID] = STMPE1801_REG_CHIP_ID,
+ [STMPE_IDX_ICR_LSB] = STMPE1801_REG_INT_CTRL_LOW,
+ [STMPE_IDX_IER_LSB] = STMPE1801_REG_INT_EN_MASK_LOW,
+ [STMPE_IDX_ISR_LSB] = STMPE1801_REG_INT_STA_LOW,
+ [STMPE_IDX_GPMR_LSB] = STMPE1801_REG_GPIO_MP_LOW,
+ [STMPE_IDX_GPSR_LSB] = STMPE1801_REG_GPIO_SET_LOW,
+ [STMPE_IDX_GPCR_LSB] = STMPE1801_REG_GPIO_CLR_LOW,
+ [STMPE_IDX_GPDR_LSB] = STMPE1801_REG_GPIO_SET_DIR_LOW,
+ [STMPE_IDX_GPRER_LSB] = STMPE1801_REG_GPIO_RE_LOW,
+ [STMPE_IDX_GPFER_LSB] = STMPE1801_REG_GPIO_FE_LOW,
+ [STMPE_IDX_IEGPIOR_LSB] = STMPE1801_REG_INT_EN_GPIO_MASK_LOW,
+ [STMPE_IDX_ISGPIOR_LSB] = STMPE1801_REG_INT_STA_GPIO_LOW,
+};
+
+static struct stmpe_variant_block stmpe1801_blocks[] = {
+ {
+ .cell = &stmpe_gpio_cell,
+ .irq = STMPE1801_IRQ_GPIOC,
+ .block = STMPE_BLOCK_GPIO,
+ },
+ {
+ .cell = &stmpe_keypad_cell,
+ .irq = STMPE1801_IRQ_KEYPAD,
+ .block = STMPE_BLOCK_KEYPAD,
+ },
+};
+
+static int stmpe1801_enable(struct stmpe *stmpe, unsigned int blocks,
+ bool enable)
+{
+ unsigned int mask = 0;
+ if (blocks & STMPE_BLOCK_GPIO)
+ mask |= STMPE1801_MSK_INT_EN_GPIO;
+
+ if (blocks & STMPE_BLOCK_KEYPAD)
+ mask |= STMPE1801_MSK_INT_EN_KPC;
+
+ return __stmpe_set_bits(stmpe, STMPE1801_REG_INT_EN_MASK_LOW, mask,
+ enable ? mask : 0);
+}
+
+static int stmpe1801_reset(struct stmpe *stmpe)
+{
+ unsigned long timeout;
+ int ret = 0;
+
+ ret = __stmpe_set_bits(stmpe, STMPE1801_REG_SYS_CTRL,
+ STMPE1801_MSK_SYS_CTRL_RESET, STMPE1801_MSK_SYS_CTRL_RESET);
+ if (ret < 0)
+ return ret;
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while (time_before(jiffies, timeout)) {
+ ret = __stmpe_reg_read(stmpe, STMPE1801_REG_SYS_CTRL);
+ if (ret < 0)
+ return ret;
+ if (!(ret & STMPE1801_MSK_SYS_CTRL_RESET))
+ return 0;
+ usleep_range(100, 200);
+ }
+ return -EIO;
+}
+
+static struct stmpe_variant_info stmpe1801 = {
+ .name = "stmpe1801",
+ .id_val = STMPE1801_ID,
+ .id_mask = 0xfff0,
+ .num_gpios = 18,
+ .af_bits = 0,
+ .regs = stmpe1801_regs,
+ .blocks = stmpe1801_blocks,
+ .num_blocks = ARRAY_SIZE(stmpe1801_blocks),
+ .num_irqs = STMPE1801_NR_INTERNAL_IRQS,
+ .enable = stmpe1801_enable,
+ /* stmpe1801 do not have any gpio alternate function */
+ .get_altfunc = NULL,
+};
+
+/*
* STMPE24XX
*/
@@ -740,6 +833,7 @@ static struct stmpe_variant_info *stmpe_variant_info[STMPE_NBR_PARTS] = {
[STMPE801] = &stmpe801,
[STMPE811] = &stmpe811,
[STMPE1601] = &stmpe1601,
+ [STMPE1801] = &stmpe1801,
[STMPE2401] = &stmpe2401,
[STMPE2403] = &stmpe2403,
};
@@ -759,16 +853,23 @@ static irqreturn_t stmpe_irq(int irq, void *data)
struct stmpe *stmpe = data;
struct stmpe_variant_info *variant = stmpe->variant;
int num = DIV_ROUND_UP(variant->num_irqs, 8);
- u8 israddr = stmpe->regs[STMPE_IDX_ISR_MSB];
+ u8 israddr;
u8 isr[num];
int ret;
int i;
if (variant->id_val == STMPE801_ID) {
- handle_nested_irq(stmpe->irq_base);
+ int base = irq_create_mapping(stmpe->domain, 0);
+
+ handle_nested_irq(base);
return IRQ_HANDLED;
}
+ if (variant->id_val == STMPE1801_ID)
+ israddr = stmpe->regs[STMPE_IDX_ISR_LSB];
+ else
+ israddr = stmpe->regs[STMPE_IDX_ISR_MSB];
+
ret = stmpe_block_read(stmpe, israddr, num, isr);
if (ret < 0)
return IRQ_NONE;
@@ -786,8 +887,9 @@ static irqreturn_t stmpe_irq(int irq, void *data)
while (status) {
int bit = __ffs(status);
int line = bank * 8 + bit;
+ int nestedirq = irq_create_mapping(stmpe->domain, line);
- handle_nested_irq(stmpe->irq_base + line);
+ handle_nested_irq(nestedirq);
status &= ~(1 << bit);
}
@@ -828,7 +930,7 @@ static void stmpe_irq_sync_unlock(struct irq_data *data)
static void stmpe_irq_mask(struct irq_data *data)
{
struct stmpe *stmpe = irq_data_get_irq_chip_data(data);
- int offset = data->irq - stmpe->irq_base;
+ int offset = data->hwirq;
int regoffset = offset / 8;
int mask = 1 << (offset % 8);
@@ -838,7 +940,7 @@ static void stmpe_irq_mask(struct irq_data *data)
static void stmpe_irq_unmask(struct irq_data *data)
{
struct stmpe *stmpe = irq_data_get_irq_chip_data(data);
- int offset = data->irq - stmpe->irq_base;
+ int offset = data->hwirq;
int regoffset = offset / 8;
int mask = 1 << (offset % 8);
@@ -853,46 +955,58 @@ static struct irq_chip stmpe_irq_chip = {
.irq_unmask = stmpe_irq_unmask,
};
-static int __devinit stmpe_irq_init(struct stmpe *stmpe)
+static int stmpe_irq_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hwirq)
{
+ struct stmpe *stmpe = d->host_data;
struct irq_chip *chip = NULL;
- int num_irqs = stmpe->variant->num_irqs;
- int base = stmpe->irq_base;
- int irq;
if (stmpe->variant->id_val != STMPE801_ID)
chip = &stmpe_irq_chip;
- for (irq = base; irq < base + num_irqs; irq++) {
- irq_set_chip_data(irq, stmpe);
- irq_set_chip_and_handler(irq, chip, handle_edge_irq);
- irq_set_nested_thread(irq, 1);
+ irq_set_chip_data(virq, stmpe);
+ irq_set_chip_and_handler(virq, chip, handle_edge_irq);
+ irq_set_nested_thread(virq, 1);
#ifdef CONFIG_ARM
- set_irq_flags(irq, IRQF_VALID);
+ set_irq_flags(virq, IRQF_VALID);
#else
- irq_set_noprobe(irq);
+ irq_set_noprobe(virq);
#endif
- }
return 0;
}
-static void stmpe_irq_remove(struct stmpe *stmpe)
+static void stmpe_irq_unmap(struct irq_domain *d, unsigned int virq)
{
- int num_irqs = stmpe->variant->num_irqs;
- int base = stmpe->irq_base;
- int irq;
-
- for (irq = base; irq < base + num_irqs; irq++) {
#ifdef CONFIG_ARM
- set_irq_flags(irq, 0);
+ set_irq_flags(virq, 0);
#endif
- irq_set_chip_and_handler(irq, NULL, NULL);
- irq_set_chip_data(irq, NULL);
+ irq_set_chip_and_handler(virq, NULL, NULL);
+ irq_set_chip_data(virq, NULL);
+}
+
+static struct irq_domain_ops stmpe_irq_ops = {
+ .map = stmpe_irq_map,
+ .unmap = stmpe_irq_unmap,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int stmpe_irq_init(struct stmpe *stmpe, struct device_node *np)
+{
+ int base = 0;
+ int num_irqs = stmpe->variant->num_irqs;
+
+ stmpe->domain = irq_domain_add_simple(np, num_irqs, base,
+ &stmpe_irq_ops, stmpe);
+ if (!stmpe->domain) {
+ dev_err(stmpe->dev, "Failed to create irqdomain\n");
+ return -ENOSYS;
}
+
+ return 0;
}
-static int __devinit stmpe_chip_init(struct stmpe *stmpe)
+static int stmpe_chip_init(struct stmpe *stmpe)
{
unsigned int irq_trigger = stmpe->pdata->irq_trigger;
int autosleep_timeout = stmpe->pdata->autosleep_timeout;
@@ -920,6 +1034,12 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe)
if (ret)
return ret;
+ if (id == STMPE1801_ID) {
+ ret = stmpe1801_reset(stmpe);
+ if (ret < 0)
+ return ret;
+ }
+
if (stmpe->irq >= 0) {
if (id == STMPE801_ID)
icr = STMPE801_REG_SYS_CTRL_INT_EN;
@@ -940,13 +1060,6 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe)
else
icr |= STMPE_ICR_LSB_HIGH;
}
-
- if (stmpe->pdata->irq_invert_polarity) {
- if (id == STMPE801_ID)
- icr ^= STMPE801_REG_SYS_CTRL_INT_HI;
- else
- icr ^= STMPE_ICR_LSB_HIGH;
- }
}
if (stmpe->pdata->autosleep) {
@@ -958,19 +1071,18 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe)
return stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_ICR_LSB], icr);
}
-static int __devinit stmpe_add_device(struct stmpe *stmpe,
- struct mfd_cell *cell, int irq)
+static int stmpe_add_device(struct stmpe *stmpe, const struct mfd_cell *cell)
{
return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1,
- NULL, stmpe->irq_base + irq, NULL);
+ NULL, 0, stmpe->domain);
}
-static int __devinit stmpe_devices_init(struct stmpe *stmpe)
+static int stmpe_devices_init(struct stmpe *stmpe)
{
struct stmpe_variant_info *variant = stmpe->variant;
unsigned int platform_blocks = stmpe->pdata->blocks;
int ret = -EINVAL;
- int i;
+ int i, j;
for (i = 0; i < variant->num_blocks; i++) {
struct stmpe_variant_block *block = &variant->blocks[i];
@@ -978,8 +1090,17 @@ static int __devinit stmpe_devices_init(struct stmpe *stmpe)
if (!(platform_blocks & block->block))
continue;
+ for (j = 0; j < block->cell->num_resources; j++) {
+ struct resource *res =
+ (struct resource *) &block->cell->resources[j];
+
+ /* Dynamically fill in a variant's IRQ. */
+ if (res->flags & IORESOURCE_IRQ)
+ res->start = res->end = block->irq + j;
+ }
+
platform_blocks &= ~block->block;
- ret = stmpe_add_device(stmpe, block->cell, block->irq);
+ ret = stmpe_add_device(stmpe, block->cell);
if (ret)
return ret;
}
@@ -992,17 +1113,62 @@ static int __devinit stmpe_devices_init(struct stmpe *stmpe)
return ret;
}
+static void stmpe_of_probe(struct stmpe_platform_data *pdata,
+ struct device_node *np)
+{
+ struct device_node *child;
+
+ pdata->id = of_alias_get_id(np, "stmpe-i2c");
+ if (pdata->id < 0)
+ pdata->id = -1;
+
+ pdata->irq_trigger = IRQF_TRIGGER_NONE;
+
+ of_property_read_u32(np, "st,autosleep-timeout",
+ &pdata->autosleep_timeout);
+
+ pdata->autosleep = (pdata->autosleep_timeout) ? true : false;
+
+ for_each_child_of_node(np, child) {
+ if (!strcmp(child->name, "stmpe_gpio")) {
+ pdata->blocks |= STMPE_BLOCK_GPIO;
+ } else if (!strcmp(child->name, "stmpe_keypad")) {
+ pdata->blocks |= STMPE_BLOCK_KEYPAD;
+ } else if (!strcmp(child->name, "stmpe_touchscreen")) {
+ pdata->blocks |= STMPE_BLOCK_TOUCHSCREEN;
+ } else if (!strcmp(child->name, "stmpe_adc")) {
+ pdata->blocks |= STMPE_BLOCK_ADC;
+ } else if (!strcmp(child->name, "stmpe_pwm")) {
+ pdata->blocks |= STMPE_BLOCK_PWM;
+ } else if (!strcmp(child->name, "stmpe_rotator")) {
+ pdata->blocks |= STMPE_BLOCK_ROTATOR;
+ }
+ }
+}
+
/* Called from client specific probe routines */
-int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum)
+int stmpe_probe(struct stmpe_client_info *ci, int partnum)
{
struct stmpe_platform_data *pdata = dev_get_platdata(ci->dev);
+ struct device_node *np = ci->dev->of_node;
struct stmpe *stmpe;
int ret;
- if (!pdata)
- return -EINVAL;
+ if (!pdata) {
+ if (!np)
+ return -EINVAL;
+
+ pdata = devm_kzalloc(ci->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ stmpe_of_probe(pdata, np);
- stmpe = kzalloc(sizeof(struct stmpe), GFP_KERNEL);
+ if (of_find_property(np, "interrupts", NULL) == NULL)
+ ci->irq = -1;
+ }
+
+ stmpe = devm_kzalloc(ci->dev, sizeof(struct stmpe), GFP_KERNEL);
if (!stmpe)
return -ENOMEM;
@@ -1012,23 +1178,35 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum)
stmpe->dev = ci->dev;
stmpe->client = ci->client;
stmpe->pdata = pdata;
- stmpe->irq_base = pdata->irq_base;
stmpe->ci = ci;
stmpe->partnum = partnum;
stmpe->variant = stmpe_variant_info[partnum];
stmpe->regs = stmpe->variant->regs;
stmpe->num_gpios = stmpe->variant->num_gpios;
+ stmpe->vcc = devm_regulator_get_optional(ci->dev, "vcc");
+ if (!IS_ERR(stmpe->vcc)) {
+ ret = regulator_enable(stmpe->vcc);
+ if (ret)
+ dev_warn(ci->dev, "failed to enable VCC supply\n");
+ }
+ stmpe->vio = devm_regulator_get_optional(ci->dev, "vio");
+ if (!IS_ERR(stmpe->vio)) {
+ ret = regulator_enable(stmpe->vio);
+ if (ret)
+ dev_warn(ci->dev, "failed to enable VIO supply\n");
+ }
dev_set_drvdata(stmpe->dev, stmpe);
if (ci->init)
ci->init(stmpe);
if (pdata->irq_over_gpio) {
- ret = gpio_request_one(pdata->irq_gpio, GPIOF_DIR_IN, "stmpe");
+ ret = devm_gpio_request_one(ci->dev, pdata->irq_gpio,
+ GPIOF_DIR_IN, "stmpe");
if (ret) {
dev_err(stmpe->dev, "failed to request IRQ GPIO: %d\n",
ret);
- goto out_free;
+ return ret;
}
stmpe->irq = gpio_to_irq(pdata->irq_gpio);
@@ -1045,67 +1223,50 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum)
dev_err(stmpe->dev,
"%s does not support no-irq mode!\n",
stmpe->variant->name);
- ret = -ENODEV;
- goto free_gpio;
+ return -ENODEV;
}
stmpe->variant = stmpe_noirq_variant_info[stmpe->partnum];
+ } else if (pdata->irq_trigger == IRQF_TRIGGER_NONE) {
+ pdata->irq_trigger = irq_get_trigger_type(stmpe->irq);
}
ret = stmpe_chip_init(stmpe);
if (ret)
- goto free_gpio;
+ return ret;
if (stmpe->irq >= 0) {
- ret = stmpe_irq_init(stmpe);
+ ret = stmpe_irq_init(stmpe, np);
if (ret)
- goto free_gpio;
+ return ret;
- ret = request_threaded_irq(stmpe->irq, NULL, stmpe_irq,
- pdata->irq_trigger | IRQF_ONESHOT,
+ ret = devm_request_threaded_irq(ci->dev, stmpe->irq, NULL,
+ stmpe_irq, pdata->irq_trigger | IRQF_ONESHOT,
"stmpe", stmpe);
if (ret) {
dev_err(stmpe->dev, "failed to request IRQ: %d\n",
ret);
- goto out_removeirq;
+ return ret;
}
}
ret = stmpe_devices_init(stmpe);
- if (ret) {
- dev_err(stmpe->dev, "failed to add children\n");
- goto out_removedevs;
- }
-
- return 0;
+ if (!ret)
+ return 0;
-out_removedevs:
+ dev_err(stmpe->dev, "failed to add children\n");
mfd_remove_devices(stmpe->dev);
- if (stmpe->irq >= 0)
- free_irq(stmpe->irq, stmpe);
-out_removeirq:
- if (stmpe->irq >= 0)
- stmpe_irq_remove(stmpe);
-free_gpio:
- if (pdata->irq_over_gpio)
- gpio_free(pdata->irq_gpio);
-out_free:
- kfree(stmpe);
+
return ret;
}
int stmpe_remove(struct stmpe *stmpe)
{
- mfd_remove_devices(stmpe->dev);
-
- if (stmpe->irq >= 0) {
- free_irq(stmpe->irq, stmpe);
- stmpe_irq_remove(stmpe);
- }
-
- if (stmpe->pdata->irq_over_gpio)
- gpio_free(stmpe->pdata->irq_gpio);
+ if (!IS_ERR(stmpe->vio))
+ regulator_disable(stmpe->vio);
+ if (!IS_ERR(stmpe->vcc))
+ regulator_disable(stmpe->vcc);
- kfree(stmpe);
+ mfd_remove_devices(stmpe->dev);
return 0;
}
diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h
index 7b8e13f5b76..9e4d21d37a1 100644
--- a/drivers/mfd/stmpe.h
+++ b/drivers/mfd/stmpe.h
@@ -38,7 +38,7 @@ static inline void stmpe_dump_bytes(const char *str, const void *buf,
* enable and altfunc callbacks
*/
struct stmpe_variant_block {
- struct mfd_cell *cell;
+ const struct mfd_cell *cell;
int irq;
enum stmpe_block block;
};
@@ -192,13 +192,62 @@ int stmpe_remove(struct stmpe *stmpe);
#define STMPE1601_SYS_CTRL_ENABLE_GPIO (1 << 3)
#define STMPE1601_SYS_CTRL_ENABLE_KPC (1 << 1)
-#define STMPE1601_SYSCON_ENABLE_SPWM (1 << 0)
+#define STMPE1601_SYS_CTRL_ENABLE_SPWM (1 << 0)
/* The 1601/2403 share the same masks */
#define STMPE1601_AUTOSLEEP_TIMEOUT_MASK (0x7)
#define STPME1601_AUTOSLEEP_ENABLE (1 << 3)
/*
+ * STMPE1801
+ */
+#define STMPE1801_ID 0xc110
+#define STMPE1801_NR_INTERNAL_IRQS 5
+#define STMPE1801_IRQ_KEYPAD_COMBI 4
+#define STMPE1801_IRQ_GPIOC 3
+#define STMPE1801_IRQ_KEYPAD_OVER 2
+#define STMPE1801_IRQ_KEYPAD 1
+#define STMPE1801_IRQ_WAKEUP 0
+
+#define STMPE1801_REG_CHIP_ID 0x00
+#define STMPE1801_REG_SYS_CTRL 0x02
+#define STMPE1801_REG_INT_CTRL_LOW 0x04
+#define STMPE1801_REG_INT_EN_MASK_LOW 0x06
+#define STMPE1801_REG_INT_STA_LOW 0x08
+#define STMPE1801_REG_INT_EN_GPIO_MASK_LOW 0x0A
+#define STMPE1801_REG_INT_EN_GPIO_MASK_MID 0x0B
+#define STMPE1801_REG_INT_EN_GPIO_MASK_HIGH 0x0C
+#define STMPE1801_REG_INT_STA_GPIO_LOW 0x0D
+#define STMPE1801_REG_INT_STA_GPIO_MID 0x0E
+#define STMPE1801_REG_INT_STA_GPIO_HIGH 0x0F
+#define STMPE1801_REG_GPIO_SET_LOW 0x10
+#define STMPE1801_REG_GPIO_SET_MID 0x11
+#define STMPE1801_REG_GPIO_SET_HIGH 0x12
+#define STMPE1801_REG_GPIO_CLR_LOW 0x13
+#define STMPE1801_REG_GPIO_CLR_MID 0x14
+#define STMPE1801_REG_GPIO_CLR_HIGH 0x15
+#define STMPE1801_REG_GPIO_MP_LOW 0x16
+#define STMPE1801_REG_GPIO_MP_MID 0x17
+#define STMPE1801_REG_GPIO_MP_HIGH 0x18
+#define STMPE1801_REG_GPIO_SET_DIR_LOW 0x19
+#define STMPE1801_REG_GPIO_SET_DIR_MID 0x1A
+#define STMPE1801_REG_GPIO_SET_DIR_HIGH 0x1B
+#define STMPE1801_REG_GPIO_RE_LOW 0x1C
+#define STMPE1801_REG_GPIO_RE_MID 0x1D
+#define STMPE1801_REG_GPIO_RE_HIGH 0x1E
+#define STMPE1801_REG_GPIO_FE_LOW 0x1F
+#define STMPE1801_REG_GPIO_FE_MID 0x20
+#define STMPE1801_REG_GPIO_FE_HIGH 0x21
+#define STMPE1801_REG_GPIO_PULL_UP_LOW 0x22
+#define STMPE1801_REG_GPIO_PULL_UP_MID 0x23
+#define STMPE1801_REG_GPIO_PULL_UP_HIGH 0x24
+
+#define STMPE1801_MSK_SYS_CTRL_RESET (1 << 7)
+
+#define STMPE1801_MSK_INT_EN_KPC (1 << 1)
+#define STMPE1801_MSK_INT_EN_GPIO (1 << 3)
+
+/*
* STMPE24xx
*/
diff --git a/drivers/mfd/stw481x.c b/drivers/mfd/stw481x.c
new file mode 100644
index 00000000000..7ceb3df09e2
--- /dev/null
+++ b/drivers/mfd/stw481x.c
@@ -0,0 +1,256 @@
+/*
+ * Core driver for STw4810/STw4811
+ *
+ * Copyright (C) 2013 ST-Ericsson SA
+ * Written on behalf of Linaro for ST-Ericsson
+ *
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/stw481x.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+/*
+ * This driver can only access the non-USB portions of STw4811, the register
+ * range 0x00-0x10 dealing with USB is bound to the two special I2C pins used
+ * for USB control.
+ */
+
+/* Registers inside the power control address space */
+#define STW_PC_VCORE_SEL 0x05U
+#define STW_PC_VAUX_SEL 0x06U
+#define STW_PC_VPLL_SEL 0x07U
+
+/**
+ * stw481x_get_pctl_reg() - get a power control register
+ * @stw481x: handle to the stw481x chip
+ * @reg: power control register to fetch
+ *
+ * The power control registers is a set of one-time-programmable registers
+ * in its own register space, accessed by writing addess bits to these
+ * two registers: bits 7,6,5 of PCTL_REG_LO corresponds to the 3 LSBs of
+ * the address and bits 8,9 of PCTL_REG_HI corresponds to the 2 MSBs of
+ * the address, forming an address space of 5 bits, i.e. 32 registers
+ * 0x00 ... 0x1f can be obtained.
+ */
+static int stw481x_get_pctl_reg(struct stw481x *stw481x, u8 reg)
+{
+ u8 msb = (reg >> 3) & 0x03;
+ u8 lsb = (reg << 5) & 0xe0;
+ unsigned int val;
+ u8 vrfy;
+ int ret;
+
+ ret = regmap_write(stw481x->map, STW_PCTL_REG_HI, msb);
+ if (ret)
+ return ret;
+ ret = regmap_write(stw481x->map, STW_PCTL_REG_LO, lsb);
+ if (ret)
+ return ret;
+ ret = regmap_read(stw481x->map, STW_PCTL_REG_HI, &val);
+ if (ret)
+ return ret;
+ vrfy = (val & 0x03) << 3;
+ ret = regmap_read(stw481x->map, STW_PCTL_REG_LO, &val);
+ if (ret)
+ return ret;
+ vrfy |= ((val >> 5) & 0x07);
+ if (vrfy != reg)
+ return -EIO;
+ return (val >> 1) & 0x0f;
+}
+
+static int stw481x_startup(struct stw481x *stw481x)
+{
+ /* Voltages multiplied by 100 */
+ u8 vcore_val[] = { 100, 105, 110, 115, 120, 122, 124, 126, 128,
+ 130, 132, 134, 136, 138, 140, 145 };
+ u8 vpll_val[] = { 105, 120, 130, 180 };
+ u8 vaux_val[] = { 15, 18, 25, 28 };
+ u8 vcore;
+ u8 vcore_slp;
+ u8 vpll;
+ u8 vaux;
+ bool vaux_en;
+ bool it_warn;
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(stw481x->map, STW_CONF1, &val);
+ if (ret)
+ return ret;
+ vaux_en = !!(val & STW_CONF1_PDN_VAUX);
+ it_warn = !!(val & STW_CONF1_IT_WARN);
+
+ dev_info(&stw481x->client->dev, "voltages %s\n",
+ (val & STW_CONF1_V_MONITORING) ? "OK" : "LOW");
+ dev_info(&stw481x->client->dev, "MMC level shifter %s\n",
+ (val & STW_CONF1_MMC_LS_STATUS) ? "high impedance" : "ON");
+ dev_info(&stw481x->client->dev, "VMMC: %s\n",
+ (val & STW_CONF1_PDN_VMMC) ? "ON" : "disabled");
+
+ dev_info(&stw481x->client->dev, "STw481x power control registers:\n");
+
+ ret = stw481x_get_pctl_reg(stw481x, STW_PC_VCORE_SEL);
+ if (ret < 0)
+ return ret;
+ vcore = ret & 0x0f;
+
+ ret = stw481x_get_pctl_reg(stw481x, STW_PC_VAUX_SEL);
+ if (ret < 0)
+ return ret;
+ vaux = (ret >> 2) & 3;
+ vpll = (ret >> 4) & 1; /* Save bit 4 */
+
+ ret = stw481x_get_pctl_reg(stw481x, STW_PC_VPLL_SEL);
+ if (ret < 0)
+ return ret;
+ vpll |= (ret >> 1) & 2;
+
+ dev_info(&stw481x->client->dev, "VCORE: %u.%uV %s\n",
+ vcore_val[vcore] / 100, vcore_val[vcore] % 100,
+ (ret & 4) ? "ON" : "OFF");
+
+ dev_info(&stw481x->client->dev, "VPLL: %u.%uV %s\n",
+ vpll_val[vpll] / 100, vpll_val[vpll] % 100,
+ (ret & 0x10) ? "ON" : "OFF");
+
+ dev_info(&stw481x->client->dev, "VAUX: %u.%uV %s\n",
+ vaux_val[vaux] / 10, vaux_val[vaux] % 10,
+ vaux_en ? "ON" : "OFF");
+
+ ret = regmap_read(stw481x->map, STW_CONF2, &val);
+ if (ret)
+ return ret;
+
+ dev_info(&stw481x->client->dev, "TWARN: %s threshold, %s\n",
+ it_warn ? "below" : "above",
+ (val & STW_CONF2_MASK_TWARN) ?
+ "enabled" : "mask through VDDOK");
+ dev_info(&stw481x->client->dev, "VMMC: %s\n",
+ (val & STW_CONF2_VMMC_EXT) ? "internal" : "external");
+ dev_info(&stw481x->client->dev, "IT WAKE UP: %s\n",
+ (val & STW_CONF2_MASK_IT_WAKE_UP) ? "enabled" : "masked");
+ dev_info(&stw481x->client->dev, "GPO1: %s\n",
+ (val & STW_CONF2_GPO1) ? "low" : "high impedance");
+ dev_info(&stw481x->client->dev, "GPO2: %s\n",
+ (val & STW_CONF2_GPO2) ? "low" : "high impedance");
+
+ ret = regmap_read(stw481x->map, STW_VCORE_SLEEP, &val);
+ if (ret)
+ return ret;
+ vcore_slp = val & 0x0f;
+ dev_info(&stw481x->client->dev, "VCORE SLEEP: %u.%uV\n",
+ vcore_val[vcore_slp] / 100, vcore_val[vcore_slp] % 100);
+
+ return 0;
+}
+
+/*
+ * MFD cells - we have one cell which is selected operation
+ * mode, and we always have a GPIO cell.
+ */
+static struct mfd_cell stw481x_cells[] = {
+ {
+ .of_compatible = "st,stw481x-vmmc",
+ .name = "stw481x-vmmc-regulator",
+ .id = -1,
+ },
+};
+
+static const struct regmap_config stw481x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int stw481x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct stw481x *stw481x;
+ int ret;
+ int i;
+
+ stw481x = devm_kzalloc(&client->dev, sizeof(*stw481x), GFP_KERNEL);
+ if (!stw481x)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, stw481x);
+ stw481x->client = client;
+ stw481x->map = devm_regmap_init_i2c(client, &stw481x_regmap_config);
+ if (IS_ERR(stw481x->map)) {
+ ret = PTR_ERR(stw481x->map);
+ dev_err(&client->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = stw481x_startup(stw481x);
+ if (ret) {
+ dev_err(&client->dev, "chip initialization failed\n");
+ return ret;
+ }
+
+ /* Set up and register the platform devices. */
+ for (i = 0; i < ARRAY_SIZE(stw481x_cells); i++) {
+ /* One state holder for all drivers, this is simple */
+ stw481x_cells[i].platform_data = stw481x;
+ stw481x_cells[i].pdata_size = sizeof(*stw481x);
+ }
+
+ ret = mfd_add_devices(&client->dev, 0, stw481x_cells,
+ ARRAY_SIZE(stw481x_cells), NULL, 0, NULL);
+ if (ret)
+ return ret;
+
+ dev_info(&client->dev, "initialized STw481x device\n");
+
+ return ret;
+}
+
+static int stw481x_remove(struct i2c_client *client)
+{
+ mfd_remove_devices(&client->dev);
+ return 0;
+}
+
+/*
+ * This ID table is completely unused, as this is a pure
+ * device-tree probed driver, but it has to be here due to
+ * the structure of the I2C core.
+ */
+static const struct i2c_device_id stw481x_id[] = {
+ { "stw481x", 0 },
+ { },
+};
+
+static const struct of_device_id stw481x_match[] = {
+ { .compatible = "st,stw4810", },
+ { .compatible = "st,stw4811", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, stw481x_match);
+
+static struct i2c_driver stw481x_driver = {
+ .driver = {
+ .name = "stw481x",
+ .of_match_table = stw481x_match,
+ },
+ .probe = stw481x_probe,
+ .remove = stw481x_remove,
+ .id_table = stw481x_id,
+};
+
+module_i2c_driver(stw481x_driver);
+
+MODULE_AUTHOR("Linus Walleij");
+MODULE_DESCRIPTION("STw481x PMIC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/sun6i-prcm.c b/drivers/mfd/sun6i-prcm.c
new file mode 100644
index 00000000000..718fc4d2adc
--- /dev/null
+++ b/drivers/mfd/sun6i-prcm.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
+ *
+ * Allwinner PRCM (Power/Reset/Clock Management) driver
+ *
+ */
+
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+struct prcm_data {
+ int nsubdevs;
+ const struct mfd_cell *subdevs;
+};
+
+static const struct resource sun6i_a31_ar100_clk_res[] = {
+ {
+ .start = 0x0,
+ .end = 0x3,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static const struct resource sun6i_a31_apb0_clk_res[] = {
+ {
+ .start = 0xc,
+ .end = 0xf,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static const struct resource sun6i_a31_apb0_gates_clk_res[] = {
+ {
+ .start = 0x28,
+ .end = 0x2b,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static const struct resource sun6i_a31_apb0_rstc_res[] = {
+ {
+ .start = 0xb0,
+ .end = 0xb3,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static const struct mfd_cell sun6i_a31_prcm_subdevs[] = {
+ {
+ .name = "sun6i-a31-ar100-clk",
+ .of_compatible = "allwinner,sun6i-a31-ar100-clk",
+ .num_resources = ARRAY_SIZE(sun6i_a31_ar100_clk_res),
+ .resources = sun6i_a31_ar100_clk_res,
+ },
+ {
+ .name = "sun6i-a31-apb0-clk",
+ .of_compatible = "allwinner,sun6i-a31-apb0-clk",
+ .num_resources = ARRAY_SIZE(sun6i_a31_apb0_clk_res),
+ .resources = sun6i_a31_apb0_clk_res,
+ },
+ {
+ .name = "sun6i-a31-apb0-gates-clk",
+ .of_compatible = "allwinner,sun6i-a31-apb0-gates-clk",
+ .num_resources = ARRAY_SIZE(sun6i_a31_apb0_gates_clk_res),
+ .resources = sun6i_a31_apb0_gates_clk_res,
+ },
+ {
+ .name = "sun6i-a31-apb0-clock-reset",
+ .of_compatible = "allwinner,sun6i-a31-clock-reset",
+ .num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res),
+ .resources = sun6i_a31_apb0_rstc_res,
+ },
+};
+
+static const struct prcm_data sun6i_a31_prcm_data = {
+ .nsubdevs = ARRAY_SIZE(sun6i_a31_prcm_subdevs),
+ .subdevs = sun6i_a31_prcm_subdevs,
+};
+
+static const struct of_device_id sun6i_prcm_dt_ids[] = {
+ {
+ .compatible = "allwinner,sun6i-a31-prcm",
+ .data = &sun6i_a31_prcm_data,
+ },
+ { /* sentinel */ },
+};
+
+static int sun6i_prcm_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *match;
+ const struct prcm_data *data;
+ struct resource *res;
+ int ret;
+
+ match = of_match_node(sun6i_prcm_dt_ids, np);
+ if (!match)
+ return -EINVAL;
+
+ data = match->data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no prcm memory region provided\n");
+ return -ENOENT;
+ }
+
+ ret = mfd_add_devices(&pdev->dev, 0, data->subdevs, data->nsubdevs,
+ res, -1, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add subdevices\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver sun6i_prcm_driver = {
+ .driver = {
+ .name = "sun6i-prcm",
+ .owner = THIS_MODULE,
+ .of_match_table = sun6i_prcm_dt_ids,
+ },
+ .probe = sun6i_prcm_probe,
+};
+module_platform_driver(sun6i_prcm_driver);
+
+MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("Allwinner sun6i PRCM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index 65fe609026c..ca15878ce5c 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -18,23 +18,22 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
+#include <linux/platform_data/syscon.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
static struct platform_driver syscon_driver;
struct syscon {
- struct device *dev;
- void __iomem *base;
struct regmap *regmap;
};
-static int syscon_match(struct device *dev, void *data)
+static int syscon_match_node(struct device *dev, void *data)
{
- struct syscon *syscon = dev_get_drvdata(dev);
struct device_node *dn = data;
- return (syscon->dev->of_node == dn) ? 1 : 0;
+ return (dev->of_node == dn) ? 1 : 0;
}
struct regmap *syscon_node_to_regmap(struct device_node *np)
@@ -43,7 +42,7 @@ struct regmap *syscon_node_to_regmap(struct device_node *np)
struct device *dev;
dev = driver_find_device(&syscon_driver.driver, NULL, np,
- syscon_match);
+ syscon_match_node);
if (!dev)
return ERR_PTR(-EPROBE_DEFER);
@@ -69,13 +68,38 @@ struct regmap *syscon_regmap_lookup_by_compatible(const char *s)
}
EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible);
+static int syscon_match_pdevname(struct device *dev, void *data)
+{
+ return !strcmp(dev_name(dev), (const char *)data);
+}
+
+struct regmap *syscon_regmap_lookup_by_pdevname(const char *s)
+{
+ struct device *dev;
+ struct syscon *syscon;
+
+ dev = driver_find_device(&syscon_driver.driver, NULL, (void *)s,
+ syscon_match_pdevname);
+ if (!dev)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ syscon = dev_get_drvdata(dev);
+
+ return syscon->regmap;
+}
+EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_pdevname);
+
struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
const char *property)
{
struct device_node *syscon_np;
struct regmap *regmap;
- syscon_np = of_parse_phandle(np, property, 0);
+ if (property)
+ syscon_np = of_parse_phandle(np, property, 0);
+ else
+ syscon_np = np;
+
if (!syscon_np)
return ERR_PTR(-ENODEV);
@@ -97,57 +121,47 @@ static struct regmap_config syscon_regmap_config = {
.reg_stride = 4,
};
-static int __devinit syscon_probe(struct platform_device *pdev)
+static int syscon_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
+ struct syscon_platform_data *pdata = dev_get_platdata(dev);
struct syscon *syscon;
- struct resource res;
- int ret;
-
- if (!np)
- return -ENOENT;
+ struct resource *res;
+ void __iomem *base;
- syscon = devm_kzalloc(dev, sizeof(struct syscon),
- GFP_KERNEL);
+ syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL);
if (!syscon)
return -ENOMEM;
- syscon->base = of_iomap(np, 0);
- if (!syscon->base)
- return -EADDRNOTAVAIL;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOENT;
- ret = of_address_to_resource(np, 0, &res);
- if (ret)
- return ret;
+ base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!base)
+ return -ENOMEM;
- syscon_regmap_config.max_register = res.end - res.start - 3;
- syscon->regmap = devm_regmap_init_mmio(dev, syscon->base,
+ syscon_regmap_config.max_register = res->end - res->start - 3;
+ if (pdata)
+ syscon_regmap_config.name = pdata->label;
+ syscon->regmap = devm_regmap_init_mmio(dev, base,
&syscon_regmap_config);
if (IS_ERR(syscon->regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(syscon->regmap);
}
- syscon->dev = dev;
platform_set_drvdata(pdev, syscon);
- dev_info(dev, "syscon regmap start 0x%x end 0x%x registered\n",
- res.start, res.end);
+ dev_dbg(dev, "regmap %pR registered\n", res);
return 0;
}
-static int __devexit syscon_remove(struct platform_device *pdev)
-{
- struct syscon *syscon;
-
- syscon = platform_get_drvdata(pdev);
- iounmap(syscon->base);
- platform_set_drvdata(pdev, NULL);
-
- return 0;
-}
+static const struct platform_device_id syscon_ids[] = {
+ { "syscon", },
+ { }
+};
static struct platform_driver syscon_driver = {
.driver = {
@@ -156,7 +170,7 @@ static struct platform_driver syscon_driver = {
.of_match_table = of_syscon_match,
},
.probe = syscon_probe,
- .remove = __devexit_p(syscon_remove),
+ .id_table = syscon_ids,
};
static int __init syscon_init(void)
diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c
index b32940ec903..9e04a748598 100644
--- a/drivers/mfd/t7l66xb.c
+++ b/drivers/mfd/t7l66xb.c
@@ -281,7 +281,7 @@ static void t7l66xb_detach_irq(struct platform_device *dev)
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_get_platdata(&dev->dev);
if (pdata && pdata->suspend)
pdata->suspend(dev);
@@ -293,7 +293,7 @@ static int t7l66xb_suspend(struct platform_device *dev, pm_message_t state)
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_get_platdata(&dev->dev);
clk_enable(t7l66xb->clk48m);
if (pdata && pdata->resume)
@@ -313,7 +313,7 @@ static int t7l66xb_resume(struct platform_device *dev)
static int t7l66xb_probe(struct platform_device *dev)
{
- struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
+ struct t7l66xb_platform_data *pdata = dev_get_platdata(&dev->dev);
struct t7l66xb *t7l66xb;
struct resource *iomem, *rscr;
int ret;
@@ -409,7 +409,7 @@ err_noirq:
static int t7l66xb_remove(struct platform_device *dev)
{
- struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
+ struct t7l66xb_platform_data *pdata = dev_get_platdata(&dev->dev);
struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
int ret;
@@ -422,7 +422,6 @@ static int t7l66xb_remove(struct platform_device *dev)
iounmap(t7l66xb->scr);
release_resource(&t7l66xb->rscr);
mfd_remove_devices(&dev->dev);
- platform_set_drvdata(dev, NULL);
kfree(t7l66xb);
return ret;
diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c
index 8f4c853ca11..bd83accc0f6 100644
--- a/drivers/mfd/tc3589x.c
+++ b/drivers/mfd/tc3589x.c
@@ -13,8 +13,23 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tc3589x.h>
+#include <linux/err.h>
+
+/**
+ * enum tc3589x_version - indicates the TC3589x version
+ */
+enum tc3589x_version {
+ TC3589X_TC35890,
+ TC3589X_TC35892,
+ TC3589X_TC35893,
+ TC3589X_TC35894,
+ TC3589X_TC35895,
+ TC3589X_TC35896,
+ TC3589X_UNKNOWN,
+};
#define TC3589x_CLKMODE_MODCTL_SLEEP 0x0
#define TC3589x_CLKMODE_MODCTL_OPERATION (1 << 0)
@@ -142,21 +157,21 @@ static struct resource keypad_resources[] = {
},
};
-static struct mfd_cell tc3589x_dev_gpio[] = {
+static const struct mfd_cell tc3589x_dev_gpio[] = {
{
.name = "tc3589x-gpio",
.num_resources = ARRAY_SIZE(gpio_resources),
.resources = &gpio_resources[0],
- .of_compatible = "tc3589x-gpio",
+ .of_compatible = "toshiba,tc3589x-gpio",
},
};
-static struct mfd_cell tc3589x_dev_keypad[] = {
+static const struct mfd_cell tc3589x_dev_keypad[] = {
{
.name = "tc3589x-keypad",
.num_resources = ARRAY_SIZE(keypad_resources),
.resources = &keypad_resources[0],
- .of_compatible = "tc3589x-keypad",
+ .of_compatible = "toshiba,tc3589x-keypad",
},
};
@@ -219,25 +234,18 @@ static void tc3589x_irq_unmap(struct irq_domain *d, unsigned int virq)
}
static struct irq_domain_ops tc3589x_irq_ops = {
- .map = tc3589x_irq_map,
+ .map = tc3589x_irq_map,
.unmap = tc3589x_irq_unmap,
- .xlate = irq_domain_xlate_twocell,
+ .xlate = irq_domain_xlate_twocell,
};
static int tc3589x_irq_init(struct tc3589x *tc3589x, struct device_node *np)
{
int base = tc3589x->irq_base;
- if (base) {
- tc3589x->domain = irq_domain_add_legacy(
- NULL, TC3589x_NR_INTERNAL_IRQS, base,
- 0, &tc3589x_irq_ops, tc3589x);
- }
- else {
- tc3589x->domain = irq_domain_add_linear(
- np, TC3589x_NR_INTERNAL_IRQS,
- &tc3589x_irq_ops, tc3589x);
- }
+ tc3589x->domain = irq_domain_add_simple(
+ np, TC3589x_NR_INTERNAL_IRQS, base,
+ &tc3589x_irq_ops, tc3589x);
if (!tc3589x->domain) {
dev_err(tc3589x->dev, "Failed to create irqdomain\n");
@@ -282,7 +290,7 @@ static int tc3589x_chip_init(struct tc3589x *tc3589x)
return tc3589x_reg_write(tc3589x, TC3589x_RSTINTCLR, 0x1);
}
-static int __devinit tc3589x_device_init(struct tc3589x *tc3589x)
+static int tc3589x_device_init(struct tc3589x *tc3589x)
{
int ret = 0;
unsigned int blocks = tc3589x->pdata->block;
@@ -312,52 +320,82 @@ static int __devinit tc3589x_device_init(struct tc3589x *tc3589x)
return ret;
}
-static int tc3589x_of_probe(struct device_node *np,
- struct tc3589x_platform_data *pdata)
+#ifdef CONFIG_OF
+static const struct of_device_id tc3589x_match[] = {
+ /* Legacy compatible string */
+ { .compatible = "tc3589x", .data = (void *) TC3589X_UNKNOWN },
+ { .compatible = "toshiba,tc35890", .data = (void *) TC3589X_TC35890 },
+ { .compatible = "toshiba,tc35892", .data = (void *) TC3589X_TC35892 },
+ { .compatible = "toshiba,tc35893", .data = (void *) TC3589X_TC35893 },
+ { .compatible = "toshiba,tc35894", .data = (void *) TC3589X_TC35894 },
+ { .compatible = "toshiba,tc35895", .data = (void *) TC3589X_TC35895 },
+ { .compatible = "toshiba,tc35896", .data = (void *) TC3589X_TC35896 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, tc3589x_match);
+
+static struct tc3589x_platform_data *
+tc3589x_of_probe(struct device *dev, enum tc3589x_version *version)
{
+ struct device_node *np = dev->of_node;
+ struct tc3589x_platform_data *pdata;
struct device_node *child;
+ const struct of_device_id *of_id;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ of_id = of_match_device(tc3589x_match, dev);
+ if (!of_id)
+ return ERR_PTR(-ENODEV);
+ *version = (enum tc3589x_version) of_id->data;
for_each_child_of_node(np, child) {
- if (!strcmp(child->name, "tc3589x_gpio")) {
+ if (of_device_is_compatible(child, "toshiba,tc3589x-gpio"))
pdata->block |= TC3589x_BLOCK_GPIO;
- }
- if (!strcmp(child->name, "tc3589x_keypad")) {
+ if (of_device_is_compatible(child, "toshiba,tc3589x-keypad"))
pdata->block |= TC3589x_BLOCK_KEYPAD;
- }
}
- return 0;
+ return pdata;
}
+#else
+static inline struct tc3589x_platform_data *
+tc3589x_of_probe(struct device *dev, enum tc3589x_version *version)
+{
+ dev_err(dev, "no device tree support\n");
+ return ERR_PTR(-ENODEV);
+}
+#endif
-static int __devinit tc3589x_probe(struct i2c_client *i2c,
+static int tc3589x_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct tc3589x_platform_data *pdata = i2c->dev.platform_data;
struct device_node *np = i2c->dev.of_node;
+ struct tc3589x_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct tc3589x *tc3589x;
+ enum tc3589x_version version;
int ret;
if (!pdata) {
- if (np) {
- pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- ret = tc3589x_of_probe(np, pdata);
- if (ret)
- return ret;
- }
- else {
+ pdata = tc3589x_of_probe(&i2c->dev, &version);
+ if (IS_ERR(pdata)) {
dev_err(&i2c->dev, "No platform data or DT found\n");
- return -EINVAL;
+ return PTR_ERR(pdata);
}
+ } else {
+ /* When not probing from device tree we have this ID */
+ version = id->driver_data;
}
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA
| I2C_FUNC_SMBUS_I2C_BLOCK))
return -EIO;
- tc3589x = kzalloc(sizeof(struct tc3589x), GFP_KERNEL);
+ tc3589x = devm_kzalloc(&i2c->dev, sizeof(struct tc3589x),
+ GFP_KERNEL);
if (!tc3589x)
return -ENOMEM;
@@ -367,51 +405,55 @@ static int __devinit tc3589x_probe(struct i2c_client *i2c,
tc3589x->i2c = i2c;
tc3589x->pdata = pdata;
tc3589x->irq_base = pdata->irq_base;
- tc3589x->num_gpio = id->driver_data;
+
+ switch (version) {
+ case TC3589X_TC35893:
+ case TC3589X_TC35895:
+ case TC3589X_TC35896:
+ tc3589x->num_gpio = 20;
+ break;
+ case TC3589X_TC35890:
+ case TC3589X_TC35892:
+ case TC3589X_TC35894:
+ case TC3589X_UNKNOWN:
+ default:
+ tc3589x->num_gpio = 24;
+ break;
+ }
i2c_set_clientdata(i2c, tc3589x);
ret = tc3589x_chip_init(tc3589x);
if (ret)
- goto out_free;
+ return ret;
ret = tc3589x_irq_init(tc3589x, np);
if (ret)
- goto out_free;
+ return ret;
ret = request_threaded_irq(tc3589x->i2c->irq, NULL, tc3589x_irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"tc3589x", tc3589x);
if (ret) {
dev_err(tc3589x->dev, "failed to request IRQ: %d\n", ret);
- goto out_free;
+ return ret;
}
ret = tc3589x_device_init(tc3589x);
if (ret) {
dev_err(tc3589x->dev, "failed to add child devices\n");
- goto out_freeirq;
+ return ret;
}
return 0;
-
-out_freeirq:
- free_irq(tc3589x->i2c->irq, tc3589x);
-out_free:
- kfree(tc3589x);
- return ret;
}
-static int __devexit tc3589x_remove(struct i2c_client *client)
+static int tc3589x_remove(struct i2c_client *client)
{
struct tc3589x *tc3589x = i2c_get_clientdata(client);
mfd_remove_devices(tc3589x->dev);
- free_irq(tc3589x->i2c->irq, tc3589x);
-
- kfree(tc3589x);
-
return 0;
}
@@ -448,17 +490,26 @@ static int tc3589x_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, tc3589x_resume);
static const struct i2c_device_id tc3589x_id[] = {
- { "tc3589x", 24 },
+ { "tc35890", TC3589X_TC35890 },
+ { "tc35892", TC3589X_TC35892 },
+ { "tc35893", TC3589X_TC35893 },
+ { "tc35894", TC3589X_TC35894 },
+ { "tc35895", TC3589X_TC35895 },
+ { "tc35896", TC3589X_TC35896 },
+ { "tc3589x", TC3589X_UNKNOWN },
{ }
};
MODULE_DEVICE_TABLE(i2c, tc3589x_id);
static struct i2c_driver tc3589x_driver = {
- .driver.name = "tc3589x",
- .driver.owner = THIS_MODULE,
- .driver.pm = &tc3589x_dev_pm_ops,
+ .driver = {
+ .name = "tc3589x",
+ .owner = THIS_MODULE,
+ .pm = &tc3589x_dev_pm_ops,
+ .of_match_table = of_match_ptr(tc3589x_match),
+ },
.probe = tc3589x_probe,
- .remove = __devexit_p(tc3589x_remove),
+ .remove = tc3589x_remove,
.id_table = tc3589x_id,
};
diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c
index 413c891102f..591a331d8d8 100644
--- a/drivers/mfd/tc6387xb.c
+++ b/drivers/mfd/tc6387xb.c
@@ -48,7 +48,7 @@ static struct resource tc6387xb_mmc_resources[] = {
static int tc6387xb_suspend(struct platform_device *dev, pm_message_t state)
{
struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
- struct tc6387xb_platform_data *pdata = dev->dev.platform_data;
+ struct tc6387xb_platform_data *pdata = dev_get_platdata(&dev->dev);
if (pdata && pdata->suspend)
pdata->suspend(dev);
@@ -60,7 +60,7 @@ static int tc6387xb_suspend(struct platform_device *dev, pm_message_t state)
static int tc6387xb_resume(struct platform_device *dev)
{
struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
- struct tc6387xb_platform_data *pdata = dev->dev.platform_data;
+ struct tc6387xb_platform_data *pdata = dev_get_platdata(&dev->dev);
clk_enable(tc6387xb->clk32k);
if (pdata && pdata->resume)
@@ -126,7 +126,7 @@ static struct tmio_mmc_data tc6387xb_mmc_data = {
/*--------------------------------------------------------------------------*/
-static struct mfd_cell tc6387xb_cells[] = {
+static const struct mfd_cell tc6387xb_cells[] = {
[TC6387XB_CELL_MMC] = {
.name = "tmio-mmc",
.enable = tc6387xb_mmc_enable,
@@ -138,9 +138,9 @@ static struct mfd_cell tc6387xb_cells[] = {
},
};
-static int __devinit tc6387xb_probe(struct platform_device *dev)
+static int tc6387xb_probe(struct platform_device *dev)
{
- struct tc6387xb_platform_data *pdata = dev->dev.platform_data;
+ struct tc6387xb_platform_data *pdata = dev_get_platdata(&dev->dev);
struct resource *iomem, *rscr;
struct clk *clk32k;
struct tc6387xb *tc6387xb;
@@ -208,7 +208,7 @@ err_no_irq:
return ret;
}
-static int __devexit tc6387xb_remove(struct platform_device *dev)
+static int tc6387xb_remove(struct platform_device *dev)
{
struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
@@ -217,7 +217,6 @@ static int __devexit tc6387xb_remove(struct platform_device *dev)
release_resource(&tc6387xb->rscr);
clk_disable(tc6387xb->clk32k);
clk_put(tc6387xb->clk32k);
- platform_set_drvdata(dev, NULL);
kfree(tc6387xb);
return 0;
@@ -229,7 +228,7 @@ static struct platform_driver tc6387xb_platform_driver = {
.name = "tc6387xb",
},
.probe = tc6387xb_probe,
- .remove = __devexit_p(tc6387xb_remove),
+ .remove = tc6387xb_remove,
.suspend = tc6387xb_suspend,
.resume = tc6387xb_resume,
};
diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c
index dcab026fcbb..11c19e53855 100644
--- a/drivers/mfd/tc6393xb.c
+++ b/drivers/mfd/tc6393xb.c
@@ -137,7 +137,7 @@ static int tc6393xb_nand_enable(struct platform_device *nand)
return 0;
}
-static struct resource __devinitdata tc6393xb_nand_resources[] = {
+static struct resource tc6393xb_nand_resources[] = {
{
.start = 0x1000,
.end = 0x1007,
@@ -196,7 +196,7 @@ static const struct resource tc6393xb_ohci_resources[] = {
},
};
-static struct resource __devinitdata tc6393xb_fb_resources[] = {
+static struct resource tc6393xb_fb_resources[] = {
{
.start = 0x5000,
.end = 0x51ff,
@@ -382,7 +382,7 @@ static struct tmio_mmc_data tc6393xb_mmc_data = {
.set_clk_div = tc6393xb_mmc_clk_div,
};
-static struct mfd_cell __devinitdata tc6393xb_cells[] = {
+static struct mfd_cell tc6393xb_cells[] = {
[TC6393XB_CELL_NAND] = {
.name = "tmio-nand",
.enable = tc6393xb_nand_enable,
@@ -602,9 +602,9 @@ static void tc6393xb_detach_irq(struct platform_device *dev)
/*--------------------------------------------------------------------------*/
-static int __devinit tc6393xb_probe(struct platform_device *dev)
+static int tc6393xb_probe(struct platform_device *dev)
{
- struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
+ struct tc6393xb_platform_data *tcpd = dev_get_platdata(&dev->dev);
struct tc6393xb *tc6393xb;
struct resource *iomem, *rscr;
int ret, temp;
@@ -731,9 +731,9 @@ err_kzalloc:
return ret;
}
-static int __devexit tc6393xb_remove(struct platform_device *dev)
+static int tc6393xb_remove(struct platform_device *dev)
{
- struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
+ struct tc6393xb_platform_data *tcpd = dev_get_platdata(&dev->dev);
struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
int ret;
@@ -756,7 +756,6 @@ static int __devexit tc6393xb_remove(struct platform_device *dev)
clk_disable(tc6393xb->clk);
iounmap(tc6393xb->scr);
release_resource(&tc6393xb->rscr);
- platform_set_drvdata(dev, NULL);
clk_put(tc6393xb->clk);
kfree(tc6393xb);
@@ -766,7 +765,7 @@ static int __devexit tc6393xb_remove(struct platform_device *dev)
#ifdef CONFIG_PM
static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state)
{
- struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
+ struct tc6393xb_platform_data *tcpd = dev_get_platdata(&dev->dev);
struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
int i, ret;
@@ -789,7 +788,7 @@ static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state)
static int tc6393xb_resume(struct platform_device *dev)
{
- struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
+ struct tc6393xb_platform_data *tcpd = dev_get_platdata(&dev->dev);
struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
int ret;
int i;
@@ -831,7 +830,7 @@ static int tc6393xb_resume(struct platform_device *dev)
static struct platform_driver tc6393xb_driver = {
.probe = tc6393xb_probe,
- .remove = __devexit_p(tc6393xb_remove),
+ .remove = tc6393xb_remove,
.suspend = tc6393xb_suspend,
.resume = tc6393xb_resume,
diff --git a/drivers/mfd/ti-ssp.c b/drivers/mfd/ti-ssp.c
deleted file mode 100644
index 7c3675a74f9..00000000000
--- a/drivers/mfd/ti-ssp.c
+++ /dev/null
@@ -1,466 +0,0 @@
-/*
- * Sequencer Serial Port (SSP) driver for Texas Instruments' SoCs
- *
- * Copyright (C) 2010 Texas Instruments Inc
- *
- * 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/errno.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/wait.h>
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/spinlock.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/ti_ssp.h>
-
-/* Register Offsets */
-#define REG_REV 0x00
-#define REG_IOSEL_1 0x04
-#define REG_IOSEL_2 0x08
-#define REG_PREDIV 0x0c
-#define REG_INTR_ST 0x10
-#define REG_INTR_EN 0x14
-#define REG_TEST_CTRL 0x18
-
-/* Per port registers */
-#define PORT_CFG_2 0x00
-#define PORT_ADDR 0x04
-#define PORT_DATA 0x08
-#define PORT_CFG_1 0x0c
-#define PORT_STATE 0x10
-
-#define SSP_PORT_CONFIG_MASK (SSP_EARLY_DIN | SSP_DELAY_DOUT)
-#define SSP_PORT_CLKRATE_MASK 0x0f
-
-#define SSP_SEQRAM_WR_EN BIT(4)
-#define SSP_SEQRAM_RD_EN BIT(5)
-#define SSP_START BIT(15)
-#define SSP_BUSY BIT(10)
-#define SSP_PORT_ASL BIT(7)
-#define SSP_PORT_CFO1 BIT(6)
-
-#define SSP_PORT_SEQRAM_SIZE 32
-
-static const int ssp_port_base[] = {0x040, 0x080};
-static const int ssp_port_seqram[] = {0x100, 0x180};
-
-struct ti_ssp {
- struct resource *res;
- struct device *dev;
- void __iomem *regs;
- spinlock_t lock;
- struct clk *clk;
- int irq;
- wait_queue_head_t wqh;
-
- /*
- * Some of the iosel2 register bits always read-back as 0, we need to
- * remember these values so that we don't clobber previously set
- * values.
- */
- u32 iosel2;
-};
-
-static inline struct ti_ssp *dev_to_ssp(struct device *dev)
-{
- return dev_get_drvdata(dev->parent);
-}
-
-static inline int dev_to_port(struct device *dev)
-{
- return to_platform_device(dev)->id;
-}
-
-/* Register Access Helpers, rmw() functions need to run locked */
-static inline u32 ssp_read(struct ti_ssp *ssp, int reg)
-{
- return __raw_readl(ssp->regs + reg);
-}
-
-static inline void ssp_write(struct ti_ssp *ssp, int reg, u32 val)
-{
- __raw_writel(val, ssp->regs + reg);
-}
-
-static inline void ssp_rmw(struct ti_ssp *ssp, int reg, u32 mask, u32 bits)
-{
- ssp_write(ssp, reg, (ssp_read(ssp, reg) & ~mask) | bits);
-}
-
-static inline u32 ssp_port_read(struct ti_ssp *ssp, int port, int reg)
-{
- return ssp_read(ssp, ssp_port_base[port] + reg);
-}
-
-static inline void ssp_port_write(struct ti_ssp *ssp, int port, int reg,
- u32 val)
-{
- ssp_write(ssp, ssp_port_base[port] + reg, val);
-}
-
-static inline void ssp_port_rmw(struct ti_ssp *ssp, int port, int reg,
- u32 mask, u32 bits)
-{
- ssp_rmw(ssp, ssp_port_base[port] + reg, mask, bits);
-}
-
-static inline void ssp_port_clr_bits(struct ti_ssp *ssp, int port, int reg,
- u32 bits)
-{
- ssp_port_rmw(ssp, port, reg, bits, 0);
-}
-
-static inline void ssp_port_set_bits(struct ti_ssp *ssp, int port, int reg,
- u32 bits)
-{
- ssp_port_rmw(ssp, port, reg, 0, bits);
-}
-
-/* Called to setup port clock mode, caller must hold ssp->lock */
-static int __set_mode(struct ti_ssp *ssp, int port, int mode)
-{
- mode &= SSP_PORT_CONFIG_MASK;
- ssp_port_rmw(ssp, port, PORT_CFG_1, SSP_PORT_CONFIG_MASK, mode);
-
- return 0;
-}
-
-int ti_ssp_set_mode(struct device *dev, int mode)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev);
- int ret;
-
- spin_lock(&ssp->lock);
- ret = __set_mode(ssp, port, mode);
- spin_unlock(&ssp->lock);
-
- return ret;
-}
-EXPORT_SYMBOL(ti_ssp_set_mode);
-
-/* Called to setup iosel2, caller must hold ssp->lock */
-static void __set_iosel2(struct ti_ssp *ssp, u32 mask, u32 val)
-{
- ssp->iosel2 = (ssp->iosel2 & ~mask) | val;
- ssp_write(ssp, REG_IOSEL_2, ssp->iosel2);
-}
-
-/* Called to setup port iosel, caller must hold ssp->lock */
-static void __set_iosel(struct ti_ssp *ssp, int port, u32 iosel)
-{
- unsigned val, shift = port ? 16 : 0;
-
- /* IOSEL1 gets the least significant 16 bits */
- val = ssp_read(ssp, REG_IOSEL_1);
- val &= 0xffff << (port ? 0 : 16);
- val |= (iosel & 0xffff) << (port ? 16 : 0);
- ssp_write(ssp, REG_IOSEL_1, val);
-
- /* IOSEL2 gets the most significant 16 bits */
- val = (iosel >> 16) & 0x7;
- __set_iosel2(ssp, 0x7 << shift, val << shift);
-}
-
-int ti_ssp_set_iosel(struct device *dev, u32 iosel)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev);
-
- spin_lock(&ssp->lock);
- __set_iosel(ssp, port, iosel);
- spin_unlock(&ssp->lock);
-
- return 0;
-}
-EXPORT_SYMBOL(ti_ssp_set_iosel);
-
-int ti_ssp_load(struct device *dev, int offs, u32* prog, int len)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev);
- int i;
-
- if (len > SSP_PORT_SEQRAM_SIZE)
- return -ENOSPC;
-
- spin_lock(&ssp->lock);
-
- /* Enable SeqRAM access */
- ssp_port_set_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN);
-
- /* Copy code */
- for (i = 0; i < len; i++) {
- __raw_writel(prog[i], ssp->regs + offs + 4*i +
- ssp_port_seqram[port]);
- }
-
- /* Disable SeqRAM access */
- ssp_port_clr_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN);
-
- spin_unlock(&ssp->lock);
-
- return 0;
-}
-EXPORT_SYMBOL(ti_ssp_load);
-
-int ti_ssp_raw_read(struct device *dev)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev);
- int shift = port ? 27 : 11;
-
- return (ssp_read(ssp, REG_IOSEL_2) >> shift) & 0xf;
-}
-EXPORT_SYMBOL(ti_ssp_raw_read);
-
-int ti_ssp_raw_write(struct device *dev, u32 val)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev), shift;
-
- spin_lock(&ssp->lock);
-
- shift = port ? 22 : 6;
- val &= 0xf;
- __set_iosel2(ssp, 0xf << shift, val << shift);
-
- spin_unlock(&ssp->lock);
-
- return 0;
-}
-EXPORT_SYMBOL(ti_ssp_raw_write);
-
-static inline int __xfer_done(struct ti_ssp *ssp, int port)
-{
- return !(ssp_port_read(ssp, port, PORT_CFG_1) & SSP_BUSY);
-}
-
-int ti_ssp_run(struct device *dev, u32 pc, u32 input, u32 *output)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev);
- int ret;
-
- if (pc & ~(0x3f))
- return -EINVAL;
-
- /* Grab ssp->lock to serialize rmw on ssp registers */
- spin_lock(&ssp->lock);
-
- ssp_port_write(ssp, port, PORT_ADDR, input >> 16);
- ssp_port_write(ssp, port, PORT_DATA, input & 0xffff);
- ssp_port_rmw(ssp, port, PORT_CFG_1, 0x3f, pc);
-
- /* grab wait queue head lock to avoid race with the isr */
- spin_lock_irq(&ssp->wqh.lock);
-
- /* kick off sequence execution in hardware */
- ssp_port_set_bits(ssp, port, PORT_CFG_1, SSP_START);
-
- /* drop ssp lock; no register writes beyond this */
- spin_unlock(&ssp->lock);
-
- ret = wait_event_interruptible_locked_irq(ssp->wqh,
- __xfer_done(ssp, port));
- spin_unlock_irq(&ssp->wqh.lock);
-
- if (ret < 0)
- return ret;
-
- if (output) {
- *output = (ssp_port_read(ssp, port, PORT_ADDR) << 16) |
- (ssp_port_read(ssp, port, PORT_DATA) & 0xffff);
- }
-
- ret = ssp_port_read(ssp, port, PORT_STATE) & 0x3f; /* stop address */
-
- return ret;
-}
-EXPORT_SYMBOL(ti_ssp_run);
-
-static irqreturn_t ti_ssp_interrupt(int irq, void *dev_data)
-{
- struct ti_ssp *ssp = dev_data;
-
- spin_lock(&ssp->wqh.lock);
-
- ssp_write(ssp, REG_INTR_ST, 0x3);
- wake_up_locked(&ssp->wqh);
-
- spin_unlock(&ssp->wqh.lock);
-
- return IRQ_HANDLED;
-}
-
-static int __devinit ti_ssp_probe(struct platform_device *pdev)
-{
- static struct ti_ssp *ssp;
- const struct ti_ssp_data *pdata = pdev->dev.platform_data;
- int error = 0, prediv = 0xff, id;
- unsigned long sysclk;
- struct device *dev = &pdev->dev;
- struct mfd_cell cells[2];
-
- ssp = kzalloc(sizeof(*ssp), GFP_KERNEL);
- if (!ssp) {
- dev_err(dev, "cannot allocate device info\n");
- return -ENOMEM;
- }
-
- ssp->dev = dev;
- dev_set_drvdata(dev, ssp);
-
- ssp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!ssp->res) {
- error = -ENODEV;
- dev_err(dev, "cannot determine register area\n");
- goto error_res;
- }
-
- if (!request_mem_region(ssp->res->start, resource_size(ssp->res),
- pdev->name)) {
- error = -ENOMEM;
- dev_err(dev, "cannot claim register memory\n");
- goto error_res;
- }
-
- ssp->regs = ioremap(ssp->res->start, resource_size(ssp->res));
- if (!ssp->regs) {
- error = -ENOMEM;
- dev_err(dev, "cannot map register memory\n");
- goto error_map;
- }
-
- ssp->clk = clk_get(dev, NULL);
- if (IS_ERR(ssp->clk)) {
- error = PTR_ERR(ssp->clk);
- dev_err(dev, "cannot claim device clock\n");
- goto error_clk;
- }
-
- ssp->irq = platform_get_irq(pdev, 0);
- if (ssp->irq < 0) {
- error = -ENODEV;
- dev_err(dev, "unknown irq\n");
- goto error_irq;
- }
-
- error = request_threaded_irq(ssp->irq, NULL, ti_ssp_interrupt, 0,
- dev_name(dev), ssp);
- if (error < 0) {
- dev_err(dev, "cannot acquire irq\n");
- goto error_irq;
- }
-
- spin_lock_init(&ssp->lock);
- init_waitqueue_head(&ssp->wqh);
-
- /* Power on and initialize SSP */
- error = clk_enable(ssp->clk);
- if (error) {
- dev_err(dev, "cannot enable device clock\n");
- goto error_enable;
- }
-
- /* Reset registers to a sensible known state */
- ssp_write(ssp, REG_IOSEL_1, 0);
- ssp_write(ssp, REG_IOSEL_2, 0);
- ssp_write(ssp, REG_INTR_EN, 0x3);
- ssp_write(ssp, REG_INTR_ST, 0x3);
- ssp_write(ssp, REG_TEST_CTRL, 0);
- ssp_port_write(ssp, 0, PORT_CFG_1, SSP_PORT_ASL);
- ssp_port_write(ssp, 1, PORT_CFG_1, SSP_PORT_ASL);
- ssp_port_write(ssp, 0, PORT_CFG_2, SSP_PORT_CFO1);
- ssp_port_write(ssp, 1, PORT_CFG_2, SSP_PORT_CFO1);
-
- sysclk = clk_get_rate(ssp->clk);
- if (pdata && pdata->out_clock)
- prediv = (sysclk / pdata->out_clock) - 1;
- prediv = clamp(prediv, 0, 0xff);
- ssp_rmw(ssp, REG_PREDIV, 0xff, prediv);
-
- memset(cells, 0, sizeof(cells));
- for (id = 0; id < 2; id++) {
- const struct ti_ssp_dev_data *data = &pdata->dev_data[id];
-
- cells[id].id = id;
- cells[id].name = data->dev_name;
- cells[id].platform_data = data->pdata;
- cells[id].data_size = data->pdata_size;
- }
-
- error = mfd_add_devices(dev, 0, cells, 2, NULL, 0, NULL);
- if (error < 0) {
- dev_err(dev, "cannot add mfd cells\n");
- goto error_enable;
- }
-
- return 0;
-
-error_enable:
- free_irq(ssp->irq, ssp);
-error_irq:
- clk_put(ssp->clk);
-error_clk:
- iounmap(ssp->regs);
-error_map:
- release_mem_region(ssp->res->start, resource_size(ssp->res));
-error_res:
- kfree(ssp);
- return error;
-}
-
-static int __devexit ti_ssp_remove(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct ti_ssp *ssp = dev_get_drvdata(dev);
-
- mfd_remove_devices(dev);
- clk_disable(ssp->clk);
- free_irq(ssp->irq, ssp);
- clk_put(ssp->clk);
- iounmap(ssp->regs);
- release_mem_region(ssp->res->start, resource_size(ssp->res));
- kfree(ssp);
- dev_set_drvdata(dev, NULL);
- return 0;
-}
-
-static struct platform_driver ti_ssp_driver = {
- .probe = ti_ssp_probe,
- .remove = __devexit_p(ti_ssp_remove),
- .driver = {
- .name = "ti-ssp",
- .owner = THIS_MODULE,
- }
-};
-
-module_platform_driver(ti_ssp_driver);
-
-MODULE_DESCRIPTION("Sequencer Serial Port (SSP) Driver");
-MODULE_AUTHOR("Cyril Chemparathy");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:ti-ssp");
diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
new file mode 100644
index 00000000000..dd4bf581622
--- /dev/null
+++ b/drivers/mfd/ti_am335x_tscadc.c
@@ -0,0 +1,379 @@
+/*
+ * TI Touch Screen / ADC MFD driver
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/mfd/core.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/sched.h>
+
+#include <linux/mfd/ti_am335x_tscadc.h>
+
+static unsigned int tscadc_readl(struct ti_tscadc_dev *tsadc, unsigned int reg)
+{
+ unsigned int val;
+
+ regmap_read(tsadc->regmap_tscadc, reg, &val);
+ return val;
+}
+
+static void tscadc_writel(struct ti_tscadc_dev *tsadc, unsigned int reg,
+ unsigned int val)
+{
+ regmap_write(tsadc->regmap_tscadc, reg, val);
+}
+
+static const struct regmap_config tscadc_regmap_config = {
+ .name = "ti_tscadc",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+};
+
+void am335x_tsc_se_set_cache(struct ti_tscadc_dev *tsadc, u32 val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tsadc->reg_lock, flags);
+ tsadc->reg_se_cache = val;
+ if (tsadc->adc_waiting)
+ wake_up(&tsadc->reg_se_wait);
+ else if (!tsadc->adc_in_use)
+ tscadc_writel(tsadc, REG_SE, val);
+
+ spin_unlock_irqrestore(&tsadc->reg_lock, flags);
+}
+EXPORT_SYMBOL_GPL(am335x_tsc_se_set_cache);
+
+static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc)
+{
+ DEFINE_WAIT(wait);
+ u32 reg;
+
+ /*
+ * disable TSC steps so it does not run while the ADC is using it. If
+ * write 0 while it is running (it just started or was already running)
+ * then it completes all steps that were enabled and stops then.
+ */
+ tscadc_writel(tsadc, REG_SE, 0);
+ reg = tscadc_readl(tsadc, REG_ADCFSM);
+ if (reg & SEQ_STATUS) {
+ tsadc->adc_waiting = true;
+ prepare_to_wait(&tsadc->reg_se_wait, &wait,
+ TASK_UNINTERRUPTIBLE);
+ spin_unlock_irq(&tsadc->reg_lock);
+
+ schedule();
+
+ spin_lock_irq(&tsadc->reg_lock);
+ finish_wait(&tsadc->reg_se_wait, &wait);
+
+ reg = tscadc_readl(tsadc, REG_ADCFSM);
+ WARN_ON(reg & SEQ_STATUS);
+ tsadc->adc_waiting = false;
+ }
+ tsadc->adc_in_use = true;
+}
+
+void am335x_tsc_se_set_once(struct ti_tscadc_dev *tsadc, u32 val)
+{
+ spin_lock_irq(&tsadc->reg_lock);
+ am335x_tscadc_need_adc(tsadc);
+
+ tscadc_writel(tsadc, REG_SE, val);
+ spin_unlock_irq(&tsadc->reg_lock);
+}
+EXPORT_SYMBOL_GPL(am335x_tsc_se_set_once);
+
+void am335x_tsc_se_adc_done(struct ti_tscadc_dev *tsadc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tsadc->reg_lock, flags);
+ tsadc->adc_in_use = false;
+ tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache);
+ spin_unlock_irqrestore(&tsadc->reg_lock, flags);
+}
+EXPORT_SYMBOL_GPL(am335x_tsc_se_adc_done);
+
+void am335x_tsc_se_clr(struct ti_tscadc_dev *tsadc, u32 val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tsadc->reg_lock, flags);
+ tsadc->reg_se_cache &= ~val;
+ tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache);
+ spin_unlock_irqrestore(&tsadc->reg_lock, flags);
+}
+EXPORT_SYMBOL_GPL(am335x_tsc_se_clr);
+
+static void tscadc_idle_config(struct ti_tscadc_dev *config)
+{
+ unsigned int idleconfig;
+
+ idleconfig = STEPCONFIG_YNN | STEPCONFIG_INM_ADCREFM |
+ STEPCONFIG_INP_ADCREFM | STEPCONFIG_YPN;
+
+ tscadc_writel(config, REG_IDLECONFIG, idleconfig);
+}
+
+static int ti_tscadc_probe(struct platform_device *pdev)
+{
+ struct ti_tscadc_dev *tscadc;
+ struct resource *res;
+ struct clk *clk;
+ struct device_node *node = pdev->dev.of_node;
+ struct mfd_cell *cell;
+ struct property *prop;
+ const __be32 *cur;
+ u32 val;
+ int err, ctrl;
+ int clock_rate;
+ int tsc_wires = 0, adc_channels = 0, total_channels;
+ int readouts = 0;
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev, "Could not find valid DT data.\n");
+ return -EINVAL;
+ }
+
+ node = of_get_child_by_name(pdev->dev.of_node, "tsc");
+ of_property_read_u32(node, "ti,wires", &tsc_wires);
+ of_property_read_u32(node, "ti,coordiante-readouts", &readouts);
+
+ node = of_get_child_by_name(pdev->dev.of_node, "adc");
+ of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
+ adc_channels++;
+ if (val > 7) {
+ dev_err(&pdev->dev, " PIN numbers are 0..7 (not %d)\n",
+ val);
+ return -EINVAL;
+ }
+ }
+ total_channels = tsc_wires + adc_channels;
+ if (total_channels > 8) {
+ dev_err(&pdev->dev, "Number of i/p channels more than 8\n");
+ return -EINVAL;
+ }
+ if (total_channels == 0) {
+ dev_err(&pdev->dev, "Need atleast one channel.\n");
+ return -EINVAL;
+ }
+
+ if (readouts * 2 + 2 + adc_channels > 16) {
+ dev_err(&pdev->dev, "Too many step configurations requested\n");
+ return -EINVAL;
+ }
+
+ /* Allocate memory for device */
+ tscadc = devm_kzalloc(&pdev->dev,
+ sizeof(struct ti_tscadc_dev), GFP_KERNEL);
+ if (!tscadc) {
+ dev_err(&pdev->dev, "failed to allocate memory.\n");
+ return -ENOMEM;
+ }
+ tscadc->dev = &pdev->dev;
+
+ err = platform_get_irq(pdev, 0);
+ if (err < 0) {
+ dev_err(&pdev->dev, "no irq ID is specified.\n");
+ goto ret;
+ } else
+ tscadc->irq = err;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(tscadc->tscadc_base))
+ return PTR_ERR(tscadc->tscadc_base);
+
+ tscadc->regmap_tscadc = devm_regmap_init_mmio(&pdev->dev,
+ tscadc->tscadc_base, &tscadc_regmap_config);
+ if (IS_ERR(tscadc->regmap_tscadc)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ err = PTR_ERR(tscadc->regmap_tscadc);
+ goto ret;
+ }
+
+ spin_lock_init(&tscadc->reg_lock);
+ init_waitqueue_head(&tscadc->reg_se_wait);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+
+ /*
+ * The TSC_ADC_Subsystem has 2 clock domains
+ * OCP_CLK and ADC_CLK.
+ * The ADC clock is expected to run at target of 3MHz,
+ * and expected to capture 12-bit data at a rate of 200 KSPS.
+ * The TSC_ADC_SS controller design assumes the OCP clock is
+ * at least 6x faster than the ADC clock.
+ */
+ clk = clk_get(&pdev->dev, "adc_tsc_fck");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed to get TSC fck\n");
+ err = PTR_ERR(clk);
+ goto err_disable_clk;
+ }
+ clock_rate = clk_get_rate(clk);
+ clk_put(clk);
+ tscadc->clk_div = clock_rate / ADC_CLK;
+
+ /* TSCADC_CLKDIV needs to be configured to the value minus 1 */
+ tscadc->clk_div--;
+ tscadc_writel(tscadc, REG_CLKDIV, tscadc->clk_div);
+
+ /* Set the control register bits */
+ ctrl = CNTRLREG_STEPCONFIGWRT |
+ CNTRLREG_STEPID;
+ if (tsc_wires > 0)
+ ctrl |= CNTRLREG_4WIRE | CNTRLREG_TSCENB;
+ tscadc_writel(tscadc, REG_CTRL, ctrl);
+
+ /* Set register bits for Idle Config Mode */
+ if (tsc_wires > 0)
+ tscadc_idle_config(tscadc);
+
+ /* Enable the TSC module enable bit */
+ ctrl = tscadc_readl(tscadc, REG_CTRL);
+ ctrl |= CNTRLREG_TSCSSENB;
+ tscadc_writel(tscadc, REG_CTRL, ctrl);
+
+ tscadc->used_cells = 0;
+ tscadc->tsc_cell = -1;
+ tscadc->adc_cell = -1;
+
+ /* TSC Cell */
+ if (tsc_wires > 0) {
+ tscadc->tsc_cell = tscadc->used_cells;
+ cell = &tscadc->cells[tscadc->used_cells++];
+ cell->name = "TI-am335x-tsc";
+ cell->of_compatible = "ti,am3359-tsc";
+ cell->platform_data = &tscadc;
+ cell->pdata_size = sizeof(tscadc);
+ }
+
+ /* ADC Cell */
+ if (adc_channels > 0) {
+ tscadc->adc_cell = tscadc->used_cells;
+ cell = &tscadc->cells[tscadc->used_cells++];
+ cell->name = "TI-am335x-adc";
+ cell->of_compatible = "ti,am3359-adc";
+ cell->platform_data = &tscadc;
+ cell->pdata_size = sizeof(tscadc);
+ }
+
+ err = mfd_add_devices(&pdev->dev, pdev->id, tscadc->cells,
+ tscadc->used_cells, NULL, 0, NULL);
+ if (err < 0)
+ goto err_disable_clk;
+
+ device_init_wakeup(&pdev->dev, true);
+ platform_set_drvdata(pdev, tscadc);
+ return 0;
+
+err_disable_clk:
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ret:
+ return err;
+}
+
+static int ti_tscadc_remove(struct platform_device *pdev)
+{
+ struct ti_tscadc_dev *tscadc = platform_get_drvdata(pdev);
+
+ tscadc_writel(tscadc, REG_SE, 0x00);
+
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ mfd_remove_devices(tscadc->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tscadc_suspend(struct device *dev)
+{
+ struct ti_tscadc_dev *tscadc_dev = dev_get_drvdata(dev);
+
+ tscadc_writel(tscadc_dev, REG_SE, 0x00);
+ pm_runtime_put_sync(dev);
+
+ return 0;
+}
+
+static int tscadc_resume(struct device *dev)
+{
+ struct ti_tscadc_dev *tscadc_dev = dev_get_drvdata(dev);
+ unsigned int restore, ctrl;
+
+ pm_runtime_get_sync(dev);
+
+ /* context restore */
+ ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_STEPID;
+ if (tscadc_dev->tsc_cell != -1)
+ ctrl |= CNTRLREG_TSCENB | CNTRLREG_4WIRE;
+ tscadc_writel(tscadc_dev, REG_CTRL, ctrl);
+
+ if (tscadc_dev->tsc_cell != -1)
+ tscadc_idle_config(tscadc_dev);
+ restore = tscadc_readl(tscadc_dev, REG_CTRL);
+ tscadc_writel(tscadc_dev, REG_CTRL,
+ (restore | CNTRLREG_TSCSSENB));
+
+ tscadc_writel(tscadc_dev, REG_CLKDIV, tscadc_dev->clk_div);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tscadc_pm_ops = {
+ .suspend = tscadc_suspend,
+ .resume = tscadc_resume,
+};
+#define TSCADC_PM_OPS (&tscadc_pm_ops)
+#else
+#define TSCADC_PM_OPS NULL
+#endif
+
+static const struct of_device_id ti_tscadc_dt_ids[] = {
+ { .compatible = "ti,am3359-tscadc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ti_tscadc_dt_ids);
+
+static struct platform_driver ti_tscadc_driver = {
+ .driver = {
+ .name = "ti_am3359-tscadc",
+ .owner = THIS_MODULE,
+ .pm = TSCADC_PM_OPS,
+ .of_match_table = ti_tscadc_dt_ids,
+ },
+ .probe = ti_tscadc_probe,
+ .remove = ti_tscadc_remove,
+
+};
+
+module_platform_driver(ti_tscadc_driver);
+
+MODULE_DESCRIPTION("TI touchscreen / ADC MFD controller driver");
+MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c
index cccc626c83c..6ce36d6970a 100644
--- a/drivers/mfd/timberdale.c
+++ b/drivers/mfd/timberdale.c
@@ -75,13 +75,13 @@ static struct i2c_board_info timberdale_i2c_board_info[] = {
},
};
-static __devinitdata struct xiic_i2c_platform_data
+static struct xiic_i2c_platform_data
timberdale_xiic_platform_data = {
.devices = timberdale_i2c_board_info,
.num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
};
-static __devinitdata struct ocores_i2c_platform_data
+static struct ocores_i2c_platform_data
timberdale_ocores_platform_data = {
.reg_shift = 2,
.clock_khz = 62500,
@@ -89,7 +89,7 @@ timberdale_ocores_platform_data = {
.num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
};
-static const __devinitconst struct resource timberdale_xiic_resources[] = {
+static const struct resource timberdale_xiic_resources[] = {
{
.start = XIICOFFSET,
.end = XIICEND,
@@ -102,7 +102,7 @@ static const __devinitconst struct resource timberdale_xiic_resources[] = {
},
};
-static const __devinitconst struct resource timberdale_ocores_resources[] = {
+static const struct resource timberdale_ocores_resources[] = {
{
.start = OCORESOFFSET,
.end = OCORESEND,
@@ -115,11 +115,11 @@ static const __devinitconst struct resource timberdale_ocores_resources[] = {
},
};
-const struct max7301_platform_data timberdale_max7301_platform_data = {
+static const struct max7301_platform_data timberdale_max7301_platform_data = {
.base = 200
};
-const struct mc33880_platform_data timberdale_mc33880_platform_data = {
+static const struct mc33880_platform_data timberdale_mc33880_platform_data = {
.base = 100
};
@@ -143,15 +143,14 @@ static struct spi_board_info timberdale_spi_8bit_board_info[] = {
},
};
-static __devinitdata struct xspi_platform_data timberdale_xspi_platform_data = {
+static struct xspi_platform_data timberdale_xspi_platform_data = {
.num_chipselect = 3,
- .little_endian = true,
/* bits per word and devices will be filled in runtime depending
* on the HW config
*/
};
-static const __devinitconst struct resource timberdale_spi_resources[] = {
+static const struct resource timberdale_spi_resources[] = {
{
.start = SPIOFFSET,
.end = SPIEND,
@@ -164,13 +163,13 @@ static const __devinitconst struct resource timberdale_spi_resources[] = {
},
};
-static __devinitdata struct ks8842_platform_data
+static struct ks8842_platform_data
timberdale_ks8842_platform_data = {
.rx_dma_channel = DMA_ETH_RX,
.tx_dma_channel = DMA_ETH_TX
};
-static const __devinitconst struct resource timberdale_eth_resources[] = {
+static const struct resource timberdale_eth_resources[] = {
{
.start = ETHOFFSET,
.end = ETHEND,
@@ -183,14 +182,14 @@ static const __devinitconst struct resource timberdale_eth_resources[] = {
},
};
-static __devinitdata struct timbgpio_platform_data
+static struct timbgpio_platform_data
timberdale_gpio_platform_data = {
.gpio_base = 0,
.nr_pins = GPIO_NR_PINS,
.irq_base = 200,
};
-static const __devinitconst struct resource timberdale_gpio_resources[] = {
+static const struct resource timberdale_gpio_resources[] = {
{
.start = GPIOOFFSET,
.end = GPIOEND,
@@ -203,7 +202,7 @@ static const __devinitconst struct resource timberdale_gpio_resources[] = {
},
};
-static const __devinitconst struct resource timberdale_mlogicore_resources[] = {
+static const struct resource timberdale_mlogicore_resources[] = {
{
.start = MLCOREOFFSET,
.end = MLCOREEND,
@@ -221,7 +220,7 @@ static const __devinitconst struct resource timberdale_mlogicore_resources[] = {
},
};
-static const __devinitconst struct resource timberdale_uart_resources[] = {
+static const struct resource timberdale_uart_resources[] = {
{
.start = UARTOFFSET,
.end = UARTEND,
@@ -234,7 +233,7 @@ static const __devinitconst struct resource timberdale_uart_resources[] = {
},
};
-static const __devinitconst struct resource timberdale_uartlite_resources[] = {
+static const struct resource timberdale_uartlite_resources[] = {
{
.start = UARTLITEOFFSET,
.end = UARTLITEEND,
@@ -247,13 +246,13 @@ static const __devinitconst struct resource timberdale_uartlite_resources[] = {
},
};
-static __devinitdata struct i2c_board_info timberdale_adv7180_i2c_board_info = {
+static struct i2c_board_info timberdale_adv7180_i2c_board_info = {
/* Requires jumper JP9 to be off */
I2C_BOARD_INFO("adv7180", 0x42 >> 1),
.irq = IRQ_TIMBERDALE_ADV7180
};
-static __devinitdata struct timb_video_platform_data
+static struct timb_video_platform_data
timberdale_video_platform_data = {
.dma_channel = DMA_VIDEO_RX,
.i2c_adapter = 0,
@@ -262,7 +261,7 @@ static __devinitdata struct timb_video_platform_data
}
};
-static const __devinitconst struct resource
+static const struct resource
timberdale_radio_resources[] = {
{
.start = RDSOFFSET,
@@ -276,22 +275,22 @@ timberdale_radio_resources[] = {
},
};
-static __devinitdata struct i2c_board_info timberdale_tef6868_i2c_board_info = {
+static struct i2c_board_info timberdale_tef6868_i2c_board_info = {
I2C_BOARD_INFO("tef6862", 0x60)
};
-static __devinitdata struct i2c_board_info timberdale_saa7706_i2c_board_info = {
+static struct i2c_board_info timberdale_saa7706_i2c_board_info = {
I2C_BOARD_INFO("saa7706h", 0x1C)
};
-static __devinitdata struct timb_radio_platform_data
+static struct timb_radio_platform_data
timberdale_radio_platform_data = {
.i2c_adapter = 0,
.tuner = &timberdale_tef6868_i2c_board_info,
.dsp = &timberdale_saa7706_i2c_board_info
};
-static const __devinitconst struct resource timberdale_video_resources[] = {
+static const struct resource timberdale_video_resources[] = {
{
.start = LOGIWOFFSET,
.end = LOGIWEND,
@@ -303,7 +302,7 @@ static const __devinitconst struct resource timberdale_video_resources[] = {
*/
};
-static __devinitdata struct timb_dma_platform_data timb_dma_platform_data = {
+static struct timb_dma_platform_data timb_dma_platform_data = {
.nr_channels = 10,
.channels = {
{
@@ -362,7 +361,7 @@ static __devinitdata struct timb_dma_platform_data timb_dma_platform_data = {
}
};
-static const __devinitconst struct resource timberdale_dma_resources[] = {
+static const struct resource timberdale_dma_resources[] = {
{
.start = DMAOFFSET,
.end = DMAEND,
@@ -375,7 +374,7 @@ static const __devinitconst struct resource timberdale_dma_resources[] = {
},
};
-static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = {
+static const struct mfd_cell timberdale_cells_bar0_cfg0[] = {
{
.name = "timb-dma",
.num_resources = ARRAY_SIZE(timberdale_dma_resources),
@@ -432,7 +431,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = {
},
};
-static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
+static const struct mfd_cell timberdale_cells_bar0_cfg1[] = {
{
.name = "timb-dma",
.num_resources = ARRAY_SIZE(timberdale_dma_resources),
@@ -499,7 +498,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
},
};
-static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = {
+static const struct mfd_cell timberdale_cells_bar0_cfg2[] = {
{
.name = "timb-dma",
.num_resources = ARRAY_SIZE(timberdale_dma_resources),
@@ -549,7 +548,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = {
},
};
-static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = {
+static const struct mfd_cell timberdale_cells_bar0_cfg3[] = {
{
.name = "timb-dma",
.num_resources = ARRAY_SIZE(timberdale_dma_resources),
@@ -606,7 +605,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = {
},
};
-static const __devinitconst struct resource timberdale_sdhc_resources[] = {
+static const struct resource timberdale_sdhc_resources[] = {
/* located in bar 1 and bar 2 */
{
.start = SDHC0OFFSET,
@@ -620,7 +619,7 @@ static const __devinitconst struct resource timberdale_sdhc_resources[] = {
},
};
-static __devinitdata struct mfd_cell timberdale_cells_bar1[] = {
+static const struct mfd_cell timberdale_cells_bar1[] = {
{
.name = "sdhci",
.num_resources = ARRAY_SIZE(timberdale_sdhc_resources),
@@ -628,7 +627,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar1[] = {
},
};
-static __devinitdata struct mfd_cell timberdale_cells_bar2[] = {
+static const struct mfd_cell timberdale_cells_bar2[] = {
{
.name = "sdhci",
.num_resources = ARRAY_SIZE(timberdale_sdhc_resources),
@@ -650,7 +649,7 @@ static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL);
/*--------------------------------------------------------------------------*/
-static int __devinit timb_probe(struct pci_dev *dev,
+static int timb_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
struct timberdale_device *priv;
@@ -679,7 +678,7 @@ static int __devinit timb_probe(struct pci_dev *dev,
priv->ctl_mapbase = mapbase + CHIPCTLOFFSET;
if (!request_mem_region(priv->ctl_mapbase, CHIPCTLSIZE, "timb-ctl")) {
dev_err(&dev->dev, "Failed to request ctl mem\n");
- goto err_request;
+ goto err_start;
}
priv->ctl_membase = ioremap(priv->ctl_mapbase, CHIPCTLSIZE);
@@ -716,7 +715,7 @@ static int __devinit timb_probe(struct pci_dev *dev,
for (i = 0; i < TIMBERDALE_NR_IRQS; i++)
msix_entries[i].entry = i;
- err = pci_enable_msix(dev, msix_entries, TIMBERDALE_NR_IRQS);
+ err = pci_enable_msix_exact(dev, msix_entries, TIMBERDALE_NR_IRQS);
if (err) {
dev_err(&dev->dev,
"MSI-X init failed: %d, expected entries: %d\n",
@@ -782,7 +781,6 @@ static int __devinit timb_probe(struct pci_dev *dev,
priv->fw.major, priv->fw.minor, ip_setup);
err = -ENODEV;
goto err_mfd;
- break;
}
if (err) {
@@ -830,17 +828,14 @@ err_config:
iounmap(priv->ctl_membase);
err_ioremap:
release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
-err_request:
- pci_set_drvdata(dev, NULL);
err_start:
pci_disable_device(dev);
err_enable:
kfree(priv);
- pci_set_drvdata(dev, NULL);
return -ENODEV;
}
-static void __devexit timb_remove(struct pci_dev *dev)
+static void timb_remove(struct pci_dev *dev)
{
struct timberdale_device *priv = pci_get_drvdata(dev);
@@ -853,11 +848,10 @@ static void __devexit timb_remove(struct pci_dev *dev)
pci_disable_msix(dev);
pci_disable_device(dev);
- pci_set_drvdata(dev, NULL);
kfree(priv);
}
-static DEFINE_PCI_DEVICE_TABLE(timberdale_pci_tbl) = {
+static const struct pci_device_id timberdale_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) },
{ 0 }
};
@@ -867,37 +861,10 @@ static struct pci_driver timberdale_pci_driver = {
.name = DRIVER_NAME,
.id_table = timberdale_pci_tbl,
.probe = timb_probe,
- .remove = __devexit_p(timb_remove),
+ .remove = timb_remove,
};
-static int __init timberdale_init(void)
-{
- int err;
-
- err = pci_register_driver(&timberdale_pci_driver);
- if (err < 0) {
- printk(KERN_ERR
- "Failed to register PCI driver for %s device.\n",
- timberdale_pci_driver.name);
- return -ENODEV;
- }
-
- printk(KERN_INFO "Driver for %s has been successfully registered.\n",
- timberdale_pci_driver.name);
-
- return 0;
-}
-
-static void __exit timberdale_exit(void)
-{
- pci_unregister_driver(&timberdale_pci_driver);
-
- printk(KERN_INFO "Driver for %s has been successfully unregistered.\n",
- timberdale_pci_driver.name);
-}
-
-module_init(timberdale_init);
-module_exit(timberdale_exit);
+module_pci_driver(timberdale_pci_driver);
MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/mfd/tps6105x.c b/drivers/mfd/tps6105x.c
index 14051bdc714..b5dfa6e4e69 100644
--- a/drivers/mfd/tps6105x.c
+++ b/drivers/mfd/tps6105x.c
@@ -86,7 +86,7 @@ fail:
}
EXPORT_SYMBOL(tps6105x_mask_and_set);
-static int __devinit tps6105x_startup(struct tps6105x *tps6105x)
+static int tps6105x_startup(struct tps6105x *tps6105x)
{
int ret;
u8 regval;
@@ -133,7 +133,7 @@ static struct mfd_cell tps6105x_cells[] = {
},
};
-static int __devinit tps6105x_probe(struct i2c_client *client,
+static int tps6105x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tps6105x *tps6105x;
@@ -147,7 +147,7 @@ static int __devinit tps6105x_probe(struct i2c_client *client,
i2c_set_clientdata(client, tps6105x);
tps6105x->client = client;
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
tps6105x->pdata = pdata;
mutex_init(&tps6105x->lock);
@@ -199,7 +199,7 @@ fail:
return ret;
}
-static int __devexit tps6105x_remove(struct i2c_client *client)
+static int tps6105x_remove(struct i2c_client *client)
{
struct tps6105x *tps6105x = i2c_get_clientdata(client);
@@ -226,7 +226,7 @@ static struct i2c_driver tps6105x_driver = {
.name = "tps6105x",
},
.probe = tps6105x_probe,
- .remove = __devexit_p(tps6105x_remove),
+ .remove = tps6105x_remove,
.id_table = tps6105x_id,
};
diff --git a/drivers/mfd/tps65010.c b/drivers/mfd/tps65010.c
index da2691f22e1..743fb524fc8 100644
--- a/drivers/mfd/tps65010.c
+++ b/drivers/mfd/tps65010.c
@@ -242,8 +242,8 @@ static int dbg_show(struct seq_file *s, void *_)
seq_printf(s, "mask2 %s\n", buf);
/* ignore ackint2 */
- schedule_delayed_work(&tps->work, POWER_POLL_DELAY);
-
+ queue_delayed_work(system_power_efficient_wq, &tps->work,
+ POWER_POLL_DELAY);
/* VMAIN voltage, enable lowpower, etc */
value = i2c_smbus_read_byte_data(tps->client, TPS_VDCDC1);
@@ -400,7 +400,8 @@ static void tps65010_interrupt(struct tps65010 *tps)
&& (tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC)))
poll = 1;
if (poll)
- schedule_delayed_work(&tps->work, POWER_POLL_DELAY);
+ queue_delayed_work(system_power_efficient_wq, &tps->work,
+ POWER_POLL_DELAY);
/* also potentially gpio-in rise or fall */
}
@@ -448,7 +449,7 @@ static irqreturn_t tps65010_irq(int irq, void *_tps)
disable_irq_nosync(irq);
set_bit(FLAG_IRQ_ENABLE, &tps->flags);
- schedule_delayed_work(&tps->work, 0);
+ queue_delayed_work(system_power_efficient_wq, &tps->work, 0);
return IRQ_HANDLED;
}
@@ -517,7 +518,7 @@ static struct tps65010 *the_tps;
static int __exit tps65010_remove(struct i2c_client *client)
{
struct tps65010 *tps = i2c_get_clientdata(client);
- struct tps65010_board *board = client->dev.platform_data;
+ struct tps65010_board *board = dev_get_platdata(&client->dev);
if (board && board->teardown) {
int status = board->teardown(client, board->context);
@@ -529,7 +530,6 @@ static int __exit tps65010_remove(struct i2c_client *client)
free_irq(client->irq, tps);
cancel_delayed_work_sync(&tps->work);
debugfs_remove(tps->file);
- kfree(tps);
the_tps = NULL;
return 0;
}
@@ -539,7 +539,7 @@ static int tps65010_probe(struct i2c_client *client,
{
struct tps65010 *tps;
int status;
- struct tps65010_board *board = client->dev.platform_data;
+ struct tps65010_board *board = dev_get_platdata(&client->dev);
if (the_tps) {
dev_dbg(&client->dev, "only one tps6501x chip allowed\n");
@@ -549,7 +549,7 @@ static int tps65010_probe(struct i2c_client *client,
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EINVAL;
- tps = kzalloc(sizeof *tps, GFP_KERNEL);
+ tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
if (!tps)
return -ENOMEM;
@@ -567,7 +567,7 @@ static int tps65010_probe(struct i2c_client *client,
if (status < 0) {
dev_dbg(&client->dev, "can't get IRQ %d, err %d\n",
client->irq, status);
- goto fail1;
+ return status;
}
/* annoying race here, ideally we'd have an option
* to claim the irq now and enable it later.
@@ -667,9 +667,6 @@ static int tps65010_probe(struct i2c_client *client,
}
return 0;
-fail1:
- kfree(tps);
- return status;
}
static const struct i2c_device_id tps65010_id[] = {
@@ -718,7 +715,8 @@ int tps65010_set_vbus_draw(unsigned mA)
&& test_and_set_bit(
FLAG_VBUS_CHANGED, &the_tps->flags)) {
/* gadget drivers call this in_irq() */
- schedule_delayed_work(&the_tps->work, 0);
+ queue_delayed_work(system_power_efficient_wq, &the_tps->work,
+ 0);
}
local_irq_restore(flags);
diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c
index 1b203499c74..a2e1990c9de 100644
--- a/drivers/mfd/tps6507x.c
+++ b/drivers/mfd/tps6507x.c
@@ -19,10 +19,12 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps6507x.h>
-static struct mfd_cell tps6507x_devs[] = {
+static const struct mfd_cell tps6507x_devs[] = {
{
.name = "tps6507x-pmic",
},
@@ -86,9 +88,9 @@ static int tps6507x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct tps6507x_dev *tps6507x;
- int ret = 0;
- tps6507x = kzalloc(sizeof(struct tps6507x_dev), GFP_KERNEL);
+ tps6507x = devm_kzalloc(&i2c->dev, sizeof(struct tps6507x_dev),
+ GFP_KERNEL);
if (tps6507x == NULL)
return -ENOMEM;
@@ -98,19 +100,8 @@ static int tps6507x_i2c_probe(struct i2c_client *i2c,
tps6507x->read_dev = tps6507x_i2c_read_device;
tps6507x->write_dev = tps6507x_i2c_write_device;
- ret = mfd_add_devices(tps6507x->dev, -1,
- tps6507x_devs, ARRAY_SIZE(tps6507x_devs),
- NULL, 0, NULL);
-
- if (ret < 0)
- goto err;
-
- return ret;
-
-err:
- mfd_remove_devices(tps6507x->dev);
- kfree(tps6507x);
- return ret;
+ return mfd_add_devices(tps6507x->dev, -1, tps6507x_devs,
+ ARRAY_SIZE(tps6507x_devs), NULL, 0, NULL);
}
static int tps6507x_i2c_remove(struct i2c_client *i2c)
@@ -118,8 +109,6 @@ static int tps6507x_i2c_remove(struct i2c_client *i2c)
struct tps6507x_dev *tps6507x = i2c_get_clientdata(i2c);
mfd_remove_devices(tps6507x->dev);
- kfree(tps6507x);
-
return 0;
}
@@ -129,11 +118,19 @@ static const struct i2c_device_id tps6507x_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id);
+#ifdef CONFIG_OF
+static const struct of_device_id tps6507x_of_match[] = {
+ {.compatible = "ti,tps6507x", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tps6507x_of_match);
+#endif
static struct i2c_driver tps6507x_i2c_driver = {
.driver = {
.name = "tps6507x",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(tps6507x_of_match),
},
.probe = tps6507x_i2c_probe,
.remove = tps6507x_i2c_remove,
diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c
index 074ae32b0d2..1c3e6e2efe4 100644
--- a/drivers/mfd/tps65090.c
+++ b/drivers/mfd/tps65090.c
@@ -25,221 +25,137 @@
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps65090.h>
-#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/err.h>
#define NUM_INT_REG 2
#define TOTAL_NUM_REG 0x18
-/* interrupt status registers */
-#define TPS65090_INT_STS 0x0
-#define TPS65090_INT_STS2 0x1
-
-/* interrupt mask registers */
-#define TPS65090_INT_MSK 0x2
-#define TPS65090_INT_MSK2 0x3
-
-struct tps65090_irq_data {
- u8 mask_reg;
- u8 mask_pos;
-};
-
-#define TPS65090_IRQ(_reg, _mask_pos) \
- { \
- .mask_reg = (_reg), \
- .mask_pos = (_mask_pos), \
+#define TPS65090_INT1_MASK_VAC_STATUS_CHANGE 1
+#define TPS65090_INT1_MASK_VSYS_STATUS_CHANGE 2
+#define TPS65090_INT1_MASK_BAT_STATUS_CHANGE 3
+#define TPS65090_INT1_MASK_CHARGING_STATUS_CHANGE 4
+#define TPS65090_INT1_MASK_CHARGING_COMPLETE 5
+#define TPS65090_INT1_MASK_OVERLOAD_DCDC1 6
+#define TPS65090_INT1_MASK_OVERLOAD_DCDC2 7
+#define TPS65090_INT2_MASK_OVERLOAD_DCDC3 0
+#define TPS65090_INT2_MASK_OVERLOAD_FET1 1
+#define TPS65090_INT2_MASK_OVERLOAD_FET2 2
+#define TPS65090_INT2_MASK_OVERLOAD_FET3 3
+#define TPS65090_INT2_MASK_OVERLOAD_FET4 4
+#define TPS65090_INT2_MASK_OVERLOAD_FET5 5
+#define TPS65090_INT2_MASK_OVERLOAD_FET6 6
+#define TPS65090_INT2_MASK_OVERLOAD_FET7 7
+
+static struct resource charger_resources[] = {
+ {
+ .start = TPS65090_IRQ_VAC_STATUS_CHANGE,
+ .end = TPS65090_IRQ_VAC_STATUS_CHANGE,
+ .flags = IORESOURCE_IRQ,
}
+};
-static const struct tps65090_irq_data tps65090_irqs[] = {
- [0] = TPS65090_IRQ(0, 0),
- [1] = TPS65090_IRQ(0, 1),
- [2] = TPS65090_IRQ(0, 2),
- [3] = TPS65090_IRQ(0, 3),
- [4] = TPS65090_IRQ(0, 4),
- [5] = TPS65090_IRQ(0, 5),
- [6] = TPS65090_IRQ(0, 6),
- [7] = TPS65090_IRQ(0, 7),
- [8] = TPS65090_IRQ(1, 0),
- [9] = TPS65090_IRQ(1, 1),
- [10] = TPS65090_IRQ(1, 2),
- [11] = TPS65090_IRQ(1, 3),
- [12] = TPS65090_IRQ(1, 4),
- [13] = TPS65090_IRQ(1, 5),
- [14] = TPS65090_IRQ(1, 6),
- [15] = TPS65090_IRQ(1, 7),
+enum tps65090_cells {
+ PMIC = 0,
+ CHARGER = 1,
};
static struct mfd_cell tps65090s[] = {
- {
+ [PMIC] = {
.name = "tps65090-pmic",
},
- {
- .name = "tps65090-regulator",
+ [CHARGER] = {
+ .name = "tps65090-charger",
+ .num_resources = ARRAY_SIZE(charger_resources),
+ .resources = &charger_resources[0],
+ .of_compatible = "ti,tps65090-charger",
},
};
-int tps65090_write(struct device *dev, int reg, uint8_t val)
-{
- struct tps65090 *tps = dev_get_drvdata(dev);
- return regmap_write(tps->rmap, reg, val);
-}
-EXPORT_SYMBOL_GPL(tps65090_write);
-
-int tps65090_read(struct device *dev, int reg, uint8_t *val)
-{
- struct tps65090 *tps = dev_get_drvdata(dev);
- unsigned int temp_val;
- int ret;
- ret = regmap_read(tps->rmap, reg, &temp_val);
- if (!ret)
- *val = temp_val;
- return ret;
-}
-EXPORT_SYMBOL_GPL(tps65090_read);
-
-int tps65090_set_bits(struct device *dev, int reg, uint8_t bit_num)
-{
- struct tps65090 *tps = dev_get_drvdata(dev);
- return regmap_update_bits(tps->rmap, reg, BIT(bit_num), ~0u);
-}
-EXPORT_SYMBOL_GPL(tps65090_set_bits);
-
-int tps65090_clr_bits(struct device *dev, int reg, uint8_t bit_num)
-{
- struct tps65090 *tps = dev_get_drvdata(dev);
- return regmap_update_bits(tps->rmap, reg, BIT(bit_num), 0u);
-}
-EXPORT_SYMBOL_GPL(tps65090_clr_bits);
-
-static void tps65090_irq_lock(struct irq_data *data)
-{
- struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data);
-
- mutex_lock(&tps65090->irq_lock);
-}
-
-static void tps65090_irq_mask(struct irq_data *irq_data)
-{
- struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data);
- unsigned int __irq = irq_data->hwirq;
- const struct tps65090_irq_data *data = &tps65090_irqs[__irq];
-
- tps65090_set_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg),
- data->mask_pos);
-}
-
-static void tps65090_irq_unmask(struct irq_data *irq_data)
-{
- struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data);
- unsigned int __irq = irq_data->irq - tps65090->irq_base;
- const struct tps65090_irq_data *data = &tps65090_irqs[__irq];
-
- tps65090_clr_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg),
- data->mask_pos);
-}
-
-static void tps65090_irq_sync_unlock(struct irq_data *data)
-{
- struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data);
-
- mutex_unlock(&tps65090->irq_lock);
-}
-
-static irqreturn_t tps65090_irq(int irq, void *data)
-{
- struct tps65090 *tps65090 = data;
- int ret = 0;
- u8 status, mask;
- unsigned long int acks = 0;
- int i;
-
- for (i = 0; i < NUM_INT_REG; i++) {
- ret = tps65090_read(tps65090->dev, TPS65090_INT_MSK + i, &mask);
- if (ret < 0) {
- dev_err(tps65090->dev,
- "failed to read mask reg [addr:%d]\n",
- TPS65090_INT_MSK + i);
- return IRQ_NONE;
- }
- ret = tps65090_read(tps65090->dev, TPS65090_INT_STS + i,
- &status);
- if (ret < 0) {
- dev_err(tps65090->dev,
- "failed to read status reg [addr:%d]\n",
- TPS65090_INT_STS + i);
- return IRQ_NONE;
- }
- if (status) {
- /* Ack only those interrupts which are not masked */
- status &= (~mask);
- ret = tps65090_write(tps65090->dev,
- TPS65090_INT_STS + i, status);
- if (ret < 0) {
- dev_err(tps65090->dev,
- "failed to write interrupt status\n");
- return IRQ_NONE;
- }
- acks |= (status << (i * 8));
- }
- }
-
- for_each_set_bit(i, &acks, ARRAY_SIZE(tps65090_irqs))
- handle_nested_irq(tps65090->irq_base + i);
- return acks ? IRQ_HANDLED : IRQ_NONE;
-}
-
-static int __devinit tps65090_irq_init(struct tps65090 *tps65090, int irq,
- int irq_base)
-{
- int i, ret;
-
- if (!irq_base) {
- dev_err(tps65090->dev, "IRQ base not set\n");
- return -EINVAL;
- }
-
- mutex_init(&tps65090->irq_lock);
-
- for (i = 0; i < NUM_INT_REG; i++)
- tps65090_write(tps65090->dev, TPS65090_INT_MSK + i, 0xFF);
-
- for (i = 0; i < NUM_INT_REG; i++)
- tps65090_write(tps65090->dev, TPS65090_INT_STS + i, 0xff);
-
- tps65090->irq_base = irq_base;
- tps65090->irq_chip.name = "tps65090";
- tps65090->irq_chip.irq_mask = tps65090_irq_mask;
- tps65090->irq_chip.irq_unmask = tps65090_irq_unmask;
- tps65090->irq_chip.irq_bus_lock = tps65090_irq_lock;
- tps65090->irq_chip.irq_bus_sync_unlock = tps65090_irq_sync_unlock;
-
- for (i = 0; i < ARRAY_SIZE(tps65090_irqs); i++) {
- int __irq = i + tps65090->irq_base;
- irq_set_chip_data(__irq, tps65090);
- irq_set_chip_and_handler(__irq, &tps65090->irq_chip,
- handle_simple_irq);
- irq_set_nested_thread(__irq, 1);
-#ifdef CONFIG_ARM
- set_irq_flags(__irq, IRQF_VALID);
-#endif
- }
-
- ret = request_threaded_irq(irq, NULL, tps65090_irq, IRQF_ONESHOT,
- "tps65090", tps65090);
- if (!ret) {
- device_init_wakeup(tps65090->dev, 1);
- enable_irq_wake(irq);
- }
+static const struct regmap_irq tps65090_irqs[] = {
+ /* INT1 IRQs*/
+ [TPS65090_IRQ_VAC_STATUS_CHANGE] = {
+ .mask = TPS65090_INT1_MASK_VAC_STATUS_CHANGE,
+ },
+ [TPS65090_IRQ_VSYS_STATUS_CHANGE] = {
+ .mask = TPS65090_INT1_MASK_VSYS_STATUS_CHANGE,
+ },
+ [TPS65090_IRQ_BAT_STATUS_CHANGE] = {
+ .mask = TPS65090_INT1_MASK_BAT_STATUS_CHANGE,
+ },
+ [TPS65090_IRQ_CHARGING_STATUS_CHANGE] = {
+ .mask = TPS65090_INT1_MASK_CHARGING_STATUS_CHANGE,
+ },
+ [TPS65090_IRQ_CHARGING_COMPLETE] = {
+ .mask = TPS65090_INT1_MASK_CHARGING_COMPLETE,
+ },
+ [TPS65090_IRQ_OVERLOAD_DCDC1] = {
+ .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC1,
+ },
+ [TPS65090_IRQ_OVERLOAD_DCDC2] = {
+ .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC2,
+ },
+ /* INT2 IRQs*/
+ [TPS65090_IRQ_OVERLOAD_DCDC3] = {
+ .reg_offset = 1,
+ .mask = TPS65090_INT2_MASK_OVERLOAD_DCDC3,
+ },
+ [TPS65090_IRQ_OVERLOAD_FET1] = {
+ .reg_offset = 1,
+ .mask = TPS65090_INT2_MASK_OVERLOAD_FET1,
+ },
+ [TPS65090_IRQ_OVERLOAD_FET2] = {
+ .reg_offset = 1,
+ .mask = TPS65090_INT2_MASK_OVERLOAD_FET2,
+ },
+ [TPS65090_IRQ_OVERLOAD_FET3] = {
+ .reg_offset = 1,
+ .mask = TPS65090_INT2_MASK_OVERLOAD_FET3,
+ },
+ [TPS65090_IRQ_OVERLOAD_FET4] = {
+ .reg_offset = 1,
+ .mask = TPS65090_INT2_MASK_OVERLOAD_FET4,
+ },
+ [TPS65090_IRQ_OVERLOAD_FET5] = {
+ .reg_offset = 1,
+ .mask = TPS65090_INT2_MASK_OVERLOAD_FET5,
+ },
+ [TPS65090_IRQ_OVERLOAD_FET6] = {
+ .reg_offset = 1,
+ .mask = TPS65090_INT2_MASK_OVERLOAD_FET6,
+ },
+ [TPS65090_IRQ_OVERLOAD_FET7] = {
+ .reg_offset = 1,
+ .mask = TPS65090_INT2_MASK_OVERLOAD_FET7,
+ },
+};
- return ret;
-}
+static struct regmap_irq_chip tps65090_irq_chip = {
+ .name = "tps65090",
+ .irqs = tps65090_irqs,
+ .num_irqs = ARRAY_SIZE(tps65090_irqs),
+ .num_regs = NUM_INT_REG,
+ .status_base = TPS65090_REG_INTR_STS,
+ .mask_base = TPS65090_REG_INTR_MASK,
+ .mask_invert = true,
+};
static bool is_volatile_reg(struct device *dev, unsigned int reg)
{
- if (reg == TPS65090_INT_STS)
- return true;
- else
+ /* Nearly all registers have status bits mixed in, except a few */
+ switch (reg) {
+ case TPS65090_REG_INTR_MASK:
+ case TPS65090_REG_INTR_MASK2:
+ case TPS65090_REG_CG_CTRL0:
+ case TPS65090_REG_CG_CTRL1:
+ case TPS65090_REG_CG_CTRL2:
+ case TPS65090_REG_CG_CTRL3:
+ case TPS65090_REG_CG_CTRL4:
+ case TPS65090_REG_CG_CTRL5:
return false;
+ }
+ return true;
}
static const struct regmap_config tps65090_regmap_config = {
@@ -251,48 +167,64 @@ static const struct regmap_config tps65090_regmap_config = {
.volatile_reg = is_volatile_reg,
};
-static int __devinit tps65090_i2c_probe(struct i2c_client *client,
+#ifdef CONFIG_OF
+static const struct of_device_id tps65090_of_match[] = {
+ { .compatible = "ti,tps65090",},
+ {},
+};
+MODULE_DEVICE_TABLE(of, tps65090_of_match);
+#endif
+
+static int tps65090_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct tps65090_platform_data *pdata = client->dev.platform_data;
+ struct tps65090_platform_data *pdata = dev_get_platdata(&client->dev);
+ int irq_base = 0;
struct tps65090 *tps65090;
int ret;
- if (!pdata) {
- dev_err(&client->dev, "tps65090 requires platform data\n");
+ if (!pdata && !client->dev.of_node) {
+ dev_err(&client->dev,
+ "tps65090 requires platform data or of_node\n");
return -EINVAL;
}
- tps65090 = devm_kzalloc(&client->dev, sizeof(struct tps65090),
- GFP_KERNEL);
- if (tps65090 == NULL)
+ if (pdata)
+ irq_base = pdata->irq_base;
+
+ tps65090 = devm_kzalloc(&client->dev, sizeof(*tps65090), GFP_KERNEL);
+ if (!tps65090) {
+ dev_err(&client->dev, "mem alloc for tps65090 failed\n");
return -ENOMEM;
+ }
- tps65090->client = client;
tps65090->dev = &client->dev;
i2c_set_clientdata(client, tps65090);
- mutex_init(&tps65090->lock);
-
- if (client->irq) {
- ret = tps65090_irq_init(tps65090, client->irq, pdata->irq_base);
- if (ret) {
- dev_err(&client->dev, "IRQ init failed with err: %d\n",
- ret);
- goto err_exit;
- }
- }
-
- tps65090->rmap = devm_regmap_init_i2c(tps65090->client,
- &tps65090_regmap_config);
+ tps65090->rmap = devm_regmap_init_i2c(client, &tps65090_regmap_config);
if (IS_ERR(tps65090->rmap)) {
ret = PTR_ERR(tps65090->rmap);
dev_err(&client->dev, "regmap_init failed with err: %d\n", ret);
- goto err_irq_exit;
+ return ret;
+ }
+
+ if (client->irq) {
+ ret = regmap_add_irq_chip(tps65090->rmap, client->irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW, irq_base,
+ &tps65090_irq_chip, &tps65090->irq_data);
+ if (ret) {
+ dev_err(&client->dev,
+ "IRQ init failed with err: %d\n", ret);
+ return ret;
+ }
+ } else {
+ /* Don't tell children they have an IRQ that'll never fire */
+ tps65090s[CHARGER].num_resources = 0;
}
ret = mfd_add_devices(tps65090->dev, -1, tps65090s,
- ARRAY_SIZE(tps65090s), NULL, 0, NULL);
+ ARRAY_SIZE(tps65090s), NULL,
+ 0, regmap_irq_get_domain(tps65090->irq_data));
if (ret) {
dev_err(&client->dev, "add mfd devices failed with err: %d\n",
ret);
@@ -303,44 +235,21 @@ static int __devinit tps65090_i2c_probe(struct i2c_client *client,
err_irq_exit:
if (client->irq)
- free_irq(client->irq, tps65090);
-err_exit:
+ regmap_del_irq_chip(client->irq, tps65090->irq_data);
return ret;
}
-static int __devexit tps65090_i2c_remove(struct i2c_client *client)
+static int tps65090_i2c_remove(struct i2c_client *client)
{
struct tps65090 *tps65090 = i2c_get_clientdata(client);
mfd_remove_devices(tps65090->dev);
if (client->irq)
- free_irq(client->irq, tps65090);
+ regmap_del_irq_chip(client->irq, tps65090->irq_data);
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int tps65090_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- if (client->irq)
- disable_irq(client->irq);
- return 0;
-}
-
-static int tps65090_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- if (client->irq)
- enable_irq(client->irq);
- return 0;
-}
-#endif
-
-static const struct dev_pm_ops tps65090_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(tps65090_suspend, tps65090_resume)
-};
-
static const struct i2c_device_id tps65090_id_table[] = {
{ "tps65090", 0 },
{ },
@@ -351,10 +260,10 @@ static struct i2c_driver tps65090_driver = {
.driver = {
.name = "tps65090",
.owner = THIS_MODULE,
- .pm = &tps65090_pm_ops,
+ .of_match_table = of_match_ptr(tps65090_of_match),
},
.probe = tps65090_i2c_probe,
- .remove = __devexit_p(tps65090_i2c_remove),
+ .remove = tps65090_i2c_remove,
.id_table = tps65090_id_table,
};
diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c
index 3fb32e65525..3cc4c7084b9 100644
--- a/drivers/mfd/tps65217.c
+++ b/drivers/mfd/tps65217.c
@@ -30,7 +30,7 @@
#include <linux/mfd/core.h>
#include <linux/mfd/tps65217.h>
-static struct mfd_cell tps65217s[] = {
+static const struct mfd_cell tps65217s[] = {
{
.name = "tps65217-pmic",
},
@@ -153,13 +153,14 @@ static const struct of_device_id tps65217_of_match[] = {
{ /* sentinel */ },
};
-static int __devinit tps65217_probe(struct i2c_client *client,
+static int tps65217_probe(struct i2c_client *client,
const struct i2c_device_id *ids)
{
struct tps65217 *tps;
unsigned int version;
- unsigned int chip_id = ids->driver_data;
+ unsigned long chip_id = ids->driver_data;
const struct of_device_id *match;
+ bool status_off = false;
int ret;
if (client->dev.of_node) {
@@ -169,7 +170,9 @@ static int __devinit tps65217_probe(struct i2c_client *client,
"Failed to find matching dt id\n");
return -EINVAL;
}
- chip_id = (unsigned int)match->data;
+ chip_id = (unsigned long)match->data;
+ status_off = of_property_read_bool(client->dev.of_node,
+ "ti,pmic-shutdown-controller");
}
if (!chip_id) {
@@ -207,6 +210,15 @@ static int __devinit tps65217_probe(struct i2c_client *client,
return ret;
}
+ /* Set the PMIC to shutdown on PWR_EN toggle */
+ if (status_off) {
+ ret = tps65217_set_bits(tps, TPS65217_REG_STATUS,
+ TPS65217_STATUS_OFF, TPS65217_STATUS_OFF,
+ TPS65217_PROTECT_NONE);
+ if (ret)
+ dev_warn(tps->dev, "unable to set the status OFF\n");
+ }
+
dev_info(tps->dev, "TPS65217 ID %#x version 1.%d\n",
(version & TPS65217_CHIPID_CHIP_MASK) >> 4,
version & TPS65217_CHIPID_REV_MASK);
@@ -214,7 +226,7 @@ static int __devinit tps65217_probe(struct i2c_client *client,
return 0;
}
-static int __devexit tps65217_remove(struct i2c_client *client)
+static int tps65217_remove(struct i2c_client *client)
{
struct tps65217 *tps = i2c_get_clientdata(client);
@@ -233,11 +245,11 @@ static struct i2c_driver tps65217_driver = {
.driver = {
.name = "tps65217",
.owner = THIS_MODULE,
- .of_match_table = of_match_ptr(tps65217_of_match),
+ .of_match_table = tps65217_of_match,
},
.id_table = tps65217_id_table,
.probe = tps65217_probe,
- .remove = __devexit_p(tps65217_remove),
+ .remove = tps65217_remove,
};
static int __init tps65217_init(void)
diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c
new file mode 100644
index 00000000000..0d256cb002e
--- /dev/null
+++ b/drivers/mfd/tps65218.c
@@ -0,0 +1,283 @@
+/*
+ * Driver for TPS65218 Integrated power management chipsets
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether expressed or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65218.h>
+
+#define TPS65218_PASSWORD_REGS_UNLOCK 0x7D
+
+/**
+ * tps65218_reg_read: Read a single tps65218 register.
+ *
+ * @tps: Device to read from.
+ * @reg: Register to read.
+ * @val: Contians the value
+ */
+int tps65218_reg_read(struct tps65218 *tps, unsigned int reg,
+ unsigned int *val)
+{
+ return regmap_read(tps->regmap, reg, val);
+}
+EXPORT_SYMBOL_GPL(tps65218_reg_read);
+
+/**
+ * tps65218_reg_write: Write a single tps65218 register.
+ *
+ * @tps65218: Device to write to.
+ * @reg: Register to write to.
+ * @val: Value to write.
+ * @level: Password protected level
+ */
+int tps65218_reg_write(struct tps65218 *tps, unsigned int reg,
+ unsigned int val, unsigned int level)
+{
+ int ret;
+ unsigned int xor_reg_val;
+
+ switch (level) {
+ case TPS65218_PROTECT_NONE:
+ return regmap_write(tps->regmap, reg, val);
+ case TPS65218_PROTECT_L1:
+ xor_reg_val = reg ^ TPS65218_PASSWORD_REGS_UNLOCK;
+ ret = regmap_write(tps->regmap, TPS65218_REG_PASSWORD,
+ xor_reg_val);
+ if (ret < 0)
+ return ret;
+
+ return regmap_write(tps->regmap, reg, val);
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(tps65218_reg_write);
+
+/**
+ * tps65218_update_bits: Modify bits w.r.t mask, val and level.
+ *
+ * @tps65218: Device to write to.
+ * @reg: Register to read-write to.
+ * @mask: Mask.
+ * @val: Value to write.
+ * @level: Password protected level
+ */
+static int tps65218_update_bits(struct tps65218 *tps, unsigned int reg,
+ unsigned int mask, unsigned int val, unsigned int level)
+{
+ int ret;
+ unsigned int data;
+
+ ret = tps65218_reg_read(tps, reg, &data);
+ if (ret) {
+ dev_err(tps->dev, "Read from reg 0x%x failed\n", reg);
+ return ret;
+ }
+
+ data &= ~mask;
+ data |= val & mask;
+
+ mutex_lock(&tps->tps_lock);
+ ret = tps65218_reg_write(tps, reg, data, level);
+ if (ret)
+ dev_err(tps->dev, "Write for reg 0x%x failed\n", reg);
+ mutex_unlock(&tps->tps_lock);
+
+ return ret;
+}
+
+int tps65218_set_bits(struct tps65218 *tps, unsigned int reg,
+ unsigned int mask, unsigned int val, unsigned int level)
+{
+ return tps65218_update_bits(tps, reg, mask, val, level);
+}
+EXPORT_SYMBOL_GPL(tps65218_set_bits);
+
+int tps65218_clear_bits(struct tps65218 *tps, unsigned int reg,
+ unsigned int mask, unsigned int level)
+{
+ return tps65218_update_bits(tps, reg, mask, 0, level);
+}
+EXPORT_SYMBOL_GPL(tps65218_clear_bits);
+
+static struct regmap_config tps65218_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct regmap_irq tps65218_irqs[] = {
+ /* INT1 IRQs */
+ [TPS65218_PRGC_IRQ] = {
+ .mask = TPS65218_INT1_PRGC,
+ },
+ [TPS65218_CC_AQC_IRQ] = {
+ .mask = TPS65218_INT1_CC_AQC,
+ },
+ [TPS65218_HOT_IRQ] = {
+ .mask = TPS65218_INT1_HOT,
+ },
+ [TPS65218_PB_IRQ] = {
+ .mask = TPS65218_INT1_PB,
+ },
+ [TPS65218_AC_IRQ] = {
+ .mask = TPS65218_INT1_AC,
+ },
+ [TPS65218_VPRG_IRQ] = {
+ .mask = TPS65218_INT1_VPRG,
+ },
+ [TPS65218_INVALID1_IRQ] = {
+ },
+ [TPS65218_INVALID2_IRQ] = {
+ },
+ /* INT2 IRQs*/
+ [TPS65218_LS1_I_IRQ] = {
+ .mask = TPS65218_INT2_LS1_I,
+ .reg_offset = 1,
+ },
+ [TPS65218_LS2_I_IRQ] = {
+ .mask = TPS65218_INT2_LS2_I,
+ .reg_offset = 1,
+ },
+ [TPS65218_LS3_I_IRQ] = {
+ .mask = TPS65218_INT2_LS3_I,
+ .reg_offset = 1,
+ },
+ [TPS65218_LS1_F_IRQ] = {
+ .mask = TPS65218_INT2_LS1_F,
+ .reg_offset = 1,
+ },
+ [TPS65218_LS2_F_IRQ] = {
+ .mask = TPS65218_INT2_LS2_F,
+ .reg_offset = 1,
+ },
+ [TPS65218_LS3_F_IRQ] = {
+ .mask = TPS65218_INT2_LS3_F,
+ .reg_offset = 1,
+ },
+ [TPS65218_INVALID3_IRQ] = {
+ },
+ [TPS65218_INVALID4_IRQ] = {
+ },
+};
+
+static struct regmap_irq_chip tps65218_irq_chip = {
+ .name = "tps65218",
+ .irqs = tps65218_irqs,
+ .num_irqs = ARRAY_SIZE(tps65218_irqs),
+
+ .num_regs = 2,
+ .mask_base = TPS65218_REG_INT_MASK1,
+};
+
+static const struct of_device_id of_tps65218_match_table[] = {
+ { .compatible = "ti,tps65218", },
+ {}
+};
+
+static int tps65218_probe(struct i2c_client *client,
+ const struct i2c_device_id *ids)
+{
+ struct tps65218 *tps;
+ const struct of_device_id *match;
+ int ret;
+
+ match = of_match_device(of_tps65218_match_table, &client->dev);
+ if (!match) {
+ dev_err(&client->dev,
+ "Failed to find matching dt id\n");
+ return -EINVAL;
+ }
+
+ tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
+ if (!tps)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, tps);
+ tps->dev = &client->dev;
+ tps->irq = client->irq;
+ tps->regmap = devm_regmap_init_i2c(client, &tps65218_regmap_config);
+ if (IS_ERR(tps->regmap)) {
+ ret = PTR_ERR(tps->regmap);
+ dev_err(tps->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ mutex_init(&tps->tps_lock);
+
+ ret = regmap_add_irq_chip(tps->regmap, tps->irq,
+ IRQF_ONESHOT, 0, &tps65218_irq_chip,
+ &tps->irq_data);
+ if (ret < 0)
+ return ret;
+
+ ret = of_platform_populate(client->dev.of_node, NULL, NULL,
+ &client->dev);
+ if (ret < 0)
+ goto err_irq;
+
+ return 0;
+
+err_irq:
+ regmap_del_irq_chip(tps->irq, tps->irq_data);
+
+ return ret;
+}
+
+static int tps65218_remove(struct i2c_client *client)
+{
+ struct tps65218 *tps = i2c_get_clientdata(client);
+
+ regmap_del_irq_chip(tps->irq, tps->irq_data);
+
+ return 0;
+}
+
+static const struct i2c_device_id tps65218_id_table[] = {
+ { "tps65218", TPS65218 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, tps65218_id_table);
+
+static struct i2c_driver tps65218_driver = {
+ .driver = {
+ .name = "tps65218",
+ .owner = THIS_MODULE,
+ .of_match_table = of_tps65218_match_table,
+ },
+ .probe = tps65218_probe,
+ .remove = tps65218_remove,
+ .id_table = tps65218_id_table,
+};
+
+module_i2c_driver(tps65218_driver);
+
+MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
+MODULE_DESCRIPTION("TPS65218 chip family multi-function driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
index 46746436877..8e1dbc46958 100644
--- a/drivers/mfd/tps6586x.c
+++ b/drivers/mfd/tps6586x.c
@@ -17,15 +17,16 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/i2c.h>
+#include <linux/platform_device.h>
#include <linux/regmap.h>
-#include <linux/regulator/of_regulator.h>
-#include <linux/regulator/machine.h>
+#include <linux/of.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps6586x.h>
@@ -94,12 +95,25 @@ static const struct tps6586x_irq_data tps6586x_irqs[] = {
[TPS6586X_INT_RTC_ALM2] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 1),
};
-static struct mfd_cell tps6586x_cell[] = {
+static struct resource tps6586x_rtc_resources[] = {
+ {
+ .start = TPS6586X_INT_RTC_ALM1,
+ .end = TPS6586X_INT_RTC_ALM1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static const struct mfd_cell tps6586x_cell[] = {
{
.name = "tps6586x-gpio",
},
{
+ .name = "tps6586x-regulator",
+ },
+ {
.name = "tps6586x-rtc",
+ .num_resources = ARRAY_SIZE(tps6586x_rtc_resources),
+ .resources = &tps6586x_rtc_resources[0],
},
{
.name = "tps6586x-onkey",
@@ -110,12 +124,15 @@ struct tps6586x {
struct device *dev;
struct i2c_client *client;
struct regmap *regmap;
+ int version;
+ int irq;
struct irq_chip irq_chip;
struct mutex irq_lock;
int irq_base;
u32 irq_en;
u8 mask_reg[5];
+ struct irq_domain *irq_domain;
};
static inline struct tps6586x *dev_to_tps6586x(struct device *dev)
@@ -184,6 +201,22 @@ int tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask)
}
EXPORT_SYMBOL_GPL(tps6586x_update);
+int tps6586x_irq_get_virq(struct device *dev, int irq)
+{
+ struct tps6586x *tps6586x = dev_to_tps6586x(dev);
+
+ return irq_create_mapping(tps6586x->irq_domain, irq);
+}
+EXPORT_SYMBOL_GPL(tps6586x_irq_get_virq);
+
+int tps6586x_get_version(struct device *dev)
+{
+ struct tps6586x *tps6586x = dev_get_drvdata(dev);
+
+ return tps6586x->version;
+}
+EXPORT_SYMBOL_GPL(tps6586x_get_version);
+
static int __remove_subdev(struct device *dev, void *unused)
{
platform_device_unregister(to_platform_device(dev));
@@ -205,7 +238,7 @@ static void tps6586x_irq_lock(struct irq_data *data)
static void tps6586x_irq_enable(struct irq_data *irq_data)
{
struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data);
- unsigned int __irq = irq_data->irq - tps6586x->irq_base;
+ unsigned int __irq = irq_data->hwirq;
const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq];
tps6586x->mask_reg[data->mask_reg] &= ~data->mask_mask;
@@ -216,7 +249,7 @@ static void tps6586x_irq_disable(struct irq_data *irq_data)
{
struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data);
- unsigned int __irq = irq_data->irq - tps6586x->irq_base;
+ unsigned int __irq = irq_data->hwirq;
const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq];
tps6586x->mask_reg[data->mask_reg] |= data->mask_mask;
@@ -239,6 +272,50 @@ static void tps6586x_irq_sync_unlock(struct irq_data *data)
mutex_unlock(&tps6586x->irq_lock);
}
+#ifdef CONFIG_PM_SLEEP
+static int tps6586x_irq_set_wake(struct irq_data *irq_data, unsigned int on)
+{
+ struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data);
+ return irq_set_irq_wake(tps6586x->irq, on);
+}
+#else
+#define tps6586x_irq_set_wake NULL
+#endif
+
+static struct irq_chip tps6586x_irq_chip = {
+ .name = "tps6586x",
+ .irq_bus_lock = tps6586x_irq_lock,
+ .irq_bus_sync_unlock = tps6586x_irq_sync_unlock,
+ .irq_disable = tps6586x_irq_disable,
+ .irq_enable = tps6586x_irq_enable,
+ .irq_set_wake = tps6586x_irq_set_wake,
+};
+
+static int tps6586x_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct tps6586x *tps6586x = h->host_data;
+
+ irq_set_chip_data(virq, tps6586x);
+ irq_set_chip_and_handler(virq, &tps6586x_irq_chip, handle_simple_irq);
+ irq_set_nested_thread(virq, 1);
+
+ /* ARM needs us to explicitly flag the IRQ as valid
+ * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+ set_irq_flags(virq, IRQF_VALID);
+#else
+ irq_set_noprobe(virq);
+#endif
+
+ return 0;
+}
+
+static struct irq_domain_ops tps6586x_domain_ops = {
+ .map = tps6586x_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
static irqreturn_t tps6586x_irq(int irq, void *data)
{
struct tps6586x *tps6586x = data;
@@ -259,7 +336,8 @@ static irqreturn_t tps6586x_irq(int irq, void *data)
int i = __ffs(acks);
if (tps6586x->irq_en & (1 << i))
- handle_nested_irq(tps6586x->irq_base + i);
+ handle_nested_irq(
+ irq_find_mapping(tps6586x->irq_domain, i));
acks &= ~(1 << i);
}
@@ -267,16 +345,15 @@ static irqreturn_t tps6586x_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int __devinit tps6586x_irq_init(struct tps6586x *tps6586x, int irq,
+static int tps6586x_irq_init(struct tps6586x *tps6586x, int irq,
int irq_base)
{
int i, ret;
u8 tmp[4];
+ int new_irq_base;
+ int irq_num = ARRAY_SIZE(tps6586x_irqs);
- if (!irq_base) {
- dev_warn(tps6586x->dev, "No interrupt support on IRQ base\n");
- return -EINVAL;
- }
+ tps6586x->irq = irq;
mutex_init(&tps6586x->irq_lock);
for (i = 0; i < 5; i++) {
@@ -286,37 +363,34 @@ static int __devinit tps6586x_irq_init(struct tps6586x *tps6586x, int irq,
tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1, sizeof(tmp), tmp);
- tps6586x->irq_base = irq_base;
-
- tps6586x->irq_chip.name = "tps6586x";
- tps6586x->irq_chip.irq_enable = tps6586x_irq_enable;
- tps6586x->irq_chip.irq_disable = tps6586x_irq_disable;
- tps6586x->irq_chip.irq_bus_lock = tps6586x_irq_lock;
- tps6586x->irq_chip.irq_bus_sync_unlock = tps6586x_irq_sync_unlock;
-
- for (i = 0; i < ARRAY_SIZE(tps6586x_irqs); i++) {
- int __irq = i + tps6586x->irq_base;
- irq_set_chip_data(__irq, tps6586x);
- irq_set_chip_and_handler(__irq, &tps6586x->irq_chip,
- handle_simple_irq);
- irq_set_nested_thread(__irq, 1);
-#ifdef CONFIG_ARM
- set_irq_flags(__irq, IRQF_VALID);
-#endif
+ if (irq_base > 0) {
+ new_irq_base = irq_alloc_descs(irq_base, 0, irq_num, -1);
+ if (new_irq_base < 0) {
+ dev_err(tps6586x->dev,
+ "Failed to alloc IRQs: %d\n", new_irq_base);
+ return new_irq_base;
+ }
+ } else {
+ new_irq_base = 0;
}
+ tps6586x->irq_domain = irq_domain_add_simple(tps6586x->dev->of_node,
+ irq_num, new_irq_base, &tps6586x_domain_ops,
+ tps6586x);
+ if (!tps6586x->irq_domain) {
+ dev_err(tps6586x->dev, "Failed to create IRQ domain\n");
+ return -ENOMEM;
+ }
ret = request_threaded_irq(irq, NULL, tps6586x_irq, IRQF_ONESHOT,
"tps6586x", tps6586x);
- if (!ret) {
+ if (!ret)
device_init_wakeup(tps6586x->dev, 1);
- enable_irq_wake(irq);
- }
return ret;
}
-static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x,
+static int tps6586x_add_subdevs(struct tps6586x *tps6586x,
struct tps6586x_platform_data *pdata)
{
struct tps6586x_subdev_info *subdev;
@@ -350,80 +424,19 @@ failed:
}
#ifdef CONFIG_OF
-static struct of_regulator_match tps6586x_matches[] = {
- { .name = "sys", .driver_data = (void *)TPS6586X_ID_SYS },
- { .name = "sm0", .driver_data = (void *)TPS6586X_ID_SM_0 },
- { .name = "sm1", .driver_data = (void *)TPS6586X_ID_SM_1 },
- { .name = "sm2", .driver_data = (void *)TPS6586X_ID_SM_2 },
- { .name = "ldo0", .driver_data = (void *)TPS6586X_ID_LDO_0 },
- { .name = "ldo1", .driver_data = (void *)TPS6586X_ID_LDO_1 },
- { .name = "ldo2", .driver_data = (void *)TPS6586X_ID_LDO_2 },
- { .name = "ldo3", .driver_data = (void *)TPS6586X_ID_LDO_3 },
- { .name = "ldo4", .driver_data = (void *)TPS6586X_ID_LDO_4 },
- { .name = "ldo5", .driver_data = (void *)TPS6586X_ID_LDO_5 },
- { .name = "ldo6", .driver_data = (void *)TPS6586X_ID_LDO_6 },
- { .name = "ldo7", .driver_data = (void *)TPS6586X_ID_LDO_7 },
- { .name = "ldo8", .driver_data = (void *)TPS6586X_ID_LDO_8 },
- { .name = "ldo9", .driver_data = (void *)TPS6586X_ID_LDO_9 },
- { .name = "ldo_rtc", .driver_data = (void *)TPS6586X_ID_LDO_RTC },
-};
-
static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *client)
{
- const unsigned int num = ARRAY_SIZE(tps6586x_matches);
struct device_node *np = client->dev.of_node;
struct tps6586x_platform_data *pdata;
- struct tps6586x_subdev_info *devs;
- struct device_node *regs;
- const char *sys_rail_name = NULL;
- unsigned int count;
- unsigned int i, j;
- int err;
-
- regs = of_find_node_by_name(np, "regulators");
- if (!regs)
- return NULL;
-
- err = of_regulator_match(&client->dev, regs, tps6586x_matches, num);
- if (err < 0) {
- of_node_put(regs);
- return NULL;
- }
-
- of_node_put(regs);
- count = err;
-
- devs = devm_kzalloc(&client->dev, count * sizeof(*devs), GFP_KERNEL);
- if (!devs)
- return NULL;
-
- for (i = 0, j = 0; i < num && j < count; i++) {
- struct regulator_init_data *reg_idata;
-
- if (!tps6586x_matches[i].init_data)
- continue;
-
- reg_idata = tps6586x_matches[i].init_data;
- devs[j].name = "tps6586x-regulator";
- devs[j].platform_data = tps6586x_matches[i].init_data;
- devs[j].id = (int)tps6586x_matches[i].driver_data;
- if (devs[j].id == TPS6586X_ID_SYS)
- sys_rail_name = reg_idata->constraints.name;
-
- if ((devs[j].id == TPS6586X_ID_LDO_5) ||
- (devs[j].id == TPS6586X_ID_LDO_RTC))
- reg_idata->supply_regulator = sys_rail_name;
-
- devs[j].of_node = tps6586x_matches[i].of_node;
- j++;
- }
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
+ if (!pdata) {
+ dev_err(&client->dev, "Memory allocation failed\n");
return NULL;
+ }
- pdata->num_subdevs = count;
- pdata->subdevs = devs;
+ pdata->num_subdevs = 0;
+ pdata->subdevs = NULL;
pdata->gpio_base = -1;
pdata->irq_base = -1;
pdata->pm_off = of_property_read_bool(np, "ti,system-power-controller");
@@ -431,7 +444,7 @@ static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *clien
return pdata;
}
-static struct of_device_id tps6586x_of_match[] = {
+static const struct of_device_id tps6586x_of_match[] = {
{ .compatible = "ti,tps6586x", },
{ },
};
@@ -468,12 +481,42 @@ static void tps6586x_power_off(void)
tps6586x_set_bits(tps6586x_dev, TPS6586X_SUPPLYENE, SLEEP_MODE_BIT);
}
-static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
+static void tps6586x_print_version(struct i2c_client *client, int version)
+{
+ const char *name;
+
+ switch (version) {
+ case TPS658621A:
+ name = "TPS658621A";
+ break;
+ case TPS658621CD:
+ name = "TPS658621C/D";
+ break;
+ case TPS658623:
+ name = "TPS658623";
+ break;
+ case TPS658640:
+ case TPS658640v2:
+ name = "TPS658640";
+ break;
+ case TPS658643:
+ name = "TPS658643";
+ break;
+ default:
+ name = "TPS6586X";
+ break;
+ }
+
+ dev_info(&client->dev, "Found %s, VERSIONCRC is %02x\n", name, version);
+}
+
+static int tps6586x_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct tps6586x_platform_data *pdata = client->dev.platform_data;
+ struct tps6586x_platform_data *pdata = dev_get_platdata(&client->dev);
struct tps6586x *tps6586x;
int ret;
+ int version;
if (!pdata && client->dev.of_node)
pdata = tps6586x_parse_dt(client);
@@ -483,19 +526,18 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
return -ENOTSUPP;
}
- ret = i2c_smbus_read_byte_data(client, TPS6586X_VERSIONCRC);
- if (ret < 0) {
- dev_err(&client->dev, "Chip ID read failed: %d\n", ret);
+ version = i2c_smbus_read_byte_data(client, TPS6586X_VERSIONCRC);
+ if (version < 0) {
+ dev_err(&client->dev, "Chip ID read failed: %d\n", version);
return -EIO;
}
- dev_info(&client->dev, "VERSIONCRC is %02x\n", ret);
-
tps6586x = devm_kzalloc(&client->dev, sizeof(*tps6586x), GFP_KERNEL);
- if (tps6586x == NULL) {
- dev_err(&client->dev, "memory for tps6586x alloc failed\n");
+ if (!tps6586x)
return -ENOMEM;
- }
+
+ tps6586x->version = version;
+ tps6586x_print_version(client, tps6586x->version);
tps6586x->client = client;
tps6586x->dev = &client->dev;
@@ -521,7 +563,7 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
ret = mfd_add_devices(tps6586x->dev, -1,
tps6586x_cell, ARRAY_SIZE(tps6586x_cell),
- NULL, 0, NULL);
+ NULL, 0, tps6586x->irq_domain);
if (ret < 0) {
dev_err(&client->dev, "mfd_add_devices failed: %d\n", ret);
goto err_mfd_add;
@@ -548,7 +590,7 @@ err_mfd_add:
return ret;
}
-static int __devexit tps6586x_i2c_remove(struct i2c_client *client)
+static int tps6586x_i2c_remove(struct i2c_client *client)
{
struct tps6586x *tps6586x = i2c_get_clientdata(client);
@@ -572,7 +614,7 @@ static struct i2c_driver tps6586x_driver = {
.of_match_table = of_match_ptr(tps6586x_of_match),
},
.probe = tps6586x_i2c_probe,
- .remove = __devexit_p(tps6586x_i2c_remove),
+ .remove = tps6586x_i2c_remove,
.id_table = tps6586x_id_table,
};
diff --git a/drivers/mfd/tps65910-irq.c b/drivers/mfd/tps65910-irq.c
deleted file mode 100644
index 09aab3e4776..00000000000
--- a/drivers/mfd/tps65910-irq.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * tps65910-irq.c -- TI TPS6591x
- *
- * Copyright 2010 Texas Instruments Inc.
- *
- * Author: Graeme Gregory <gg@slimlogic.co.uk>
- * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/bug.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/irqdomain.h>
-#include <linux/gpio.h>
-#include <linux/mfd/tps65910.h>
-
-/*
- * This is a threaded IRQ handler so can access I2C/SPI. Since all
- * interrupts are clear on read the IRQ line will be reasserted and
- * the physical IRQ will be handled again if another interrupt is
- * asserted while we run - in the normal course of events this is a
- * rare occurrence so we save I2C/SPI reads. We're also assuming that
- * it's rare to get lots of interrupts firing simultaneously so try to
- * minimise I/O.
- */
-static irqreturn_t tps65910_irq(int irq, void *irq_data)
-{
- struct tps65910 *tps65910 = irq_data;
- unsigned int reg;
- u32 irq_sts;
- u32 irq_mask;
- int i;
-
- tps65910_reg_read(tps65910, TPS65910_INT_STS, &reg);
- irq_sts = reg;
- tps65910_reg_read(tps65910, TPS65910_INT_STS2, &reg);
- irq_sts |= reg << 8;
- switch (tps65910_chip_id(tps65910)) {
- case TPS65911:
- tps65910_reg_read(tps65910, TPS65910_INT_STS3, &reg);
- irq_sts |= reg << 16;
- }
-
- tps65910_reg_read(tps65910, TPS65910_INT_MSK, &reg);
- irq_mask = reg;
- tps65910_reg_read(tps65910, TPS65910_INT_MSK2, &reg);
- irq_mask |= reg << 8;
- switch (tps65910_chip_id(tps65910)) {
- case TPS65911:
- tps65910_reg_read(tps65910, TPS65910_INT_MSK3, &reg);
- irq_mask |= reg << 16;
- }
-
- irq_sts &= ~irq_mask;
-
- if (!irq_sts)
- return IRQ_NONE;
-
- for (i = 0; i < tps65910->irq_num; i++) {
-
- if (!(irq_sts & (1 << i)))
- continue;
-
- handle_nested_irq(irq_find_mapping(tps65910->domain, i));
- }
-
- /* Write the STS register back to clear IRQs we handled */
- reg = irq_sts & 0xFF;
- irq_sts >>= 8;
- tps65910_reg_write(tps65910, TPS65910_INT_STS, reg);
- reg = irq_sts & 0xFF;
- tps65910_reg_write(tps65910, TPS65910_INT_STS2, reg);
- switch (tps65910_chip_id(tps65910)) {
- case TPS65911:
- reg = irq_sts >> 8;
- tps65910_reg_write(tps65910, TPS65910_INT_STS3, reg);
- }
-
- return IRQ_HANDLED;
-}
-
-static void tps65910_irq_lock(struct irq_data *data)
-{
- struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
-
- mutex_lock(&tps65910->irq_lock);
-}
-
-static void tps65910_irq_sync_unlock(struct irq_data *data)
-{
- struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
- u32 reg_mask;
- unsigned int reg;
-
- tps65910_reg_read(tps65910, TPS65910_INT_MSK, &reg);
- reg_mask = reg;
- tps65910_reg_read(tps65910, TPS65910_INT_MSK2, &reg);
- reg_mask |= reg << 8;
- switch (tps65910_chip_id(tps65910)) {
- case TPS65911:
- tps65910_reg_read(tps65910, TPS65910_INT_MSK3, &reg);
- reg_mask |= reg << 16;
- }
-
- if (tps65910->irq_mask != reg_mask) {
- reg = tps65910->irq_mask & 0xFF;
- tps65910_reg_write(tps65910, TPS65910_INT_MSK, reg);
- reg = tps65910->irq_mask >> 8 & 0xFF;
- tps65910_reg_write(tps65910, TPS65910_INT_MSK2, reg);
- switch (tps65910_chip_id(tps65910)) {
- case TPS65911:
- reg = tps65910->irq_mask >> 16;
- tps65910_reg_write(tps65910, TPS65910_INT_MSK3, reg);
- }
- }
- mutex_unlock(&tps65910->irq_lock);
-}
-
-static void tps65910_irq_enable(struct irq_data *data)
-{
- struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
-
- tps65910->irq_mask &= ~(1 << data->hwirq);
-}
-
-static void tps65910_irq_disable(struct irq_data *data)
-{
- struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
-
- tps65910->irq_mask |= (1 << data->hwirq);
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int tps65910_irq_set_wake(struct irq_data *data, unsigned int enable)
-{
- struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
- return irq_set_irq_wake(tps65910->chip_irq, enable);
-}
-#else
-#define tps65910_irq_set_wake NULL
-#endif
-
-static struct irq_chip tps65910_irq_chip = {
- .name = "tps65910",
- .irq_bus_lock = tps65910_irq_lock,
- .irq_bus_sync_unlock = tps65910_irq_sync_unlock,
- .irq_disable = tps65910_irq_disable,
- .irq_enable = tps65910_irq_enable,
- .irq_set_wake = tps65910_irq_set_wake,
-};
-
-static int tps65910_irq_map(struct irq_domain *h, unsigned int virq,
- irq_hw_number_t hw)
-{
- struct tps65910 *tps65910 = h->host_data;
-
- irq_set_chip_data(virq, tps65910);
- irq_set_chip_and_handler(virq, &tps65910_irq_chip, handle_edge_irq);
- irq_set_nested_thread(virq, 1);
-
- /* ARM needs us to explicitly flag the IRQ as valid
- * and will set them noprobe when we do so. */
-#ifdef CONFIG_ARM
- set_irq_flags(virq, IRQF_VALID);
-#else
- irq_set_noprobe(virq);
-#endif
-
- return 0;
-}
-
-static struct irq_domain_ops tps65910_domain_ops = {
- .map = tps65910_irq_map,
- .xlate = irq_domain_xlate_twocell,
-};
-
-int tps65910_irq_init(struct tps65910 *tps65910, int irq,
- struct tps65910_platform_data *pdata)
-{
- int ret;
- int flags = IRQF_ONESHOT;
-
- if (!irq) {
- dev_warn(tps65910->dev, "No interrupt support, no core IRQ\n");
- return -EINVAL;
- }
-
- if (!pdata) {
- dev_warn(tps65910->dev, "No interrupt support, no pdata\n");
- return -EINVAL;
- }
-
- switch (tps65910_chip_id(tps65910)) {
- case TPS65910:
- tps65910->irq_num = TPS65910_NUM_IRQ;
- break;
- case TPS65911:
- tps65910->irq_num = TPS65911_NUM_IRQ;
- break;
- }
-
- if (pdata->irq_base > 0) {
- pdata->irq_base = irq_alloc_descs(pdata->irq_base, 0,
- tps65910->irq_num, -1);
- if (pdata->irq_base < 0) {
- dev_warn(tps65910->dev, "Failed to alloc IRQs: %d\n",
- pdata->irq_base);
- return pdata->irq_base;
- }
- }
-
- tps65910->irq_mask = 0xFFFFFF;
-
- mutex_init(&tps65910->irq_lock);
- tps65910->chip_irq = irq;
- tps65910->irq_base = pdata->irq_base;
-
- if (pdata->irq_base > 0)
- tps65910->domain = irq_domain_add_legacy(tps65910->dev->of_node,
- tps65910->irq_num,
- pdata->irq_base,
- 0,
- &tps65910_domain_ops, tps65910);
- else
- tps65910->domain = irq_domain_add_linear(tps65910->dev->of_node,
- tps65910->irq_num,
- &tps65910_domain_ops, tps65910);
-
- if (!tps65910->domain) {
- dev_err(tps65910->dev, "Failed to create IRQ domain\n");
- return -ENOMEM;
- }
-
- ret = request_threaded_irq(irq, NULL, tps65910_irq, flags,
- "tps65910", tps65910);
-
- irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
-
- if (ret != 0)
- dev_err(tps65910->dev, "Failed to request IRQ: %d\n", ret);
-
- return ret;
-}
-
-int tps65910_irq_exit(struct tps65910 *tps65910)
-{
- if (tps65910->chip_irq)
- free_irq(tps65910->chip_irq, tps65910);
- return 0;
-}
diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c
index 0d79ce2b501..f9e42ea1cb1 100644
--- a/drivers/mfd/tps65910.c
+++ b/drivers/mfd/tps65910.c
@@ -19,9 +19,13 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/mfd/core.h>
#include <linux/regmap.h>
#include <linux/mfd/tps65910.h>
+#include <linux/of.h>
#include <linux/of_device.h>
static struct resource rtc_resources[] = {
@@ -32,7 +36,7 @@ static struct resource rtc_resources[] = {
}
};
-static struct mfd_cell tps65910s[] = {
+static const struct mfd_cell tps65910s[] = {
{
.name = "tps65910-gpio",
},
@@ -50,6 +54,221 @@ static struct mfd_cell tps65910s[] = {
};
+static const struct regmap_irq tps65911_irqs[] = {
+ /* INT_STS */
+ [TPS65911_IRQ_PWRHOLD_F] = {
+ .mask = INT_MSK_PWRHOLD_F_IT_MSK_MASK,
+ .reg_offset = 0,
+ },
+ [TPS65911_IRQ_VBAT_VMHI] = {
+ .mask = INT_MSK_VMBHI_IT_MSK_MASK,
+ .reg_offset = 0,
+ },
+ [TPS65911_IRQ_PWRON] = {
+ .mask = INT_MSK_PWRON_IT_MSK_MASK,
+ .reg_offset = 0,
+ },
+ [TPS65911_IRQ_PWRON_LP] = {
+ .mask = INT_MSK_PWRON_LP_IT_MSK_MASK,
+ .reg_offset = 0,
+ },
+ [TPS65911_IRQ_PWRHOLD_R] = {
+ .mask = INT_MSK_PWRHOLD_R_IT_MSK_MASK,
+ .reg_offset = 0,
+ },
+ [TPS65911_IRQ_HOTDIE] = {
+ .mask = INT_MSK_HOTDIE_IT_MSK_MASK,
+ .reg_offset = 0,
+ },
+ [TPS65911_IRQ_RTC_ALARM] = {
+ .mask = INT_MSK_RTC_ALARM_IT_MSK_MASK,
+ .reg_offset = 0,
+ },
+ [TPS65911_IRQ_RTC_PERIOD] = {
+ .mask = INT_MSK_RTC_PERIOD_IT_MSK_MASK,
+ .reg_offset = 0,
+ },
+
+ /* INT_STS2 */
+ [TPS65911_IRQ_GPIO0_R] = {
+ .mask = INT_MSK2_GPIO0_R_IT_MSK_MASK,
+ .reg_offset = 1,
+ },
+ [TPS65911_IRQ_GPIO0_F] = {
+ .mask = INT_MSK2_GPIO0_F_IT_MSK_MASK,
+ .reg_offset = 1,
+ },
+ [TPS65911_IRQ_GPIO1_R] = {
+ .mask = INT_MSK2_GPIO1_R_IT_MSK_MASK,
+ .reg_offset = 1,
+ },
+ [TPS65911_IRQ_GPIO1_F] = {
+ .mask = INT_MSK2_GPIO1_F_IT_MSK_MASK,
+ .reg_offset = 1,
+ },
+ [TPS65911_IRQ_GPIO2_R] = {
+ .mask = INT_MSK2_GPIO2_R_IT_MSK_MASK,
+ .reg_offset = 1,
+ },
+ [TPS65911_IRQ_GPIO2_F] = {
+ .mask = INT_MSK2_GPIO2_F_IT_MSK_MASK,
+ .reg_offset = 1,
+ },
+ [TPS65911_IRQ_GPIO3_R] = {
+ .mask = INT_MSK2_GPIO3_R_IT_MSK_MASK,
+ .reg_offset = 1,
+ },
+ [TPS65911_IRQ_GPIO3_F] = {
+ .mask = INT_MSK2_GPIO3_F_IT_MSK_MASK,
+ .reg_offset = 1,
+ },
+
+ /* INT_STS2 */
+ [TPS65911_IRQ_GPIO4_R] = {
+ .mask = INT_MSK3_GPIO4_R_IT_MSK_MASK,
+ .reg_offset = 2,
+ },
+ [TPS65911_IRQ_GPIO4_F] = {
+ .mask = INT_MSK3_GPIO4_F_IT_MSK_MASK,
+ .reg_offset = 2,
+ },
+ [TPS65911_IRQ_GPIO5_R] = {
+ .mask = INT_MSK3_GPIO5_R_IT_MSK_MASK,
+ .reg_offset = 2,
+ },
+ [TPS65911_IRQ_GPIO5_F] = {
+ .mask = INT_MSK3_GPIO5_F_IT_MSK_MASK,
+ .reg_offset = 2,
+ },
+ [TPS65911_IRQ_WTCHDG] = {
+ .mask = INT_MSK3_WTCHDG_IT_MSK_MASK,
+ .reg_offset = 2,
+ },
+ [TPS65911_IRQ_VMBCH2_H] = {
+ .mask = INT_MSK3_VMBCH2_H_IT_MSK_MASK,
+ .reg_offset = 2,
+ },
+ [TPS65911_IRQ_VMBCH2_L] = {
+ .mask = INT_MSK3_VMBCH2_L_IT_MSK_MASK,
+ .reg_offset = 2,
+ },
+ [TPS65911_IRQ_PWRDN] = {
+ .mask = INT_MSK3_PWRDN_IT_MSK_MASK,
+ .reg_offset = 2,
+ },
+};
+
+static const struct regmap_irq tps65910_irqs[] = {
+ /* INT_STS */
+ [TPS65910_IRQ_VBAT_VMBDCH] = {
+ .mask = TPS65910_INT_MSK_VMBDCH_IT_MSK_MASK,
+ .reg_offset = 0,
+ },
+ [TPS65910_IRQ_VBAT_VMHI] = {
+ .mask = TPS65910_INT_MSK_VMBHI_IT_MSK_MASK,
+ .reg_offset = 0,
+ },
+ [TPS65910_IRQ_PWRON] = {
+ .mask = TPS65910_INT_MSK_PWRON_IT_MSK_MASK,
+ .reg_offset = 0,
+ },
+ [TPS65910_IRQ_PWRON_LP] = {
+ .mask = TPS65910_INT_MSK_PWRON_LP_IT_MSK_MASK,
+ .reg_offset = 0,
+ },
+ [TPS65910_IRQ_PWRHOLD] = {
+ .mask = TPS65910_INT_MSK_PWRHOLD_IT_MSK_MASK,
+ .reg_offset = 0,
+ },
+ [TPS65910_IRQ_HOTDIE] = {
+ .mask = TPS65910_INT_MSK_HOTDIE_IT_MSK_MASK,
+ .reg_offset = 0,
+ },
+ [TPS65910_IRQ_RTC_ALARM] = {
+ .mask = TPS65910_INT_MSK_RTC_ALARM_IT_MSK_MASK,
+ .reg_offset = 0,
+ },
+ [TPS65910_IRQ_RTC_PERIOD] = {
+ .mask = TPS65910_INT_MSK_RTC_PERIOD_IT_MSK_MASK,
+ .reg_offset = 0,
+ },
+
+ /* INT_STS2 */
+ [TPS65910_IRQ_GPIO_R] = {
+ .mask = TPS65910_INT_MSK2_GPIO0_F_IT_MSK_MASK,
+ .reg_offset = 1,
+ },
+ [TPS65910_IRQ_GPIO_F] = {
+ .mask = TPS65910_INT_MSK2_GPIO0_R_IT_MSK_MASK,
+ .reg_offset = 1,
+ },
+};
+
+static struct regmap_irq_chip tps65911_irq_chip = {
+ .name = "tps65910",
+ .irqs = tps65911_irqs,
+ .num_irqs = ARRAY_SIZE(tps65911_irqs),
+ .num_regs = 3,
+ .irq_reg_stride = 2,
+ .status_base = TPS65910_INT_STS,
+ .mask_base = TPS65910_INT_MSK,
+ .ack_base = TPS65910_INT_STS,
+};
+
+static struct regmap_irq_chip tps65910_irq_chip = {
+ .name = "tps65910",
+ .irqs = tps65910_irqs,
+ .num_irqs = ARRAY_SIZE(tps65910_irqs),
+ .num_regs = 2,
+ .irq_reg_stride = 2,
+ .status_base = TPS65910_INT_STS,
+ .mask_base = TPS65910_INT_MSK,
+ .ack_base = TPS65910_INT_STS,
+};
+
+static int tps65910_irq_init(struct tps65910 *tps65910, int irq,
+ struct tps65910_platform_data *pdata)
+{
+ int ret = 0;
+ static struct regmap_irq_chip *tps6591x_irqs_chip;
+
+ if (!irq) {
+ dev_warn(tps65910->dev, "No interrupt support, no core IRQ\n");
+ return -EINVAL;
+ }
+
+ if (!pdata) {
+ dev_warn(tps65910->dev, "No interrupt support, no pdata\n");
+ return -EINVAL;
+ }
+
+ switch (tps65910_chip_id(tps65910)) {
+ case TPS65910:
+ tps6591x_irqs_chip = &tps65910_irq_chip;
+ break;
+ case TPS65911:
+ tps6591x_irqs_chip = &tps65911_irq_chip;
+ break;
+ }
+
+ tps65910->chip_irq = irq;
+ ret = regmap_add_irq_chip(tps65910->regmap, tps65910->chip_irq,
+ IRQF_ONESHOT, pdata->irq_base,
+ tps6591x_irqs_chip, &tps65910->irq_data);
+ if (ret < 0) {
+ dev_warn(tps65910->dev, "Failed to add irq_chip %d\n", ret);
+ tps65910->chip_irq = 0;
+ }
+ return ret;
+}
+
+static int tps65910_irq_exit(struct tps65910 *tps65910)
+{
+ if (tps65910->chip_irq > 0)
+ regmap_del_irq_chip(tps65910->chip_irq, tps65910->irq_data);
+ return 0;
+}
+
static bool is_volatile_reg(struct device *dev, unsigned int reg)
{
struct tps65910 *tps65910 = dev_get_drvdata(dev);
@@ -78,7 +297,7 @@ static const struct regmap_config tps65910_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int __devinit tps65910_ck32k_init(struct tps65910 *tps65910,
+static int tps65910_ck32k_init(struct tps65910 *tps65910,
struct tps65910_board *pmic_pdata)
{
int ret;
@@ -96,7 +315,7 @@ static int __devinit tps65910_ck32k_init(struct tps65910 *tps65910,
return 0;
}
-static int __devinit tps65910_sleepinit(struct tps65910 *tps65910,
+static int tps65910_sleepinit(struct tps65910 *tps65910,
struct tps65910_board *pmic_pdata)
{
struct device *dev = NULL;
@@ -160,7 +379,7 @@ err_sleep_init:
}
#ifdef CONFIG_OF
-static struct of_device_id tps65910_of_match[] = {
+static const struct of_device_id tps65910_of_match[] = {
{ .compatible = "ti,tps65910", .data = (void *)TPS65910},
{ .compatible = "ti,tps65911", .data = (void *)TPS65911},
{ },
@@ -194,14 +413,10 @@ static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client,
ret = of_property_read_u32(np, "ti,vmbch-threshold", &prop);
if (!ret)
board_info->vmbch_threshold = prop;
- else if (*chip_id == TPS65911)
- dev_warn(&client->dev, "VMBCH-Threshold not specified");
ret = of_property_read_u32(np, "ti,vmbch2-threshold", &prop);
if (!ret)
board_info->vmbch2_threshold = prop;
- else if (*chip_id == TPS65911)
- dev_warn(&client->dev, "VMBCH2-Threshold not specified");
prop = of_property_read_bool(np, "ti,en-ck32k-xtal");
board_info->en_ck32k_xtal = prop;
@@ -237,7 +452,7 @@ static void tps65910_power_off(void)
DEVCTRL_DEV_ON_MASK);
}
-static __devinit int tps65910_i2c_probe(struct i2c_client *i2c,
+static int tps65910_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct tps65910 *tps65910;
@@ -270,7 +485,6 @@ static __devinit int tps65910_i2c_probe(struct i2c_client *i2c,
tps65910->dev = &i2c->dev;
tps65910->i2c_client = i2c;
tps65910->id = chip_id;
- mutex_init(&tps65910->io_mutex);
tps65910->regmap = devm_regmap_init_i2c(i2c, &tps65910_regmap_config);
if (IS_ERR(tps65910->regmap)) {
@@ -279,14 +493,6 @@ static __devinit int tps65910_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = mfd_add_devices(tps65910->dev, -1,
- tps65910s, ARRAY_SIZE(tps65910s),
- NULL, 0, NULL);
- if (ret < 0) {
- dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret);
- return ret;
- }
-
init_data->irq = pmic_plat_data->irq;
init_data->irq_base = pmic_plat_data->irq_base;
@@ -299,10 +505,20 @@ static __devinit int tps65910_i2c_probe(struct i2c_client *i2c,
pm_power_off = tps65910_power_off;
}
+ ret = mfd_add_devices(tps65910->dev, -1,
+ tps65910s, ARRAY_SIZE(tps65910s),
+ NULL, 0,
+ regmap_irq_get_domain(tps65910->irq_data));
+ if (ret < 0) {
+ dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret);
+ tps65910_irq_exit(tps65910);
+ return ret;
+ }
+
return ret;
}
-static __devexit int tps65910_i2c_remove(struct i2c_client *i2c)
+static int tps65910_i2c_remove(struct i2c_client *i2c)
{
struct tps65910 *tps65910 = i2c_get_clientdata(i2c);
@@ -327,7 +543,7 @@ static struct i2c_driver tps65910_i2c_driver = {
.of_match_table = of_match_ptr(tps65910_of_match),
},
.probe = tps65910_i2c_probe,
- .remove = __devexit_p(tps65910_i2c_remove),
+ .remove = tps65910_i2c_remove,
.id_table = tps65910_i2c_id,
};
diff --git a/drivers/mfd/tps65911-comparator.c b/drivers/mfd/tps65911-comparator.c
index 0b6e361432c..c0816ebd9d7 100644
--- a/drivers/mfd/tps65911-comparator.c
+++ b/drivers/mfd/tps65911-comparator.c
@@ -122,7 +122,7 @@ static ssize_t comp_threshold_show(struct device *dev,
static DEVICE_ATTR(comp1_threshold, S_IRUGO, comp_threshold_show, NULL);
static DEVICE_ATTR(comp2_threshold, S_IRUGO, comp_threshold_show, NULL);
-static __devinit int tps65911_comparator_probe(struct platform_device *pdev)
+static int tps65911_comparator_probe(struct platform_device *pdev)
{
struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent);
struct tps65910_board *pdata = dev_get_platdata(tps65910->dev);
@@ -152,7 +152,7 @@ static __devinit int tps65911_comparator_probe(struct platform_device *pdev)
return ret;
}
-static __devexit int tps65911_comparator_remove(struct platform_device *pdev)
+static int tps65911_comparator_remove(struct platform_device *pdev)
{
struct tps65910 *tps65910;
@@ -169,7 +169,7 @@ static struct platform_driver tps65911_comparator_driver = {
.owner = THIS_MODULE,
},
.probe = tps65911_comparator_probe,
- .remove = __devexit_p(tps65911_comparator_remove),
+ .remove = tps65911_comparator_remove,
};
static int __init tps65911_comparator_init(void)
diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c
index 4658b5bdcd8..1f82d60b1d0 100644
--- a/drivers/mfd/tps65912-core.c
+++ b/drivers/mfd/tps65912-core.c
@@ -15,13 +15,12 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps65912.h>
-static struct mfd_cell tps65912s[] = {
+static const struct mfd_cell tps65912s[] = {
{
.name = "tps65912-pmic",
},
@@ -123,7 +122,7 @@ EXPORT_SYMBOL_GPL(tps65912_reg_write);
int tps65912_device_init(struct tps65912 *tps65912)
{
- struct tps65912_board *pmic_plat_data = tps65912->dev->platform_data;
+ struct tps65912_board *pmic_plat_data = dev_get_platdata(tps65912->dev);
struct tps65912_platform_data *init_data;
int ret, dcdc_avs, value;
@@ -162,14 +161,13 @@ int tps65912_device_init(struct tps65912 *tps65912)
err:
kfree(init_data);
mfd_remove_devices(tps65912->dev);
- kfree(tps65912);
return ret;
}
void tps65912_device_exit(struct tps65912 *tps65912)
{
mfd_remove_devices(tps65912->dev);
- kfree(tps65912);
+ tps65912_irq_exit(tps65912);
}
MODULE_AUTHOR("Margarita Olaya <magi@slimlogic.co.uk>");
diff --git a/drivers/mfd/tps65912-i2c.c b/drivers/mfd/tps65912-i2c.c
index c041f2c3d2b..6a6343ee95f 100644
--- a/drivers/mfd/tps65912-i2c.c
+++ b/drivers/mfd/tps65912-i2c.c
@@ -77,7 +77,8 @@ static int tps65912_i2c_probe(struct i2c_client *i2c,
{
struct tps65912 *tps65912;
- tps65912 = kzalloc(sizeof(struct tps65912), GFP_KERNEL);
+ tps65912 = devm_kzalloc(&i2c->dev,
+ sizeof(struct tps65912), GFP_KERNEL);
if (tps65912 == NULL)
return -ENOMEM;
diff --git a/drivers/mfd/tps65912-irq.c b/drivers/mfd/tps65912-irq.c
index d360a83a273..fbecec7f1e3 100644
--- a/drivers/mfd/tps65912-irq.c
+++ b/drivers/mfd/tps65912-irq.c
@@ -15,7 +15,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/interrupt.h>
diff --git a/drivers/mfd/tps65912-spi.c b/drivers/mfd/tps65912-spi.c
index 27d3302d56b..69a5178bf15 100644
--- a/drivers/mfd/tps65912-spi.c
+++ b/drivers/mfd/tps65912-spi.c
@@ -81,11 +81,12 @@ static int tps65912_spi_read(struct tps65912 *tps65912, u8 addr,
return ret;
}
-static int __devinit tps65912_spi_probe(struct spi_device *spi)
+static int tps65912_spi_probe(struct spi_device *spi)
{
struct tps65912 *tps65912;
- tps65912 = kzalloc(sizeof(struct tps65912), GFP_KERNEL);
+ tps65912 = devm_kzalloc(&spi->dev,
+ sizeof(struct tps65912), GFP_KERNEL);
if (tps65912 == NULL)
return -ENOMEM;
@@ -99,7 +100,7 @@ static int __devinit tps65912_spi_probe(struct spi_device *spi)
return tps65912_device_init(tps65912);
}
-static int __devexit tps65912_spi_remove(struct spi_device *spi)
+static int tps65912_spi_remove(struct spi_device *spi)
{
struct tps65912 *tps65912 = spi_get_drvdata(spi);
@@ -114,7 +115,7 @@ static struct spi_driver tps65912_spi_driver = {
.owner = THIS_MODULE,
},
.probe = tps65912_spi_probe,
- .remove = __devexit_p(tps65912_spi_remove),
+ .remove = tps65912_spi_remove,
};
static int __init tps65912_spi_init(void)
diff --git a/drivers/mfd/tps80031.c b/drivers/mfd/tps80031.c
new file mode 100644
index 00000000000..ed6c5b0956e
--- /dev/null
+++ b/drivers/mfd/tps80031.c
@@ -0,0 +1,573 @@
+/*
+ * tps80031.c -- TI TPS80031/TPS80032 mfd core driver.
+ *
+ * MFD core driver for TI TPS80031/TPS80032 Fully Integrated
+ * Power Management with Power Path and Battery Charger
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; 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/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps80031.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+static struct resource tps80031_rtc_resources[] = {
+ {
+ .start = TPS80031_INT_RTC_ALARM,
+ .end = TPS80031_INT_RTC_ALARM,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/* TPS80031 sub mfd devices */
+static const struct mfd_cell tps80031_cell[] = {
+ {
+ .name = "tps80031-pmic",
+ },
+ {
+ .name = "tps80031-clock",
+ },
+ {
+ .name = "tps80031-rtc",
+ .num_resources = ARRAY_SIZE(tps80031_rtc_resources),
+ .resources = tps80031_rtc_resources,
+ },
+ {
+ .name = "tps80031-gpadc",
+ },
+ {
+ .name = "tps80031-fuel-gauge",
+ },
+ {
+ .name = "tps80031-charger",
+ },
+};
+
+static int tps80031_slave_address[TPS80031_NUM_SLAVES] = {
+ TPS80031_I2C_ID0_ADDR,
+ TPS80031_I2C_ID1_ADDR,
+ TPS80031_I2C_ID2_ADDR,
+ TPS80031_I2C_ID3_ADDR,
+};
+
+struct tps80031_pupd_data {
+ u8 reg;
+ u8 pullup_bit;
+ u8 pulldown_bit;
+};
+
+#define TPS80031_IRQ(_reg, _mask) \
+ { \
+ .reg_offset = (TPS80031_INT_MSK_LINE_##_reg) - \
+ TPS80031_INT_MSK_LINE_A, \
+ .mask = BIT(_mask), \
+ }
+
+static const struct regmap_irq tps80031_main_irqs[] = {
+ [TPS80031_INT_PWRON] = TPS80031_IRQ(A, 0),
+ [TPS80031_INT_RPWRON] = TPS80031_IRQ(A, 1),
+ [TPS80031_INT_SYS_VLOW] = TPS80031_IRQ(A, 2),
+ [TPS80031_INT_RTC_ALARM] = TPS80031_IRQ(A, 3),
+ [TPS80031_INT_RTC_PERIOD] = TPS80031_IRQ(A, 4),
+ [TPS80031_INT_HOT_DIE] = TPS80031_IRQ(A, 5),
+ [TPS80031_INT_VXX_SHORT] = TPS80031_IRQ(A, 6),
+ [TPS80031_INT_SPDURATION] = TPS80031_IRQ(A, 7),
+ [TPS80031_INT_WATCHDOG] = TPS80031_IRQ(B, 0),
+ [TPS80031_INT_BAT] = TPS80031_IRQ(B, 1),
+ [TPS80031_INT_SIM] = TPS80031_IRQ(B, 2),
+ [TPS80031_INT_MMC] = TPS80031_IRQ(B, 3),
+ [TPS80031_INT_RES] = TPS80031_IRQ(B, 4),
+ [TPS80031_INT_GPADC_RT] = TPS80031_IRQ(B, 5),
+ [TPS80031_INT_GPADC_SW2_EOC] = TPS80031_IRQ(B, 6),
+ [TPS80031_INT_CC_AUTOCAL] = TPS80031_IRQ(B, 7),
+ [TPS80031_INT_ID_WKUP] = TPS80031_IRQ(C, 0),
+ [TPS80031_INT_VBUSS_WKUP] = TPS80031_IRQ(C, 1),
+ [TPS80031_INT_ID] = TPS80031_IRQ(C, 2),
+ [TPS80031_INT_VBUS] = TPS80031_IRQ(C, 3),
+ [TPS80031_INT_CHRG_CTRL] = TPS80031_IRQ(C, 4),
+ [TPS80031_INT_EXT_CHRG] = TPS80031_IRQ(C, 5),
+ [TPS80031_INT_INT_CHRG] = TPS80031_IRQ(C, 6),
+ [TPS80031_INT_RES2] = TPS80031_IRQ(C, 7),
+};
+
+static struct regmap_irq_chip tps80031_irq_chip = {
+ .name = "tps80031",
+ .irqs = tps80031_main_irqs,
+ .num_irqs = ARRAY_SIZE(tps80031_main_irqs),
+ .num_regs = 3,
+ .status_base = TPS80031_INT_STS_A,
+ .mask_base = TPS80031_INT_MSK_LINE_A,
+};
+
+#define PUPD_DATA(_reg, _pulldown_bit, _pullup_bit) \
+ { \
+ .reg = TPS80031_CFG_INPUT_PUPD##_reg, \
+ .pulldown_bit = _pulldown_bit, \
+ .pullup_bit = _pullup_bit, \
+ }
+
+static const struct tps80031_pupd_data tps80031_pupds[] = {
+ [TPS80031_PREQ1] = PUPD_DATA(1, BIT(0), BIT(1)),
+ [TPS80031_PREQ2A] = PUPD_DATA(1, BIT(2), BIT(3)),
+ [TPS80031_PREQ2B] = PUPD_DATA(1, BIT(4), BIT(5)),
+ [TPS80031_PREQ2C] = PUPD_DATA(1, BIT(6), BIT(7)),
+ [TPS80031_PREQ3] = PUPD_DATA(2, BIT(0), BIT(1)),
+ [TPS80031_NRES_WARM] = PUPD_DATA(2, 0, BIT(2)),
+ [TPS80031_PWM_FORCE] = PUPD_DATA(2, BIT(5), 0),
+ [TPS80031_CHRG_EXT_CHRG_STATZ] = PUPD_DATA(2, 0, BIT(6)),
+ [TPS80031_SIM] = PUPD_DATA(3, BIT(0), BIT(1)),
+ [TPS80031_MMC] = PUPD_DATA(3, BIT(2), BIT(3)),
+ [TPS80031_GPADC_START] = PUPD_DATA(3, BIT(4), 0),
+ [TPS80031_DVSI2C_SCL] = PUPD_DATA(4, 0, BIT(0)),
+ [TPS80031_DVSI2C_SDA] = PUPD_DATA(4, 0, BIT(1)),
+ [TPS80031_CTLI2C_SCL] = PUPD_DATA(4, 0, BIT(2)),
+ [TPS80031_CTLI2C_SDA] = PUPD_DATA(4, 0, BIT(3)),
+};
+static struct tps80031 *tps80031_power_off_dev;
+
+int tps80031_ext_power_req_config(struct device *dev,
+ unsigned long ext_ctrl_flag, int preq_bit,
+ int state_reg_add, int trans_reg_add)
+{
+ u8 res_ass_reg = 0;
+ int preq_mask_bit = 0;
+ int ret;
+
+ if (!(ext_ctrl_flag & TPS80031_EXT_PWR_REQ))
+ return 0;
+
+ if (ext_ctrl_flag & TPS80031_PWR_REQ_INPUT_PREQ1) {
+ res_ass_reg = TPS80031_PREQ1_RES_ASS_A + (preq_bit >> 3);
+ preq_mask_bit = 5;
+ } else if (ext_ctrl_flag & TPS80031_PWR_REQ_INPUT_PREQ2) {
+ res_ass_reg = TPS80031_PREQ2_RES_ASS_A + (preq_bit >> 3);
+ preq_mask_bit = 6;
+ } else if (ext_ctrl_flag & TPS80031_PWR_REQ_INPUT_PREQ3) {
+ res_ass_reg = TPS80031_PREQ3_RES_ASS_A + (preq_bit >> 3);
+ preq_mask_bit = 7;
+ }
+
+ /* Configure REQ_ASS registers */
+ ret = tps80031_set_bits(dev, TPS80031_SLAVE_ID1, res_ass_reg,
+ BIT(preq_bit & 0x7));
+ if (ret < 0) {
+ dev_err(dev, "reg 0x%02x setbit failed, err = %d\n",
+ res_ass_reg, ret);
+ return ret;
+ }
+
+ /* Unmask the PREQ */
+ ret = tps80031_clr_bits(dev, TPS80031_SLAVE_ID1,
+ TPS80031_PHOENIX_MSK_TRANSITION, BIT(preq_mask_bit));
+ if (ret < 0) {
+ dev_err(dev, "reg 0x%02x clrbit failed, err = %d\n",
+ TPS80031_PHOENIX_MSK_TRANSITION, ret);
+ return ret;
+ }
+
+ /* Switch regulator control to resource now */
+ if (ext_ctrl_flag & (TPS80031_PWR_REQ_INPUT_PREQ2 |
+ TPS80031_PWR_REQ_INPUT_PREQ3)) {
+ ret = tps80031_update(dev, TPS80031_SLAVE_ID1, state_reg_add,
+ 0x0, TPS80031_STATE_MASK);
+ if (ret < 0)
+ dev_err(dev, "reg 0x%02x update failed, err = %d\n",
+ state_reg_add, ret);
+ } else {
+ ret = tps80031_update(dev, TPS80031_SLAVE_ID1, trans_reg_add,
+ TPS80031_TRANS_SLEEP_OFF,
+ TPS80031_TRANS_SLEEP_MASK);
+ if (ret < 0)
+ dev_err(dev, "reg 0x%02x update failed, err = %d\n",
+ trans_reg_add, ret);
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tps80031_ext_power_req_config);
+
+static void tps80031_power_off(void)
+{
+ dev_info(tps80031_power_off_dev->dev, "switching off PMU\n");
+ tps80031_write(tps80031_power_off_dev->dev, TPS80031_SLAVE_ID1,
+ TPS80031_PHOENIX_DEV_ON, TPS80031_DEVOFF);
+}
+
+static void tps80031_pupd_init(struct tps80031 *tps80031,
+ struct tps80031_platform_data *pdata)
+{
+ struct tps80031_pupd_init_data *pupd_init_data = pdata->pupd_init_data;
+ int data_size = pdata->pupd_init_data_size;
+ int i;
+
+ for (i = 0; i < data_size; ++i) {
+ struct tps80031_pupd_init_data *pupd_init = &pupd_init_data[i];
+ const struct tps80031_pupd_data *pupd =
+ &tps80031_pupds[pupd_init->input_pin];
+ u8 update_value = 0;
+ u8 update_mask = pupd->pulldown_bit | pupd->pullup_bit;
+
+ if (pupd_init->setting == TPS80031_PUPD_PULLDOWN)
+ update_value = pupd->pulldown_bit;
+ else if (pupd_init->setting == TPS80031_PUPD_PULLUP)
+ update_value = pupd->pullup_bit;
+
+ tps80031_update(tps80031->dev, TPS80031_SLAVE_ID1, pupd->reg,
+ update_value, update_mask);
+ }
+}
+
+static int tps80031_init_ext_control(struct tps80031 *tps80031,
+ struct tps80031_platform_data *pdata)
+{
+ struct device *dev = tps80031->dev;
+ int ret;
+ int i;
+
+ /* Clear all external control for this rail */
+ for (i = 0; i < 9; ++i) {
+ ret = tps80031_write(dev, TPS80031_SLAVE_ID1,
+ TPS80031_PREQ1_RES_ASS_A + i, 0);
+ if (ret < 0) {
+ dev_err(dev, "reg 0x%02x write failed, err = %d\n",
+ TPS80031_PREQ1_RES_ASS_A + i, ret);
+ return ret;
+ }
+ }
+
+ /* Mask the PREQ */
+ ret = tps80031_set_bits(dev, TPS80031_SLAVE_ID1,
+ TPS80031_PHOENIX_MSK_TRANSITION, 0x7 << 5);
+ if (ret < 0) {
+ dev_err(dev, "reg 0x%02x set_bits failed, err = %d\n",
+ TPS80031_PHOENIX_MSK_TRANSITION, ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int tps80031_irq_init(struct tps80031 *tps80031, int irq, int irq_base)
+{
+ struct device *dev = tps80031->dev;
+ int i, ret;
+
+ /*
+ * The MASK register used for updating status register when
+ * interrupt occurs and LINE register used to pass the status
+ * to actual interrupt line. As per datasheet:
+ * When INT_MSK_LINE [i] is set to 1, the associated interrupt
+ * number i is INT line masked, which means that no interrupt is
+ * generated on the INT line.
+ * When INT_MSK_LINE [i] is set to 0, the associated interrupt
+ * number i is line enabled: An interrupt is generated on the
+ * INT line.
+ * In any case, the INT_STS [i] status bit may or may not be updated,
+ * only linked to the INT_MSK_STS [i] configuration register bit.
+ *
+ * When INT_MSK_STS [i] is set to 1, the associated interrupt number
+ * i is status masked, which means that no interrupt is stored in
+ * the INT_STS[i] status bit. Note that no interrupt number i is
+ * generated on the INT line, even if the INT_MSK_LINE [i] register
+ * bit is set to 0.
+ * When INT_MSK_STS [i] is set to 0, the associated interrupt number i
+ * is status enabled: An interrupt status is updated in the INT_STS [i]
+ * register. The interrupt may or may not be generated on the INT line,
+ * depending on the INT_MSK_LINE [i] configuration register bit.
+ */
+ for (i = 0; i < 3; i++)
+ tps80031_write(dev, TPS80031_SLAVE_ID2,
+ TPS80031_INT_MSK_STS_A + i, 0x00);
+
+ ret = regmap_add_irq_chip(tps80031->regmap[TPS80031_SLAVE_ID2], irq,
+ IRQF_ONESHOT, irq_base,
+ &tps80031_irq_chip, &tps80031->irq_data);
+ if (ret < 0) {
+ dev_err(dev, "add irq failed, err = %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static bool rd_wr_reg_id0(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TPS80031_SMPS1_CFG_FORCE ... TPS80031_SMPS2_CFG_VOLTAGE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rd_wr_reg_id1(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TPS80031_SECONDS_REG ... TPS80031_RTC_RESET_STATUS_REG:
+ case TPS80031_VALIDITY0 ... TPS80031_VALIDITY7:
+ case TPS80031_PHOENIX_START_CONDITION ... TPS80031_KEY_PRESS_DUR_CFG:
+ case TPS80031_SMPS4_CFG_TRANS ... TPS80031_SMPS3_CFG_VOLTAGE:
+ case TPS80031_BROADCAST_ADDR_ALL ... TPS80031_BROADCAST_ADDR_CLK_RST:
+ case TPS80031_VANA_CFG_TRANS ... TPS80031_LDO7_CFG_VOLTAGE:
+ case TPS80031_REGEN1_CFG_TRANS ... TPS80031_TMP_CFG_STATE:
+ case TPS80031_PREQ1_RES_ASS_A ... TPS80031_PREQ3_RES_ASS_C:
+ case TPS80031_SMPS_OFFSET ... TPS80031_BATDEBOUNCING:
+ case TPS80031_CFG_INPUT_PUPD1 ... TPS80031_CFG_SMPS_PD:
+ case TPS80031_BACKUP_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool is_volatile_reg_id1(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TPS80031_SMPS4_CFG_TRANS ... TPS80031_SMPS3_CFG_VOLTAGE:
+ case TPS80031_VANA_CFG_TRANS ... TPS80031_LDO7_CFG_VOLTAGE:
+ case TPS80031_REGEN1_CFG_TRANS ... TPS80031_TMP_CFG_STATE:
+ case TPS80031_PREQ1_RES_ASS_A ... TPS80031_PREQ3_RES_ASS_C:
+ case TPS80031_SMPS_OFFSET ... TPS80031_BATDEBOUNCING:
+ case TPS80031_CFG_INPUT_PUPD1 ... TPS80031_CFG_SMPS_PD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rd_wr_reg_id2(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TPS80031_USB_VENDOR_ID_LSB ... TPS80031_USB_OTG_REVISION:
+ case TPS80031_GPADC_CTRL ... TPS80031_CTRL_P1:
+ case TPS80031_RTCH0_LSB ... TPS80031_GPCH0_MSB:
+ case TPS80031_TOGGLE1 ... TPS80031_VIBMODE:
+ case TPS80031_PWM1ON ... TPS80031_PWM2OFF:
+ case TPS80031_FG_REG_00 ... TPS80031_FG_REG_11:
+ case TPS80031_INT_STS_A ... TPS80031_INT_MSK_STS_C:
+ case TPS80031_CONTROLLER_CTRL2 ... TPS80031_LED_PWM_CTRL2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rd_wr_reg_id3(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TPS80031_GPADC_TRIM0 ... TPS80031_GPADC_TRIM18:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config tps80031_regmap_configs[] = {
+ {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = rd_wr_reg_id0,
+ .readable_reg = rd_wr_reg_id0,
+ .max_register = TPS80031_MAX_REGISTER,
+ },
+ {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = rd_wr_reg_id1,
+ .readable_reg = rd_wr_reg_id1,
+ .volatile_reg = is_volatile_reg_id1,
+ .max_register = TPS80031_MAX_REGISTER,
+ },
+ {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = rd_wr_reg_id2,
+ .readable_reg = rd_wr_reg_id2,
+ .max_register = TPS80031_MAX_REGISTER,
+ },
+ {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = rd_wr_reg_id3,
+ .readable_reg = rd_wr_reg_id3,
+ .max_register = TPS80031_MAX_REGISTER,
+ },
+};
+
+static int tps80031_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tps80031_platform_data *pdata = dev_get_platdata(&client->dev);
+ struct tps80031 *tps80031;
+ int ret;
+ uint8_t es_version;
+ uint8_t ep_ver;
+ int i;
+
+ if (!pdata) {
+ dev_err(&client->dev, "tps80031 requires platform data\n");
+ return -EINVAL;
+ }
+
+ tps80031 = devm_kzalloc(&client->dev, sizeof(*tps80031), GFP_KERNEL);
+ if (!tps80031) {
+ dev_err(&client->dev, "Malloc failed for tps80031\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < TPS80031_NUM_SLAVES; i++) {
+ if (tps80031_slave_address[i] == client->addr)
+ tps80031->clients[i] = client;
+ else
+ tps80031->clients[i] = i2c_new_dummy(client->adapter,
+ tps80031_slave_address[i]);
+ if (!tps80031->clients[i]) {
+ dev_err(&client->dev, "can't attach client %d\n", i);
+ ret = -ENOMEM;
+ goto fail_client_reg;
+ }
+
+ i2c_set_clientdata(tps80031->clients[i], tps80031);
+ tps80031->regmap[i] = devm_regmap_init_i2c(tps80031->clients[i],
+ &tps80031_regmap_configs[i]);
+ if (IS_ERR(tps80031->regmap[i])) {
+ ret = PTR_ERR(tps80031->regmap[i]);
+ dev_err(&client->dev,
+ "regmap %d init failed, err %d\n", i, ret);
+ goto fail_client_reg;
+ }
+ }
+
+ ret = tps80031_read(&client->dev, TPS80031_SLAVE_ID3,
+ TPS80031_JTAGVERNUM, &es_version);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Silicon version number read failed: %d\n", ret);
+ goto fail_client_reg;
+ }
+
+ ret = tps80031_read(&client->dev, TPS80031_SLAVE_ID3,
+ TPS80031_EPROM_REV, &ep_ver);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Silicon eeprom version read failed: %d\n", ret);
+ goto fail_client_reg;
+ }
+
+ dev_info(&client->dev, "ES version 0x%02x and EPROM version 0x%02x\n",
+ es_version, ep_ver);
+ tps80031->es_version = es_version;
+ tps80031->dev = &client->dev;
+ i2c_set_clientdata(client, tps80031);
+ tps80031->chip_info = id->driver_data;
+
+ ret = tps80031_irq_init(tps80031, client->irq, pdata->irq_base);
+ if (ret) {
+ dev_err(&client->dev, "IRQ init failed: %d\n", ret);
+ goto fail_client_reg;
+ }
+
+ tps80031_pupd_init(tps80031, pdata);
+
+ tps80031_init_ext_control(tps80031, pdata);
+
+ ret = mfd_add_devices(tps80031->dev, -1,
+ tps80031_cell, ARRAY_SIZE(tps80031_cell),
+ NULL, 0,
+ regmap_irq_get_domain(tps80031->irq_data));
+ if (ret < 0) {
+ dev_err(&client->dev, "mfd_add_devices failed: %d\n", ret);
+ goto fail_mfd_add;
+ }
+
+ if (pdata->use_power_off && !pm_power_off) {
+ tps80031_power_off_dev = tps80031;
+ pm_power_off = tps80031_power_off;
+ }
+ return 0;
+
+fail_mfd_add:
+ regmap_del_irq_chip(client->irq, tps80031->irq_data);
+
+fail_client_reg:
+ for (i = 0; i < TPS80031_NUM_SLAVES; i++) {
+ if (tps80031->clients[i] && (tps80031->clients[i] != client))
+ i2c_unregister_device(tps80031->clients[i]);
+ }
+ return ret;
+}
+
+static int tps80031_remove(struct i2c_client *client)
+{
+ struct tps80031 *tps80031 = i2c_get_clientdata(client);
+ int i;
+
+ if (tps80031_power_off_dev == tps80031) {
+ tps80031_power_off_dev = NULL;
+ pm_power_off = NULL;
+ }
+
+ mfd_remove_devices(tps80031->dev);
+
+ regmap_del_irq_chip(client->irq, tps80031->irq_data);
+
+ for (i = 0; i < TPS80031_NUM_SLAVES; i++) {
+ if (tps80031->clients[i] != client)
+ i2c_unregister_device(tps80031->clients[i]);
+ }
+ return 0;
+}
+
+static const struct i2c_device_id tps80031_id_table[] = {
+ { "tps80031", TPS80031 },
+ { "tps80032", TPS80032 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tps80031_id_table);
+
+static struct i2c_driver tps80031_driver = {
+ .driver = {
+ .name = "tps80031",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps80031_probe,
+ .remove = tps80031_remove,
+ .id_table = tps80031_id_table,
+};
+
+static int __init tps80031_init(void)
+{
+ return i2c_add_driver(&tps80031_driver);
+}
+subsys_initcall(tps80031_init);
+
+static void __exit tps80031_exit(void)
+{
+ i2c_del_driver(&tps80031_driver);
+}
+module_exit(tps80031_exit);
+
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_DESCRIPTION("TPS80031 core driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 4ae64232020..db11b4f4061 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -32,6 +32,7 @@
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/device.h>
@@ -46,6 +47,9 @@
#include <linux/i2c.h>
#include <linux/i2c/twl.h>
+/* Register descriptions for audio */
+#include <linux/mfd/twl4030-audio.h>
+
#include "twl-core.h"
/*
@@ -65,19 +69,6 @@
/* Triton Core internal information (BEGIN) */
-/* Last - for index max*/
-#define TWL4030_MODULE_LAST TWL4030_MODULE_SECURED_REG
-
-#define TWL_NUM_SLAVES 4
-
-#define SUB_CHIP_ID0 0
-#define SUB_CHIP_ID1 1
-#define SUB_CHIP_ID2 2
-#define SUB_CHIP_ID3 3
-#define SUB_CHIP_ID_INVAL 0xff
-
-#define TWL_MODULE_LAST TWL4030_MODULE_LAST
-
/* Base Address defns for twl4030_map[] */
/* subchip/slave 0 - USB ID */
@@ -96,10 +87,7 @@
#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_PWM 0x00F8
#define TWL4030_BASEADD_KEYPAD 0x00D2
#define TWL5031_BASEADD_ACCESSORY 0x0074 /* Replaces Main Charge */
@@ -110,7 +98,11 @@
#define TWL4030_BASEADD_BACKUP 0x0014
#define TWL4030_BASEADD_INT 0x002E
#define TWL4030_BASEADD_PM_MASTER 0x0036
+
#define TWL4030_BASEADD_PM_RECEIVER 0x005B
+#define TWL4030_DCDC_GLOBAL_CFG 0x06
+#define SMARTREFLEX_ENABLE BIT(3)
+
#define TWL4030_BASEADD_RTC 0x001C
#define TWL4030_BASEADD_SECURED_REG 0x0000
@@ -119,7 +111,7 @@
/* subchip/slave 0 0x48 - POWER */
#define TWL6030_BASEADD_RTC 0x0000
-#define TWL6030_BASEADD_MEM 0x0017
+#define TWL6030_BASEADD_SECURED_REG 0x0017
#define TWL6030_BASEADD_PM_MASTER 0x001F
#define TWL6030_BASEADD_PM_SLAVE_MISC 0x0030 /* PM_RECEIVER */
#define TWL6030_BASEADD_PM_MISC 0x00E2
@@ -133,7 +125,8 @@
#define TWL6030_BASEADD_GASGAUGE 0x00C0
#define TWL6030_BASEADD_PIH 0x00D0
#define TWL6030_BASEADD_CHARGER 0x00E0
-#define TWL6025_BASEADD_CHARGER 0x00DA
+#define TWL6032_BASEADD_CHARGER 0x00DA
+#define TWL6030_BASEADD_LED 0x00F4
/* subchip/slave 2 0x4A - DFT */
#define TWL6030_BASEADD_DIEID 0x00C0
@@ -155,74 +148,198 @@
/*----------------------------------------------------------------------*/
-/* is driver active, bound to a chip? */
-static bool inuse;
-
-/* TWL IDCODE Register value */
-static u32 twl_idcode;
-
-static unsigned int twl_id;
-unsigned int twl_rev(void)
-{
- return twl_id;
-}
-EXPORT_SYMBOL(twl_rev);
-
/* Structure for each TWL4030/TWL6030 Slave */
struct twl_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;
+ struct regmap *regmap;
};
-static struct twl_client twl_modules[TWL_NUM_SLAVES];
-
/* mapping the module id to slave id and base address */
struct twl_mapping {
unsigned char sid; /* Slave ID */
unsigned char base; /* base address */
};
-static struct twl_mapping *twl_map;
-static struct twl_mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
+struct twl_private {
+ bool ready; /* The core driver is ready to be used */
+ u32 twl_idcode; /* TWL IDCODE Register value */
+ unsigned int twl_id;
+
+ struct twl_mapping *twl_map;
+ struct twl_client *twl_modules;
+};
+
+static struct twl_private *twl_priv;
+
+static struct twl_mapping twl4030_map[] = {
/*
* NOTE: don't change this table without updating the
* <linux/i2c/twl.h> defines for TWL4030_MODULE_*
* so they continue to match the order in this table.
*/
+ /* Common IPs */
{ 0, TWL4030_BASEADD_USB },
+ { 1, TWL4030_BASEADD_PIH },
+ { 2, TWL4030_BASEADD_MAIN_CHARGE },
+ { 3, TWL4030_BASEADD_PM_MASTER },
+ { 3, TWL4030_BASEADD_PM_RECEIVER },
+ { 3, TWL4030_BASEADD_RTC },
+ { 2, TWL4030_BASEADD_PWM },
+ { 2, TWL4030_BASEADD_LED },
+ { 3, TWL4030_BASEADD_SECURED_REG },
+
+ /* TWL4030 specific IPs */
{ 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 },
+
{ 2, TWL5031_BASEADD_ACCESSORY },
{ 2, TWL5031_BASEADD_INTERRUPTS },
+};
- { 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 },
+static struct reg_default twl4030_49_defaults[] = {
+ /* Audio Registers */
+ { 0x01, 0x00}, /* CODEC_MODE */
+ { 0x02, 0x00}, /* OPTION */
+ /* 0x03 Unused */
+ { 0x04, 0x00}, /* MICBIAS_CTL */
+ { 0x05, 0x00}, /* ANAMICL */
+ { 0x06, 0x00}, /* ANAMICR */
+ { 0x07, 0x00}, /* AVADC_CTL */
+ { 0x08, 0x00}, /* ADCMICSEL */
+ { 0x09, 0x00}, /* DIGMIXING */
+ { 0x0a, 0x0f}, /* ATXL1PGA */
+ { 0x0b, 0x0f}, /* ATXR1PGA */
+ { 0x0c, 0x0f}, /* AVTXL2PGA */
+ { 0x0d, 0x0f}, /* AVTXR2PGA */
+ { 0x0e, 0x00}, /* AUDIO_IF */
+ { 0x0f, 0x00}, /* VOICE_IF */
+ { 0x10, 0x3f}, /* ARXR1PGA */
+ { 0x11, 0x3f}, /* ARXL1PGA */
+ { 0x12, 0x3f}, /* ARXR2PGA */
+ { 0x13, 0x3f}, /* ARXL2PGA */
+ { 0x14, 0x25}, /* VRXPGA */
+ { 0x15, 0x00}, /* VSTPGA */
+ { 0x16, 0x00}, /* VRX2ARXPGA */
+ { 0x17, 0x00}, /* AVDAC_CTL */
+ { 0x18, 0x00}, /* ARX2VTXPGA */
+ { 0x19, 0x32}, /* ARXL1_APGA_CTL*/
+ { 0x1a, 0x32}, /* ARXR1_APGA_CTL*/
+ { 0x1b, 0x32}, /* ARXL2_APGA_CTL*/
+ { 0x1c, 0x32}, /* ARXR2_APGA_CTL*/
+ { 0x1d, 0x00}, /* ATX2ARXPGA */
+ { 0x1e, 0x00}, /* BT_IF */
+ { 0x1f, 0x55}, /* BTPGA */
+ { 0x20, 0x00}, /* BTSTPGA */
+ { 0x21, 0x00}, /* EAR_CTL */
+ { 0x22, 0x00}, /* HS_SEL */
+ { 0x23, 0x00}, /* HS_GAIN_SET */
+ { 0x24, 0x00}, /* HS_POPN_SET */
+ { 0x25, 0x00}, /* PREDL_CTL */
+ { 0x26, 0x00}, /* PREDR_CTL */
+ { 0x27, 0x00}, /* PRECKL_CTL */
+ { 0x28, 0x00}, /* PRECKR_CTL */
+ { 0x29, 0x00}, /* HFL_CTL */
+ { 0x2a, 0x00}, /* HFR_CTL */
+ { 0x2b, 0x05}, /* ALC_CTL */
+ { 0x2c, 0x00}, /* ALC_SET1 */
+ { 0x2d, 0x00}, /* ALC_SET2 */
+ { 0x2e, 0x00}, /* BOOST_CTL */
+ { 0x2f, 0x00}, /* SOFTVOL_CTL */
+ { 0x30, 0x13}, /* DTMF_FREQSEL */
+ { 0x31, 0x00}, /* DTMF_TONEXT1H */
+ { 0x32, 0x00}, /* DTMF_TONEXT1L */
+ { 0x33, 0x00}, /* DTMF_TONEXT2H */
+ { 0x34, 0x00}, /* DTMF_TONEXT2L */
+ { 0x35, 0x79}, /* DTMF_TONOFF */
+ { 0x36, 0x11}, /* DTMF_WANONOFF */
+ { 0x37, 0x00}, /* I2S_RX_SCRAMBLE_H */
+ { 0x38, 0x00}, /* I2S_RX_SCRAMBLE_M */
+ { 0x39, 0x00}, /* I2S_RX_SCRAMBLE_L */
+ { 0x3a, 0x06}, /* APLL_CTL */
+ { 0x3b, 0x00}, /* DTMF_CTL */
+ { 0x3c, 0x44}, /* DTMF_PGA_CTL2 (0x3C) */
+ { 0x3d, 0x69}, /* DTMF_PGA_CTL1 (0x3D) */
+ { 0x3e, 0x00}, /* MISC_SET_1 */
+ { 0x3f, 0x00}, /* PCMBTMUX */
+ /* 0x40 - 0x42 Unused */
+ { 0x43, 0x00}, /* RX_PATH_SEL */
+ { 0x44, 0x32}, /* VDL_APGA_CTL */
+ { 0x45, 0x00}, /* VIBRA_CTL */
+ { 0x46, 0x00}, /* VIBRA_SET */
+ { 0x47, 0x00}, /* VIBRA_PWM_SET */
+ { 0x48, 0x00}, /* ANAMIC_GAIN */
+ { 0x49, 0x00}, /* MISC_SET_2 */
+ /* End of Audio Registers */
+};
+
+static bool twl4030_49_nop_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00:
+ case 0x03:
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_range twl4030_49_volatile_ranges[] = {
+ regmap_reg_range(TWL4030_BASEADD_TEST, 0xff),
+};
+
+static const struct regmap_access_table twl4030_49_volatile_table = {
+ .yes_ranges = twl4030_49_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(twl4030_49_volatile_ranges),
+};
+
+static struct regmap_config twl4030_regmap_config[4] = {
+ {
+ /* Address 0x48 */
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+ },
+ {
+ /* Address 0x49 */
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+
+ .readable_reg = twl4030_49_nop_reg,
+ .writeable_reg = twl4030_49_nop_reg,
+
+ .volatile_table = &twl4030_49_volatile_table,
+
+ .reg_defaults = twl4030_49_defaults,
+ .num_reg_defaults = ARRAY_SIZE(twl4030_49_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ },
+ {
+ /* Address 0x4a */
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+ },
+ {
+ /* Address 0x4b */
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+ },
};
static struct twl_mapping twl6030_map[] = {
@@ -231,41 +348,100 @@ static struct twl_mapping twl6030_map[] = {
* <linux/i2c/twl.h> defines for TWL4030_MODULE_*
* so they continue to match the order in this table.
*/
- { SUB_CHIP_ID1, TWL6030_BASEADD_USB },
- { SUB_CHIP_ID_INVAL, TWL6030_BASEADD_AUDIO },
- { SUB_CHIP_ID2, TWL6030_BASEADD_DIEID },
- { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
- { SUB_CHIP_ID1, TWL6030_BASEADD_PIH },
-
- { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
- { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
- { SUB_CHIP_ID1, TWL6030_BASEADD_GPADC_CTRL },
- { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
- { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
-
- { SUB_CHIP_ID1, TWL6030_BASEADD_CHARGER },
- { SUB_CHIP_ID1, TWL6030_BASEADD_GASGAUGE },
- { SUB_CHIP_ID1, TWL6030_BASEADD_PWM },
- { SUB_CHIP_ID0, TWL6030_BASEADD_ZERO },
- { SUB_CHIP_ID1, TWL6030_BASEADD_ZERO },
-
- { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
- { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
- { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
- { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
- { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
- { SUB_CHIP_ID0, TWL6030_BASEADD_PM_MASTER },
- { SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_MISC },
-
- { SUB_CHIP_ID0, TWL6030_BASEADD_RTC },
- { SUB_CHIP_ID0, TWL6030_BASEADD_MEM },
- { SUB_CHIP_ID1, TWL6025_BASEADD_CHARGER },
+
+ /* Common IPs */
+ { 1, TWL6030_BASEADD_USB },
+ { 1, TWL6030_BASEADD_PIH },
+ { 1, TWL6030_BASEADD_CHARGER },
+ { 0, TWL6030_BASEADD_PM_MASTER },
+ { 0, TWL6030_BASEADD_PM_SLAVE_MISC },
+
+ { 0, TWL6030_BASEADD_RTC },
+ { 1, TWL6030_BASEADD_PWM },
+ { 1, TWL6030_BASEADD_LED },
+ { 0, TWL6030_BASEADD_SECURED_REG },
+
+ /* TWL6030 specific IPs */
+ { 0, TWL6030_BASEADD_ZERO },
+ { 1, TWL6030_BASEADD_ZERO },
+ { 2, TWL6030_BASEADD_ZERO },
+ { 1, TWL6030_BASEADD_GPADC_CTRL },
+ { 1, TWL6030_BASEADD_GASGAUGE },
+};
+
+static struct regmap_config twl6030_regmap_config[3] = {
+ {
+ /* Address 0x48 */
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+ },
+ {
+ /* Address 0x49 */
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+ },
+ {
+ /* Address 0x4a */
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+ },
};
/*----------------------------------------------------------------------*/
+static inline int twl_get_num_slaves(void)
+{
+ if (twl_class_is_4030())
+ return 4; /* TWL4030 class have four slave address */
+ else
+ return 3; /* TWL6030 class have three slave address */
+}
+
+static inline int twl_get_last_module(void)
+{
+ if (twl_class_is_4030())
+ return TWL4030_MODULE_LAST;
+ else
+ return TWL6030_MODULE_LAST;
+}
+
/* Exported Functions */
+unsigned int twl_rev(void)
+{
+ return twl_priv ? twl_priv->twl_id : 0;
+}
+EXPORT_SYMBOL(twl_rev);
+
+/**
+ * twl_get_regmap - Get the regmap associated with the given module
+ * @mod_no: module number
+ *
+ * Returns the regmap pointer or NULL in case of failure.
+ */
+static struct regmap *twl_get_regmap(u8 mod_no)
+{
+ int sid;
+ struct twl_client *twl;
+
+ if (unlikely(!twl_priv || !twl_priv->ready)) {
+ pr_err("%s: not initialized\n", DRIVER_NAME);
+ return NULL;
+ }
+ if (unlikely(mod_no >= twl_get_last_module())) {
+ pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
+ return NULL;
+ }
+
+ sid = twl_priv->twl_map[mod_no].sid;
+ twl = &twl_priv->twl_modules[sid];
+
+ return twl->regmap;
+}
+
/**
* twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0
* @mod_no: module number
@@ -273,60 +449,24 @@ static struct twl_mapping twl6030_map[] = {
* @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 twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
{
+ struct regmap *regmap = twl_get_regmap(mod_no);
int ret;
- int sid;
- struct twl_client *twl;
- struct i2c_msg *msg;
- if (unlikely(mod_no > TWL_MODULE_LAST)) {
- pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
- return -EPERM;
- }
- if (unlikely(!inuse)) {
- pr_err("%s: not initialized\n", DRIVER_NAME);
+ if (!regmap)
return -EPERM;
- }
- sid = twl_map[mod_no].sid;
- if (unlikely(sid == SUB_CHIP_ID_INVAL)) {
- pr_err("%s: module %d is not part of the pmic\n",
- DRIVER_NAME, mod_no);
- return -EINVAL;
- }
- twl = &twl_modules[sid];
- 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 = twl_map[mod_no].base + reg;
- ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 1);
- mutex_unlock(&twl->xfer_lock);
-
- /* i2c_transfer returns number of messages transferred */
- if (ret != 1) {
- pr_err("%s: i2c_write failed to transfer all messages\n",
- DRIVER_NAME);
- if (ret < 0)
- return ret;
- else
- return -EIO;
- } else {
- return 0;
- }
+ ret = regmap_bulk_write(regmap, twl_priv->twl_map[mod_no].base + reg,
+ value, num_bytes);
+
+ if (ret)
+ pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n",
+ DRIVER_NAME, mod_no, reg, num_bytes);
+
+ return ret;
}
EXPORT_SYMBOL(twl_i2c_write);
@@ -341,91 +481,43 @@ EXPORT_SYMBOL(twl_i2c_write);
*/
int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
{
+ struct regmap *regmap = twl_get_regmap(mod_no);
int ret;
- u8 val;
- int sid;
- struct twl_client *twl;
- struct i2c_msg *msg;
- if (unlikely(mod_no > TWL_MODULE_LAST)) {
- pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
- return -EPERM;
- }
- if (unlikely(!inuse)) {
- pr_err("%s: not initialized\n", DRIVER_NAME);
+ if (!regmap)
return -EPERM;
- }
- sid = twl_map[mod_no].sid;
- if (unlikely(sid == SUB_CHIP_ID_INVAL)) {
- pr_err("%s: module %d is not part of the pmic\n",
- DRIVER_NAME, mod_no);
- return -EINVAL;
- }
- twl = &twl_modules[sid];
-
- 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 = twl_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);
-
- /* i2c_transfer returns number of messages transferred */
- if (ret != 2) {
- pr_err("%s: i2c_read failed to transfer all messages\n",
- DRIVER_NAME);
- if (ret < 0)
- return ret;
- else
- return -EIO;
- } else {
- return 0;
- }
+
+ ret = regmap_bulk_read(regmap, twl_priv->twl_map[mod_no].base + reg,
+ value, num_bytes);
+
+ if (ret)
+ pr_err("%s: Read failed (mod %d, reg 0x%02x count %d)\n",
+ DRIVER_NAME, mod_no, reg, num_bytes);
+
+ return ret;
}
EXPORT_SYMBOL(twl_i2c_read);
/**
- * twl_i2c_write_u8 - Writes a 8 bit register in TWL4030/TWL5030/TWL60X0
+ * twl_regcache_bypass - Configure the regcache bypass for the regmap associated
+ * with the module
* @mod_no: module number
- * @value: the value to be written 8 bit
- * @reg: register address (just offset will do)
+ * @enable: Regcache bypass state
*
- * Returns result of operation - 0 is success
+ * Returns 0 else failure.
*/
-int twl_i2c_write_u8(u8 mod_no, u8 value, u8 reg)
+int twl_set_regcache_bypass(u8 mod_no, bool enable)
{
+ struct regmap *regmap = twl_get_regmap(mod_no);
- /* 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 twl_i2c_write(mod_no, temp_buffer, reg, 1);
-}
-EXPORT_SYMBOL(twl_i2c_write_u8);
+ if (!regmap)
+ return -EPERM;
-/**
- * twl_i2c_read_u8 - Reads a 8 bit register from TWL4030/TWL5030/TWL60X0
- * @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 twl_i2c_read_u8(u8 mod_no, u8 *value, u8 reg)
-{
- return twl_i2c_read(mod_no, value, reg, 1);
+ regcache_cache_bypass(regmap, enable);
+
+ return 0;
}
-EXPORT_SYMBOL(twl_i2c_read_u8);
+EXPORT_SYMBOL(twl_set_regcache_bypass);
/*----------------------------------------------------------------------*/
@@ -445,7 +537,7 @@ static int twl_read_idcode_register(void)
goto fail;
}
- err = twl_i2c_read(TWL4030_MODULE_INTBR, (u8 *)(&twl_idcode),
+ err = twl_i2c_read(TWL4030_MODULE_INTBR, (u8 *)(&twl_priv->twl_idcode),
REG_IDCODE_7_0, 4);
if (err) {
pr_err("TWL4030: unable to read IDCODE -%d\n", err);
@@ -466,7 +558,7 @@ fail:
*/
int twl_get_type(void)
{
- return TWL_SIL_TYPE(twl_idcode);
+ return TWL_SIL_TYPE(twl_priv->twl_idcode);
}
EXPORT_SYMBOL_GPL(twl_get_type);
@@ -477,7 +569,7 @@ EXPORT_SYMBOL_GPL(twl_get_type);
*/
int twl_get_version(void)
{
- return TWL_SIL_REV(twl_idcode);
+ return TWL_SIL_REV(twl_priv->twl_idcode);
}
EXPORT_SYMBOL_GPL(twl_get_version);
@@ -514,13 +606,20 @@ int twl_get_hfclk_rate(void)
EXPORT_SYMBOL_GPL(twl_get_hfclk_rate);
static struct device *
-add_numbered_child(unsigned chip, const char *name, int num,
+add_numbered_child(unsigned mod_no, const char *name, int num,
void *pdata, unsigned pdata_len,
bool can_wakeup, int irq0, int irq1)
{
struct platform_device *pdev;
- struct twl_client *twl = &twl_modules[chip];
- int status;
+ struct twl_client *twl;
+ int status, sid;
+
+ if (unlikely(mod_no >= twl_get_last_module())) {
+ pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
+ return ERR_PTR(-EPERM);
+ }
+ sid = twl_priv->twl_map[mod_no].sid;
+ twl = &twl_priv->twl_modules[sid];
pdev = platform_device_alloc(name, num);
if (!pdev) {
@@ -565,11 +664,11 @@ err:
return &pdev->dev;
}
-static inline struct device *add_child(unsigned chip, const char *name,
+static inline struct device *add_child(unsigned mod_no, const char *name,
void *pdata, unsigned pdata_len,
bool can_wakeup, int irq0, int irq1)
{
- return add_numbered_child(chip, name, -1, pdata, pdata_len,
+ return add_numbered_child(mod_no, name, -1, pdata, pdata_len,
can_wakeup, irq0, irq1);
}
@@ -578,7 +677,6 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
struct regulator_consumer_supply *consumers,
unsigned num_consumers, unsigned long features)
{
- unsigned sub_chip_id;
struct twl_regulator_driver_data drv_data;
/* regulator framework demands init_data ... */
@@ -605,8 +703,7 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
}
/* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */
- sub_chip_id = twl_map[TWL_MODULE_PM_MASTER].sid;
- return add_numbered_child(sub_chip_id, "twl_reg", num,
+ return add_numbered_child(TWL_MODULE_PM_MASTER, "twl_reg", num,
pdata, sizeof(*pdata), false, 0, 0);
}
@@ -628,10 +725,9 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
unsigned long features)
{
struct device *child;
- unsigned sub_chip_id;
if (IS_ENABLED(CONFIG_GPIO_TWL4030) && pdata->gpio) {
- child = add_child(SUB_CHIP_ID1, "twl4030_gpio",
+ child = add_child(TWL4030_MODULE_GPIO, "twl4030_gpio",
pdata->gpio, sizeof(*pdata->gpio),
false, irq_base + GPIO_INTR_OFFSET, 0);
if (IS_ERR(child))
@@ -639,15 +735,16 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
}
if (IS_ENABLED(CONFIG_KEYBOARD_TWL4030) && pdata->keypad) {
- child = add_child(SUB_CHIP_ID2, "twl4030_keypad",
+ child = add_child(TWL4030_MODULE_KEYPAD, "twl4030_keypad",
pdata->keypad, sizeof(*pdata->keypad),
true, irq_base + KEYPAD_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
- if (IS_ENABLED(CONFIG_TWL4030_MADC) && pdata->madc) {
- child = add_child(2, "twl4030_madc",
+ if (IS_ENABLED(CONFIG_TWL4030_MADC) && pdata->madc &&
+ twl_class_is_4030()) {
+ child = add_child(TWL4030_MODULE_MADC, "twl4030_madc",
pdata->madc, sizeof(*pdata->madc),
true, irq_base + MADC_INTR_OFFSET, 0);
if (IS_ERR(child))
@@ -662,16 +759,21 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
* Eventually, Linux might become more aware of such
* HW security concerns, and "least privilege".
*/
- sub_chip_id = twl_map[TWL_MODULE_RTC].sid;
- child = add_child(sub_chip_id, "twl_rtc",
- NULL, 0,
+ child = add_child(TWL_MODULE_RTC, "twl_rtc", NULL, 0,
true, irq_base + RTC_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
- if (IS_ENABLED(CONFIG_PWM_TWL6030) && twl_class_is_6030()) {
- child = add_child(TWL6030_MODULE_ID1, "twl6030-pwm", NULL, 0,
+ if (IS_ENABLED(CONFIG_PWM_TWL)) {
+ child = add_child(TWL_MODULE_PWM, "twl-pwm", NULL, 0,
+ false, 0, 0);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+
+ if (IS_ENABLED(CONFIG_PWM_TWL_LED)) {
+ child = add_child(TWL_MODULE_LED, "twl-pwmled", NULL, 0,
false, 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
@@ -723,9 +825,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
}
- child = add_child(0, "twl4030_usb",
- pdata->usb, sizeof(*pdata->usb),
- true,
+ child = add_child(TWL_MODULE_USB, "twl4030_usb",
+ pdata->usb, sizeof(*pdata->usb), true,
/* irq0 = USB_PRES, irq1 = USB */
irq_base + USB_PRES_INTR_OFFSET,
irq_base + USB_INTR_OFFSET);
@@ -740,81 +841,24 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
usb3v1[0].dev_name = dev_name(child);
}
}
- if (IS_ENABLED(CONFIG_TWL6030_USB) && pdata->usb &&
- twl_class_is_6030()) {
-
- static struct regulator_consumer_supply usb3v3;
- int regulator;
-
- if (IS_ENABLED(CONFIG_REGULATOR_TWL4030)) {
- /* this is a template that gets copied */
- struct regulator_init_data usb_fixed = {
- .constraints.valid_modes_mask =
- REGULATOR_MODE_NORMAL
- | REGULATOR_MODE_STANDBY,
- .constraints.valid_ops_mask =
- REGULATOR_CHANGE_MODE
- | REGULATOR_CHANGE_STATUS,
- };
-
- if (features & TWL6025_SUBCLASS) {
- usb3v3.supply = "ldousb";
- regulator = TWL6025_REG_LDOUSB;
- } else {
- usb3v3.supply = "vusb";
- regulator = TWL6030_REG_VUSB;
- }
- child = add_regulator_linked(regulator, &usb_fixed,
- &usb3v3, 1,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
- }
-
- pdata->usb->features = features;
-
- child = add_child(0, "twl6030_usb",
- pdata->usb, sizeof(*pdata->usb),
- true,
- /* irq1 = VBUS_PRES, irq0 = USB ID */
- irq_base + USBOTG_INTR_OFFSET,
- irq_base + USB_PRES_INTR_OFFSET);
-
- if (IS_ERR(child))
- return PTR_ERR(child);
- /* we need to connect regulators to this transceiver */
- if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && child)
- usb3v3.dev_name = dev_name(child);
- } else if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) &&
- twl_class_is_6030()) {
- if (features & TWL6025_SUBCLASS)
- child = add_regulator(TWL6025_REG_LDOUSB,
- pdata->ldousb, features);
- else
- child = add_regulator(TWL6030_REG_VUSB,
- pdata->vusb, features);
-
- if (IS_ERR(child))
- return PTR_ERR(child);
- }
if (IS_ENABLED(CONFIG_TWL4030_WATCHDOG) && twl_class_is_4030()) {
- child = add_child(0, "twl4030_wdt", NULL, 0, false, 0, 0);
+ child = add_child(TWL_MODULE_PM_RECEIVER, "twl4030_wdt", NULL,
+ 0, false, 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (IS_ENABLED(CONFIG_INPUT_TWL4030_PWRBUTTON) && twl_class_is_4030()) {
- child = add_child(1, "twl4030_pwrbutton",
- NULL, 0, true, irq_base + 8 + 0, 0);
+ child = add_child(TWL_MODULE_PM_MASTER, "twl4030_pwrbutton",
+ NULL, 0, true, irq_base + 8 + 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (IS_ENABLED(CONFIG_MFD_TWL4030_AUDIO) && pdata->audio &&
twl_class_is_4030()) {
- sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
- child = add_child(sub_chip_id, "twl4030-audio",
+ child = add_child(TWL4030_MODULE_AUDIO_VOICE, "twl4030-audio",
pdata->audio, sizeof(*pdata->audio),
false, 0, 0);
if (IS_ERR(child))
@@ -910,151 +954,9 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
return PTR_ERR(child);
}
- /* twl6030 regulators */
- if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && twl_class_is_6030() &&
- !(features & TWL6025_SUBCLASS)) {
- child = add_regulator(TWL6030_REG_VDD1, pdata->vdd1,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6030_REG_VDD2, pdata->vdd2,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6030_REG_VDD3, pdata->vdd3,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6030_REG_V1V8, pdata->v1v8,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6030_REG_V2V1, pdata->v2v1,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6030_REG_VMMC, pdata->vmmc,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6030_REG_VPP, pdata->vpp,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6030_REG_VUSIM, pdata->vusim,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6030_REG_VCXIO, pdata->vcxio,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6030_REG_VDAC, pdata->vdac,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6030_REG_VAUX1_6030, pdata->vaux1,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6030_REG_VAUX2_6030, pdata->vaux2,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6030_REG_VAUX3_6030, pdata->vaux3,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6030_REG_CLK32KG, pdata->clk32kg,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
- }
-
- /* 6030 and 6025 share this regulator */
- if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && twl_class_is_6030()) {
- child = add_regulator(TWL6030_REG_VANA, pdata->vana,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
- }
-
- /* twl6025 regulators */
- if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && twl_class_is_6030() &&
- (features & TWL6025_SUBCLASS)) {
- child = add_regulator(TWL6025_REG_LDO5, pdata->ldo5,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6025_REG_LDO1, pdata->ldo1,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6025_REG_LDO7, pdata->ldo7,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6025_REG_LDO6, pdata->ldo6,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6025_REG_LDOLN, pdata->ldoln,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6025_REG_LDO2, pdata->ldo2,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6025_REG_LDO4, pdata->ldo4,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6025_REG_LDO3, pdata->ldo3,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6025_REG_SMPS3, pdata->smps3,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6025_REG_SMPS4, pdata->smps4,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- child = add_regulator(TWL6025_REG_VIO, pdata->vio6025,
- features);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
- }
-
if (IS_ENABLED(CONFIG_CHARGER_TWL4030) && pdata->bci &&
!(features & (TPS_SUBSET | TWL5031))) {
- child = add_child(3, "twl4030_bci",
+ child = add_child(TWL_MODULE_MAIN_CHARGE, "twl4030_bci",
pdata->bci, sizeof(*pdata->bci), false,
/* irq0 = CHG_PRES, irq1 = BCI */
irq_base + BCI_PRES_INTR_OFFSET,
@@ -1063,6 +965,14 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
return PTR_ERR(child);
}
+ if (IS_ENABLED(CONFIG_TWL4030_POWER) && pdata->power) {
+ child = add_child(TWL_MODULE_PM_MASTER, "twl4030_power",
+ pdata->power, sizeof(*pdata->power), false,
+ 0, 0);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+
return 0;
}
@@ -1077,8 +987,8 @@ static inline int __init protect_pm_master(void)
{
int e = 0;
- e = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
- TWL4030_PM_MASTER_PROTECT_KEY);
+ e = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0,
+ TWL4030_PM_MASTER_PROTECT_KEY);
return e;
}
@@ -1086,12 +996,10 @@ static inline int __init unprotect_pm_master(void)
{
int e = 0;
- e |= twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
- TWL4030_PM_MASTER_KEY_CFG1,
- TWL4030_PM_MASTER_PROTECT_KEY);
- e |= twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
- TWL4030_PM_MASTER_KEY_CFG2,
- TWL4030_PM_MASTER_PROTECT_KEY);
+ e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1,
+ TWL4030_PM_MASTER_PROTECT_KEY);
+ e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2,
+ TWL4030_PM_MASTER_PROTECT_KEY);
return e;
}
@@ -1147,39 +1055,54 @@ static int twl_remove(struct i2c_client *client)
unsigned i, num_slaves;
int status;
- if (twl_class_is_4030()) {
+ if (twl_class_is_4030())
status = twl4030_exit_irq();
- num_slaves = TWL_NUM_SLAVES;
- } else {
+ else
status = twl6030_exit_irq();
- num_slaves = TWL_NUM_SLAVES - 1;
- }
if (status < 0)
return status;
+ num_slaves = twl_get_num_slaves();
for (i = 0; i < num_slaves; i++) {
- struct twl_client *twl = &twl_modules[i];
+ struct twl_client *twl = &twl_priv->twl_modules[i];
if (twl->client && twl->client != client)
i2c_unregister_device(twl->client);
- twl_modules[i].client = NULL;
+ twl->client = NULL;
}
- inuse = false;
+ twl_priv->ready = false;
return 0;
}
+static struct of_dev_auxdata twl_auxdata_lookup[] = {
+ OF_DEV_AUXDATA("ti,twl4030-gpio", 0, "twl4030-gpio", NULL),
+ { /* sentinel */ },
+};
+
/* NOTE: This driver only handles a single twl4030/tps659x0 chip */
-static int __devinit
+static int
twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
- struct twl4030_platform_data *pdata = client->dev.platform_data;
+ struct twl4030_platform_data *pdata = dev_get_platdata(&client->dev);
struct device_node *node = client->dev.of_node;
struct platform_device *pdev;
+ struct regmap_config *twl_regmap_config;
int irq_base = 0;
int status;
unsigned i, num_slaves;
+ if (!node && !pdata) {
+ dev_err(&client->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ if (twl_priv) {
+ dev_dbg(&client->dev, "only one instance of %s allowed\n",
+ DRIVER_NAME);
+ return -EBUSY;
+ }
+
pdev = platform_device_alloc(DRIVER_NAME, -1);
if (!pdev) {
dev_err(&client->dev, "can't alloc pdev\n");
@@ -1192,59 +1115,50 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
return status;
}
- if (node && !pdata) {
- /*
- * XXX: Temporary pdata until the information is correctly
- * retrieved by every TWL modules from DT.
- */
- pdata = devm_kzalloc(&client->dev,
- sizeof(struct twl4030_platform_data),
- GFP_KERNEL);
- if (!pdata) {
- status = -ENOMEM;
- goto free;
- }
- }
-
- if (!pdata) {
- dev_dbg(&client->dev, "no platform data?\n");
- status = -EINVAL;
- goto free;
- }
-
- platform_set_drvdata(pdev, pdata);
-
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
dev_dbg(&client->dev, "can't talk I2C?\n");
status = -EIO;
goto free;
}
- if (inuse) {
- dev_dbg(&client->dev, "driver is already in use\n");
- status = -EBUSY;
+ twl_priv = devm_kzalloc(&client->dev, sizeof(struct twl_private),
+ GFP_KERNEL);
+ if (!twl_priv) {
+ status = -ENOMEM;
goto free;
}
if ((id->driver_data) & TWL6030_CLASS) {
- twl_id = TWL6030_CLASS_ID;
- twl_map = &twl6030_map[0];
- num_slaves = TWL_NUM_SLAVES - 1;
+ twl_priv->twl_id = TWL6030_CLASS_ID;
+ twl_priv->twl_map = &twl6030_map[0];
+ /* The charger base address is different in twl6032 */
+ if ((id->driver_data) & TWL6032_SUBCLASS)
+ twl_priv->twl_map[TWL_MODULE_MAIN_CHARGE].base =
+ TWL6032_BASEADD_CHARGER;
+ twl_regmap_config = twl6030_regmap_config;
} else {
- twl_id = TWL4030_CLASS_ID;
- twl_map = &twl4030_map[0];
- num_slaves = TWL_NUM_SLAVES;
+ twl_priv->twl_id = TWL4030_CLASS_ID;
+ twl_priv->twl_map = &twl4030_map[0];
+ twl_regmap_config = twl4030_regmap_config;
+ }
+
+ num_slaves = twl_get_num_slaves();
+ twl_priv->twl_modules = devm_kzalloc(&client->dev,
+ sizeof(struct twl_client) * num_slaves,
+ GFP_KERNEL);
+ if (!twl_priv->twl_modules) {
+ status = -ENOMEM;
+ goto free;
}
for (i = 0; i < num_slaves; i++) {
- struct twl_client *twl = &twl_modules[i];
+ struct twl_client *twl = &twl_priv->twl_modules[i];
- twl->address = client->addr + i;
if (i == 0) {
twl->client = client;
} else {
twl->client = i2c_new_dummy(client->adapter,
- twl->address);
+ client->addr + i);
if (!twl->client) {
dev_err(&client->dev,
"can't attach client %d\n", i);
@@ -1252,24 +1166,29 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
goto fail;
}
}
- mutex_init(&twl->xfer_lock);
+
+ twl->regmap = devm_regmap_init_i2c(twl->client,
+ &twl_regmap_config[i]);
+ if (IS_ERR(twl->regmap)) {
+ status = PTR_ERR(twl->regmap);
+ dev_err(&client->dev,
+ "Failed to allocate regmap %d, err: %d\n", i,
+ status);
+ goto fail;
+ }
}
- inuse = true;
+ twl_priv->ready = true;
/* setup clock framework */
- clocks_init(&pdev->dev, pdata->clock);
+ clocks_init(&pdev->dev, pdata ? pdata->clock : NULL);
/* read TWL IDCODE Register */
- if (twl_id == TWL4030_CLASS_ID) {
+ if (twl_class_is_4030()) {
status = twl_read_idcode_register();
WARN(status < 0, "Error: reading twl_idcode register value\n");
}
- /* load power event scripts */
- if (IS_ENABLED(CONFIG_TWL4030_POWER) && pdata->power)
- twl4030_power_init(pdata->power);
-
/* Maybe init the T2 Interrupt subsystem */
if (client->irq) {
if (twl_class_is_4030()) {
@@ -1289,6 +1208,11 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
* Disable TWL4030/TWL5030 I2C Pull-up on I2C1 and I2C4(SR) interface.
* Program I2C_SCL_CTRL_PU(bit 0)=0, I2C_SDA_CTRL_PU (bit 2)=0,
* SR_I2C_SCL_CTRL_PU(bit 4)=0 and SR_I2C_SDA_CTRL_PU(bit 6)=0.
+ *
+ * Also, always enable SmartReflex bit as that's needed for omaps to
+ * to do anything over I2C4 for voltage scaling even if SmartReflex
+ * is disabled. Without the SmartReflex bit omap sys_clkreq idle
+ * signal will never trigger for retention idle.
*/
if (twl_class_is_4030()) {
u8 temp;
@@ -1297,13 +1221,22 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
temp &= ~(SR_I2C_SDA_CTRL_PU | SR_I2C_SCL_CTRL_PU | \
I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU);
twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);
+
+ twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &temp,
+ TWL4030_DCDC_GLOBAL_CFG);
+ temp |= SMARTREFLEX_ENABLE;
+ twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, temp,
+ TWL4030_DCDC_GLOBAL_CFG);
}
- status = -ENODEV;
- if (node)
- status = of_platform_populate(node, NULL, NULL, &client->dev);
- if (status)
+ if (node) {
+ if (pdata)
+ twl_auxdata_lookup[0].platform_data = pdata->gpio;
+ status = of_platform_populate(node, NULL, twl_auxdata_lookup,
+ &client->dev);
+ } else {
status = add_children(pdata, irq_base, id->driver_data);
+ }
fail:
if (status < 0)
@@ -1325,7 +1258,7 @@ static const struct i2c_device_id twl_ids[] = {
{ "tps65921", TPS_SUBSET }, /* fewer LDOs; no codec, no LED
and vibrator. Charger in USB module*/
{ "twl6030", TWL6030_CLASS }, /* "Phoenix power chip" */
- { "twl6025", TWL6030_CLASS | TWL6025_SUBCLASS }, /* "Phoenix lite" */
+ { "twl6032", TWL6030_CLASS | TWL6032_SUBCLASS }, /* "Phoenix lite" */
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(i2c, twl_ids);
@@ -1338,17 +1271,7 @@ static struct i2c_driver twl_driver = {
.remove = twl_remove,
};
-static int __init twl_init(void)
-{
- return i2c_add_driver(&twl_driver);
-}
-subsys_initcall(twl_init);
-
-static void __exit twl_exit(void)
-{
- i2c_del_driver(&twl_driver);
-}
-module_exit(twl_exit);
+module_i2c_driver(twl_driver);
MODULE_AUTHOR("Texas Instruments, Inc.");
MODULE_DESCRIPTION("I2C Core interface for TWL");
diff --git a/drivers/mfd/twl4030-audio.c b/drivers/mfd/twl4030-audio.c
index 5c11acf9e0f..07fe542e6fc 100644
--- a/drivers/mfd/twl4030-audio.c
+++ b/drivers/mfd/twl4030-audio.c
@@ -118,7 +118,7 @@ EXPORT_SYMBOL_GPL(twl4030_audio_enable_resource);
* Disable the resource.
* The function returns with error or the content of the register
*/
-int twl4030_audio_disable_resource(unsigned id)
+int twl4030_audio_disable_resource(enum twl4030_audio_res id)
{
struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
int val;
@@ -184,10 +184,10 @@ static bool twl4030_audio_has_vibra(struct twl4030_audio_data *pdata,
return false;
}
-static int __devinit twl4030_audio_probe(struct platform_device *pdev)
+static int twl4030_audio_probe(struct platform_device *pdev)
{
struct twl4030_audio *audio;
- struct twl4030_audio_data *pdata = pdev->dev.platform_data;
+ struct twl4030_audio_data *pdata = dev_get_platdata(&pdev->dev);
struct device_node *node = pdev->dev.of_node;
struct mfd_cell *cell = NULL;
int ret, childs = 0;
@@ -261,18 +261,15 @@ static int __devinit twl4030_audio_probe(struct platform_device *pdev)
ret = -ENODEV;
}
- if (ret) {
- platform_set_drvdata(pdev, NULL);
+ if (ret)
twl4030_audio_dev = NULL;
- }
return ret;
}
-static int __devexit twl4030_audio_remove(struct platform_device *pdev)
+static int twl4030_audio_remove(struct platform_device *pdev)
{
mfd_remove_devices(&pdev->dev);
- platform_set_drvdata(pdev, NULL);
twl4030_audio_dev = NULL;
return 0;
@@ -291,7 +288,7 @@ static struct platform_driver twl4030_audio_driver = {
.of_match_table = twl4030_audio_of_match,
},
.probe = twl4030_audio_probe,
- .remove = __devexit_p(twl4030_audio_remove),
+ .remove = twl4030_audio_remove,
};
module_platform_driver(twl4030_audio_driver);
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c
index ad733d76207..596b1f657e2 100644
--- a/drivers/mfd/twl4030-irq.c
+++ b/drivers/mfd/twl4030-irq.c
@@ -27,7 +27,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/init.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -295,8 +294,8 @@ static irqreturn_t handle_twl4030_pih(int irq, void *devid)
irqreturn_t ret;
u8 pih_isr;
- ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
- REG_PIH_ISR_P1);
+ ret = twl_i2c_read_u8(TWL_MODULE_PIH, &pih_isr,
+ REG_PIH_ISR_P1);
if (ret) {
pr_warning("twl4030: I2C error %d reading PIH ISR\n", ret);
return IRQ_NONE;
@@ -501,7 +500,7 @@ static void twl4030_sih_bus_sync_unlock(struct irq_data *data)
} imr;
/* byte[0] gets overwritten as we write ... */
- imr.word = cpu_to_le32(agent->imr << 8);
+ imr.word = cpu_to_le32(agent->imr);
agent->imr_change_pending = false;
/* write the whole mask ... simpler than subsetting it */
@@ -526,7 +525,7 @@ static void twl4030_sih_bus_sync_unlock(struct irq_data *data)
* any processor on the other IRQ line, EDR registers are
* shared.
*/
- status = twl_i2c_read(sih->module, bytes + 1,
+ status = twl_i2c_read(sih->module, bytes,
sih->edr_offset, sih->bytes_edr);
if (status) {
pr_err("twl4030: %s, %s --> %d\n", __func__,
@@ -537,16 +536,13 @@ static void twl4030_sih_bus_sync_unlock(struct irq_data *data)
/* Modify only the bits we know must change */
while (edge_change) {
int i = fls(edge_change) - 1;
- struct irq_data *idata;
- int byte = 1 + (i >> 2);
+ int byte = i >> 2;
int off = (i & 0x3) * 2;
unsigned int type;
- idata = irq_get_irq_data(i + agent->irq_base);
-
bytes[byte] &= ~(0x03 << off);
- type = irqd_get_trigger_type(idata);
+ type = irq_get_trigger_type(i + agent->irq_base);
if (type & IRQ_TYPE_EDGE_RISING)
bytes[byte] |= BIT(off + 1);
if (type & IRQ_TYPE_EDGE_FALLING)
@@ -573,6 +569,7 @@ static struct irq_chip twl4030_sih_irq_chip = {
.irq_set_type = twl4030_sih_set_type,
.irq_bus_lock = twl4030_sih_bus_lock,
.irq_bus_sync_unlock = twl4030_sih_bus_sync_unlock,
+ .flags = IRQCHIP_SKIP_SET_WAKE,
};
/*----------------------------------------------------------------------*/
@@ -672,7 +669,8 @@ int twl4030_sih_setup(struct device *dev, int module, int irq_base)
irq = sih_mod + twl4030_irq_base;
irq_set_handler_data(irq, agent);
agent->irq_name = kasprintf(GFP_KERNEL, "twl4030_%s", sih->name);
- status = request_threaded_irq(irq, NULL, handle_twl4030_sih, 0,
+ status = request_threaded_irq(irq, NULL, handle_twl4030_sih,
+ IRQF_EARLY_RESUME,
agent->irq_name ?: sih->name, NULL);
dev_info(dev, "%s (irq %d) chaining IRQs %d..%d\n", sih->name,
diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c
deleted file mode 100644
index 456ecb5ac4f..00000000000
--- a/drivers/mfd/twl4030-madc.c
+++ /dev/null
@@ -1,815 +0,0 @@
-/*
- *
- * TWL4030 MADC module driver-This driver monitors the real time
- * conversion of analog signals like battery temperature,
- * battery type, battery level etc.
- *
- * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
- * J Keerthy <j-keerthy@ti.com>
- *
- * Based on twl4030-madc.c
- * Copyright (C) 2008 Nokia Corporation
- * Mikko Ylinen <mikko.k.ylinen@nokia.com>
- *
- * Amit Kucheria <amit.kucheria@canonical.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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/i2c/twl.h>
-#include <linux/i2c/twl4030-madc.h>
-#include <linux/module.h>
-#include <linux/stddef.h>
-#include <linux/mutex.h>
-#include <linux/bitops.h>
-#include <linux/jiffies.h>
-#include <linux/types.h>
-#include <linux/gfp.h>
-#include <linux/err.h>
-
-/*
- * struct twl4030_madc_data - a container for madc info
- * @dev - pointer to device structure for madc
- * @lock - mutex protecting this data structure
- * @requests - Array of request struct corresponding to SW1, SW2 and RT
- * @imr - Interrupt mask register of MADC
- * @isr - Interrupt status register of MADC
- */
-struct twl4030_madc_data {
- struct device *dev;
- struct mutex lock; /* mutex protecting this data structure */
- struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
- int imr;
- int isr;
-};
-
-static struct twl4030_madc_data *twl4030_madc;
-
-struct twl4030_prescale_divider_ratios {
- s16 numerator;
- s16 denominator;
-};
-
-static const struct twl4030_prescale_divider_ratios
-twl4030_divider_ratios[16] = {
- {1, 1}, /* CHANNEL 0 No Prescaler */
- {1, 1}, /* CHANNEL 1 No Prescaler */
- {6, 10}, /* CHANNEL 2 */
- {6, 10}, /* CHANNEL 3 */
- {6, 10}, /* CHANNEL 4 */
- {6, 10}, /* CHANNEL 5 */
- {6, 10}, /* CHANNEL 6 */
- {6, 10}, /* CHANNEL 7 */
- {3, 14}, /* CHANNEL 8 */
- {1, 3}, /* CHANNEL 9 */
- {1, 1}, /* CHANNEL 10 No Prescaler */
- {15, 100}, /* CHANNEL 11 */
- {1, 4}, /* CHANNEL 12 */
- {1, 1}, /* CHANNEL 13 Reserved channels */
- {1, 1}, /* CHANNEL 14 Reseved channels */
- {5, 11}, /* CHANNEL 15 */
-};
-
-
-/*
- * Conversion table from -3 to 55 degree Celcius
- */
-static int therm_tbl[] = {
-30800, 29500, 28300, 27100,
-26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, 17900,
-17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, 12600, 12100,
-11600, 11200, 10800, 10400, 10000, 9630, 9280, 8950, 8620, 8310,
-8020, 7730, 7460, 7200, 6950, 6710, 6470, 6250, 6040, 5830,
-5640, 5450, 5260, 5090, 4920, 4760, 4600, 4450, 4310, 4170,
-4040, 3910, 3790, 3670, 3550
-};
-
-/*
- * Structure containing the registers
- * of different conversion methods supported by MADC.
- * Hardware or RT real time conversion request initiated by external host
- * processor for RT Signal conversions.
- * External host processors can also request for non RT conversions
- * SW1 and SW2 software conversions also called asynchronous or GPC request.
- */
-static
-const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
- [TWL4030_MADC_RT] = {
- .sel = TWL4030_MADC_RTSELECT_LSB,
- .avg = TWL4030_MADC_RTAVERAGE_LSB,
- .rbase = TWL4030_MADC_RTCH0_LSB,
- },
- [TWL4030_MADC_SW1] = {
- .sel = TWL4030_MADC_SW1SELECT_LSB,
- .avg = TWL4030_MADC_SW1AVERAGE_LSB,
- .rbase = TWL4030_MADC_GPCH0_LSB,
- .ctrl = TWL4030_MADC_CTRL_SW1,
- },
- [TWL4030_MADC_SW2] = {
- .sel = TWL4030_MADC_SW2SELECT_LSB,
- .avg = TWL4030_MADC_SW2AVERAGE_LSB,
- .rbase = TWL4030_MADC_GPCH0_LSB,
- .ctrl = TWL4030_MADC_CTRL_SW2,
- },
-};
-
-/*
- * Function to read a particular channel value.
- * @madc - pointer to struct twl4030_madc_data
- * @reg - lsb of ADC Channel
- * If the i2c read fails it returns an error else returns 0.
- */
-static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
-{
- u8 msb, lsb;
- int ret;
- /*
- * For each ADC channel, we have MSB and LSB register pair. MSB address
- * is always LSB address+1. reg parameter is the address of LSB register
- */
- ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &msb, reg + 1);
- if (ret) {
- dev_err(madc->dev, "unable to read MSB register 0x%X\n",
- reg + 1);
- return ret;
- }
- ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &lsb, reg);
- if (ret) {
- dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg);
- return ret;
- }
-
- return (int)(((msb << 8) | lsb) >> 6);
-}
-
-/*
- * Return battery temperature
- * Or < 0 on failure.
- */
-static int twl4030battery_temperature(int raw_volt)
-{
- u8 val;
- int temp, curr, volt, res, ret;
-
- volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R;
- /* Getting and calculating the supply current in micro ampers */
- ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val,
- REG_BCICTL2);
- if (ret < 0)
- return ret;
- curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10;
- /* Getting and calculating the thermistor resistance in ohms */
- res = volt * 1000 / curr;
- /* calculating temperature */
- for (temp = 58; temp >= 0; temp--) {
- int actual = therm_tbl[temp];
-
- if ((actual - res) >= 0)
- break;
- }
-
- return temp + 1;
-}
-
-static int twl4030battery_current(int raw_volt)
-{
- int ret;
- u8 val;
-
- ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val,
- TWL4030_BCI_BCICTL1);
- if (ret)
- return ret;
- if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */
- return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1;
- else /* slope of 0.88 mV/mA */
- return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2;
-}
-/*
- * Function to read channel values
- * @madc - pointer to twl4030_madc_data struct
- * @reg_base - Base address of the first channel
- * @Channels - 16 bit bitmap. If the bit is set, channel value is read
- * @buf - The channel values are stored here. if read fails error
- * value is stored
- * Returns the number of successfully read channels.
- */
-static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
- u8 reg_base, unsigned
- long channels, int *buf)
-{
- int count = 0, count_req = 0, i;
- u8 reg;
-
- for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) {
- reg = reg_base + 2 * i;
- buf[i] = twl4030_madc_channel_raw_read(madc, reg);
- if (buf[i] < 0) {
- dev_err(madc->dev,
- "Unable to read register 0x%X\n", reg);
- count_req++;
- continue;
- }
- switch (i) {
- case 10:
- buf[i] = twl4030battery_current(buf[i]);
- if (buf[i] < 0) {
- dev_err(madc->dev, "err reading current\n");
- count_req++;
- } else {
- count++;
- buf[i] = buf[i] - 750;
- }
- break;
- case 1:
- buf[i] = twl4030battery_temperature(buf[i]);
- if (buf[i] < 0) {
- dev_err(madc->dev, "err reading temperature\n");
- count_req++;
- } else {
- buf[i] -= 3;
- count++;
- }
- break;
- default:
- count++;
- /* Analog Input (V) = conv_result * step_size / R
- * conv_result = decimal value of 10-bit conversion
- * result
- * step size = 1.5 / (2 ^ 10 -1)
- * R = Prescaler ratio for input channels.
- * Result given in mV hence multiplied by 1000.
- */
- buf[i] = (buf[i] * 3 * 1000 *
- twl4030_divider_ratios[i].denominator)
- / (2 * 1023 *
- twl4030_divider_ratios[i].numerator);
- }
- }
- if (count_req)
- dev_err(madc->dev, "%d channel conversion failed\n", count_req);
-
- return count;
-}
-
-/*
- * Enables irq.
- * @madc - pointer to twl4030_madc_data struct
- * @id - irq number to be enabled
- * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
- * corresponding to RT, SW1, SW2 conversion requests.
- * If the i2c read fails it returns an error else returns 0.
- */
-static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id)
-{
- u8 val;
- int ret;
-
- ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
- if (ret) {
- dev_err(madc->dev, "unable to read imr register 0x%X\n",
- madc->imr);
- return ret;
- }
- val &= ~(1 << id);
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
- if (ret) {
- dev_err(madc->dev,
- "unable to write imr register 0x%X\n", madc->imr);
- return ret;
-
- }
-
- return 0;
-}
-
-/*
- * Disables irq.
- * @madc - pointer to twl4030_madc_data struct
- * @id - irq number to be disabled
- * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
- * corresponding to RT, SW1, SW2 conversion requests.
- * Returns error if i2c read/write fails.
- */
-static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id)
-{
- u8 val;
- int ret;
-
- ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
- if (ret) {
- dev_err(madc->dev, "unable to read imr register 0x%X\n",
- madc->imr);
- return ret;
- }
- val |= (1 << id);
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
- if (ret) {
- dev_err(madc->dev,
- "unable to write imr register 0x%X\n", madc->imr);
- return ret;
- }
-
- return 0;
-}
-
-static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc)
-{
- struct twl4030_madc_data *madc = _madc;
- const struct twl4030_madc_conversion_method *method;
- u8 isr_val, imr_val;
- int i, len, ret;
- struct twl4030_madc_request *r;
-
- mutex_lock(&madc->lock);
- ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr);
- if (ret) {
- dev_err(madc->dev, "unable to read isr register 0x%X\n",
- madc->isr);
- goto err_i2c;
- }
- ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr);
- if (ret) {
- dev_err(madc->dev, "unable to read imr register 0x%X\n",
- madc->imr);
- goto err_i2c;
- }
- isr_val &= ~imr_val;
- for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
- if (!(isr_val & (1 << i)))
- continue;
- ret = twl4030_madc_disable_irq(madc, i);
- if (ret < 0)
- dev_dbg(madc->dev, "Disable interrupt failed%d\n", i);
- madc->requests[i].result_pending = 1;
- }
- for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
- r = &madc->requests[i];
- /* No pending results for this method, move to next one */
- if (!r->result_pending)
- continue;
- method = &twl4030_conversion_methods[r->method];
- /* Read results */
- len = twl4030_madc_read_channels(madc, method->rbase,
- r->channels, r->rbuf);
- /* Return results to caller */
- if (r->func_cb != NULL) {
- r->func_cb(len, r->channels, r->rbuf);
- r->func_cb = NULL;
- }
- /* Free request */
- r->result_pending = 0;
- r->active = 0;
- }
- mutex_unlock(&madc->lock);
-
- return IRQ_HANDLED;
-
-err_i2c:
- /*
- * In case of error check whichever request is active
- * and service the same.
- */
- for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
- r = &madc->requests[i];
- if (r->active == 0)
- continue;
- method = &twl4030_conversion_methods[r->method];
- /* Read results */
- len = twl4030_madc_read_channels(madc, method->rbase,
- r->channels, r->rbuf);
- /* Return results to caller */
- if (r->func_cb != NULL) {
- r->func_cb(len, r->channels, r->rbuf);
- r->func_cb = NULL;
- }
- /* Free request */
- r->result_pending = 0;
- r->active = 0;
- }
- mutex_unlock(&madc->lock);
-
- return IRQ_HANDLED;
-}
-
-static int twl4030_madc_set_irq(struct twl4030_madc_data *madc,
- struct twl4030_madc_request *req)
-{
- struct twl4030_madc_request *p;
- int ret;
-
- p = &madc->requests[req->method];
- memcpy(p, req, sizeof(*req));
- ret = twl4030_madc_enable_irq(madc, req->method);
- if (ret < 0) {
- dev_err(madc->dev, "enable irq failed!!\n");
- return ret;
- }
-
- return 0;
-}
-
-/*
- * Function which enables the madc conversion
- * by writing to the control register.
- * @madc - pointer to twl4030_madc_data struct
- * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1
- * corresponding to RT SW1 or SW2 conversion methods.
- * Returns 0 if succeeds else a negative error value
- */
-static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
- int conv_method)
-{
- const struct twl4030_madc_conversion_method *method;
- int ret = 0;
- method = &twl4030_conversion_methods[conv_method];
- switch (conv_method) {
- case TWL4030_MADC_SW1:
- case TWL4030_MADC_SW2:
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
- TWL4030_MADC_SW_START, method->ctrl);
- if (ret) {
- dev_err(madc->dev,
- "unable to write ctrl register 0x%X\n",
- method->ctrl);
- return ret;
- }
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-/*
- * Function that waits for conversion to be ready
- * @madc - pointer to twl4030_madc_data struct
- * @timeout_ms - timeout value in milliseconds
- * @status_reg - ctrl register
- * returns 0 if succeeds else a negative error value
- */
-static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc,
- unsigned int timeout_ms,
- u8 status_reg)
-{
- unsigned long timeout;
- int ret;
-
- timeout = jiffies + msecs_to_jiffies(timeout_ms);
- do {
- u8 reg;
-
- ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &reg, status_reg);
- if (ret) {
- dev_err(madc->dev,
- "unable to read status register 0x%X\n",
- status_reg);
- return ret;
- }
- if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW))
- return 0;
- usleep_range(500, 2000);
- } while (!time_after(jiffies, timeout));
- dev_err(madc->dev, "conversion timeout!\n");
-
- return -EAGAIN;
-}
-
-/*
- * An exported function which can be called from other kernel drivers.
- * @req twl4030_madc_request structure
- * req->rbuf will be filled with read values of channels based on the
- * channel index. If a particular channel reading fails there will
- * be a negative error value in the corresponding array element.
- * returns 0 if succeeds else error value
- */
-int twl4030_madc_conversion(struct twl4030_madc_request *req)
-{
- const struct twl4030_madc_conversion_method *method;
- u8 ch_msb, ch_lsb;
- int ret;
-
- if (!req || !twl4030_madc)
- return -EINVAL;
-
- mutex_lock(&twl4030_madc->lock);
- if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) {
- ret = -EINVAL;
- goto out;
- }
- /* Do we have a conversion request ongoing */
- if (twl4030_madc->requests[req->method].active) {
- ret = -EBUSY;
- goto out;
- }
- ch_msb = (req->channels >> 8) & 0xff;
- ch_lsb = req->channels & 0xff;
- method = &twl4030_conversion_methods[req->method];
- /* Select channels to be converted */
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->sel + 1);
- if (ret) {
- dev_err(twl4030_madc->dev,
- "unable to write sel register 0x%X\n", method->sel + 1);
- goto out;
- }
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel);
- if (ret) {
- dev_err(twl4030_madc->dev,
- "unable to write sel register 0x%X\n", method->sel + 1);
- goto out;
- }
- /* Select averaging for all channels if do_avg is set */
- if (req->do_avg) {
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
- ch_msb, method->avg + 1);
- if (ret) {
- dev_err(twl4030_madc->dev,
- "unable to write avg register 0x%X\n",
- method->avg + 1);
- goto out;
- }
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
- ch_lsb, method->avg);
- if (ret) {
- dev_err(twl4030_madc->dev,
- "unable to write sel reg 0x%X\n",
- method->sel + 1);
- goto out;
- }
- }
- if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) {
- ret = twl4030_madc_set_irq(twl4030_madc, req);
- if (ret < 0)
- goto out;
- ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
- if (ret < 0)
- goto out;
- twl4030_madc->requests[req->method].active = 1;
- ret = 0;
- goto out;
- }
- /* With RT method we should not be here anymore */
- if (req->method == TWL4030_MADC_RT) {
- ret = -EINVAL;
- goto out;
- }
- ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
- if (ret < 0)
- goto out;
- twl4030_madc->requests[req->method].active = 1;
- /* Wait until conversion is ready (ctrl register returns EOC) */
- ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl);
- if (ret) {
- twl4030_madc->requests[req->method].active = 0;
- goto out;
- }
- ret = twl4030_madc_read_channels(twl4030_madc, method->rbase,
- req->channels, req->rbuf);
- twl4030_madc->requests[req->method].active = 0;
-
-out:
- mutex_unlock(&twl4030_madc->lock);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(twl4030_madc_conversion);
-
-/*
- * Return channel value
- * Or < 0 on failure.
- */
-int twl4030_get_madc_conversion(int channel_no)
-{
- struct twl4030_madc_request req;
- int temp = 0;
- int ret;
-
- req.channels = (1 << channel_no);
- req.method = TWL4030_MADC_SW2;
- req.active = 0;
- req.func_cb = NULL;
- ret = twl4030_madc_conversion(&req);
- if (ret < 0)
- return ret;
- if (req.rbuf[channel_no] > 0)
- temp = req.rbuf[channel_no];
-
- return temp;
-}
-EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion);
-
-/*
- * Function to enable or disable bias current for
- * main battery type reading or temperature sensing
- * @madc - pointer to twl4030_madc_data struct
- * @chan - can be one of the two values
- * TWL4030_BCI_ITHEN - Enables bias current for main battery type reading
- * TWL4030_BCI_TYPEN - Enables bias current for main battery temperature
- * sensing
- * @on - enable or disable chan.
- */
-static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
- int chan, int on)
-{
- int ret;
- u8 regval;
-
- ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
- &regval, TWL4030_BCI_BCICTL1);
- if (ret) {
- dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X",
- TWL4030_BCI_BCICTL1);
- return ret;
- }
- if (on)
- regval |= chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
- else
- regval &= chan ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN;
- ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE,
- regval, TWL4030_BCI_BCICTL1);
- if (ret) {
- dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n",
- TWL4030_BCI_BCICTL1);
- return ret;
- }
-
- return 0;
-}
-
-/*
- * Function that sets MADC software power on bit to enable MADC
- * @madc - pointer to twl4030_madc_data struct
- * @on - Enable or disable MADC software powen on bit.
- * returns error if i2c read/write fails else 0
- */
-static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
-{
- u8 regval;
- int ret;
-
- ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
- &regval, TWL4030_MADC_CTRL1);
- if (ret) {
- dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n",
- TWL4030_MADC_CTRL1);
- return ret;
- }
- if (on)
- regval |= TWL4030_MADC_MADCON;
- else
- regval &= ~TWL4030_MADC_MADCON;
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1);
- if (ret) {
- dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n",
- TWL4030_MADC_CTRL1);
- return ret;
- }
-
- return 0;
-}
-
-/*
- * Initialize MADC and request for threaded irq
- */
-static int __devinit twl4030_madc_probe(struct platform_device *pdev)
-{
- struct twl4030_madc_data *madc;
- struct twl4030_madc_platform_data *pdata = pdev->dev.platform_data;
- int ret;
- u8 regval;
-
- if (!pdata) {
- dev_err(&pdev->dev, "platform_data not available\n");
- return -EINVAL;
- }
- madc = kzalloc(sizeof(*madc), GFP_KERNEL);
- if (!madc)
- return -ENOMEM;
-
- madc->dev = &pdev->dev;
-
- /*
- * Phoenix provides 2 interrupt lines. The first one is connected to
- * the OMAP. The other one can be connected to the other processor such
- * as modem. Hence two separate ISR and IMR registers.
- */
- madc->imr = (pdata->irq_line == 1) ?
- TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
- madc->isr = (pdata->irq_line == 1) ?
- TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2;
- ret = twl4030_madc_set_power(madc, 1);
- if (ret < 0)
- goto err_power;
- ret = twl4030_madc_set_current_generator(madc, 0, 1);
- if (ret < 0)
- goto err_current_generator;
-
- ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
- &regval, TWL4030_BCI_BCICTL1);
- if (ret) {
- dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n",
- TWL4030_BCI_BCICTL1);
- goto err_i2c;
- }
- regval |= TWL4030_BCI_MESBAT;
- ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE,
- regval, TWL4030_BCI_BCICTL1);
- if (ret) {
- dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n",
- TWL4030_BCI_BCICTL1);
- goto err_i2c;
- }
-
- /* Check that MADC clock is on */
- ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &regval, TWL4030_REG_GPBR1);
- if (ret) {
- dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n",
- TWL4030_REG_GPBR1);
- goto err_i2c;
- }
-
- /* If MADC clk is not on, turn it on */
- if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) {
- dev_info(&pdev->dev, "clk disabled, enabling\n");
- regval |= TWL4030_GPBR1_MADC_HFCLK_EN;
- ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval,
- TWL4030_REG_GPBR1);
- if (ret) {
- dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n",
- TWL4030_REG_GPBR1);
- goto err_i2c;
- }
- }
-
- platform_set_drvdata(pdev, madc);
- mutex_init(&madc->lock);
- ret = request_threaded_irq(platform_get_irq(pdev, 0), NULL,
- twl4030_madc_threaded_irq_handler,
- IRQF_TRIGGER_RISING, "twl4030_madc", madc);
- if (ret) {
- dev_dbg(&pdev->dev, "could not request irq\n");
- goto err_irq;
- }
- twl4030_madc = madc;
- return 0;
-err_irq:
- platform_set_drvdata(pdev, NULL);
-err_i2c:
- twl4030_madc_set_current_generator(madc, 0, 0);
-err_current_generator:
- twl4030_madc_set_power(madc, 0);
-err_power:
- kfree(madc);
-
- return ret;
-}
-
-static int __devexit twl4030_madc_remove(struct platform_device *pdev)
-{
- struct twl4030_madc_data *madc = platform_get_drvdata(pdev);
-
- free_irq(platform_get_irq(pdev, 0), madc);
- platform_set_drvdata(pdev, NULL);
- twl4030_madc_set_current_generator(madc, 0, 0);
- twl4030_madc_set_power(madc, 0);
- kfree(madc);
-
- return 0;
-}
-
-static struct platform_driver twl4030_madc_driver = {
- .probe = twl4030_madc_probe,
- .remove = __exit_p(twl4030_madc_remove),
- .driver = {
- .name = "twl4030_madc",
- .owner = THIS_MODULE,
- },
-};
-
-module_platform_driver(twl4030_madc_driver);
-
-MODULE_DESCRIPTION("TWL4030 ADC driver");
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("J Keerthy");
-MODULE_ALIAS("platform:twl4030_madc");
diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c
index 79ca33dfacc..3bc969a5916 100644
--- a/drivers/mfd/twl4030-power.c
+++ b/drivers/mfd/twl4030-power.c
@@ -28,13 +28,22 @@
#include <linux/pm.h>
#include <linux/i2c/twl.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/mach-types.h>
static u8 twl4030_start_script_address = 0x2b;
-#define PWR_P1_SW_EVENTS 0x10
-#define PWR_DEVOFF (1 << 0)
+/* Register bits for P1, P2 and P3_SW_EVENTS */
+#define PWR_STOPON_PRWON BIT(6)
+#define PWR_STOPON_SYSEN BIT(5)
+#define PWR_ENABLE_WARMRESET BIT(4)
+#define PWR_LVL_WAKEUP BIT(3)
+#define PWR_DEVACT BIT(2)
+#define PWR_DEVSLP BIT(1)
+#define PWR_DEVOFF BIT(0)
+
#define SEQ_OFFSYNC (1 << 0)
#define PHY_TO_OFF_PM_MASTER(p) (p - 0x36)
@@ -51,10 +60,6 @@ static u8 twl4030_start_script_address = 0x2b;
#define R_CFG_P2_TRANSITION PHY_TO_OFF_PM_MASTER(0x37)
#define R_CFG_P3_TRANSITION PHY_TO_OFF_PM_MASTER(0x38)
-#define LVL_WAKEUP 0x08
-
-#define ENABLE_WARMRESET (1<<4)
-
#define END_OF_SCRIPT 0x3f
#define R_SEQ_ADD_A2S PHY_TO_OFF_PM_MASTER(0x55)
@@ -124,21 +129,66 @@ static u8 res_config_addrs[] = {
[RES_MAIN_REF] = 0x94,
};
-static int __devinit twl4030_write_script_byte(u8 address, u8 byte)
+/*
+ * Usable values for .remap_sleep and .remap_off
+ * Based on table "5.3.3 Resource Operating modes"
+ */
+enum {
+ TWL_REMAP_OFF = 0,
+ TWL_REMAP_SLEEP = 8,
+ TWL_REMAP_ACTIVE = 9,
+};
+
+/*
+ * Macros to configure the PM register states for various resources.
+ * Note that we can make MSG_SINGULAR etc private to this driver once
+ * omap3 has been made DT only.
+ */
+#define TWL_DFLT_DELAY 2 /* typically 2 32 KiHz cycles */
+#define TWL_DEV_GRP_P123 (DEV_GRP_P1 | DEV_GRP_P2 | DEV_GRP_P3)
+#define TWL_RESOURCE_SET(res, state) \
+ { MSG_SINGULAR(DEV_GRP_NULL, (res), (state)), TWL_DFLT_DELAY }
+#define TWL_RESOURCE_ON(res) TWL_RESOURCE_SET(res, RES_STATE_ACTIVE)
+#define TWL_RESOURCE_OFF(res) TWL_RESOURCE_SET(res, RES_STATE_OFF)
+#define TWL_RESOURCE_RESET(res) TWL_RESOURCE_SET(res, RES_STATE_WRST)
+/*
+ * It seems that type1 and type2 is just the resource init order
+ * number for the type1 and type2 group.
+ */
+#define TWL_RESOURCE_SET_ACTIVE(res, state) \
+ { MSG_SINGULAR(DEV_GRP_NULL, (res), RES_STATE_ACTIVE), (state) }
+#define TWL_RESOURCE_GROUP_RESET(group, type1, type2) \
+ { MSG_BROADCAST(DEV_GRP_NULL, (group), (type1), (type2), \
+ RES_STATE_WRST), TWL_DFLT_DELAY }
+#define TWL_RESOURCE_GROUP_SLEEP(group, type, type2) \
+ { MSG_BROADCAST(DEV_GRP_NULL, (group), (type), (type2), \
+ RES_STATE_SLEEP), TWL_DFLT_DELAY }
+#define TWL_RESOURCE_GROUP_ACTIVE(group, type, type2) \
+ { MSG_BROADCAST(DEV_GRP_NULL, (group), (type), (type2), \
+ RES_STATE_ACTIVE), TWL_DFLT_DELAY }
+#define TWL_REMAP_SLEEP(res, devgrp, typ, typ2) \
+ { .resource = (res), .devgroup = (devgrp), \
+ .type = (typ), .type2 = (typ2), \
+ .remap_off = TWL_REMAP_OFF, \
+ .remap_sleep = TWL_REMAP_SLEEP, }
+#define TWL_REMAP_OFF(res, devgrp, typ, typ2) \
+ { .resource = (res), .devgroup = (devgrp), \
+ .type = (typ), .type2 = (typ2), \
+ .remap_off = TWL_REMAP_OFF, .remap_sleep = TWL_REMAP_OFF, }
+
+static int twl4030_write_script_byte(u8 address, u8 byte)
{
int err;
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
- R_MEMORY_ADDRESS);
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, address, R_MEMORY_ADDRESS);
if (err)
goto out;
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, byte,
- R_MEMORY_DATA);
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, byte, R_MEMORY_DATA);
out:
return err;
}
-static int __devinit twl4030_write_script_ins(u8 address, u16 pmb_message,
+static int twl4030_write_script_ins(u8 address, u16 pmb_message,
u8 delay, u8 next)
{
int err;
@@ -158,10 +208,10 @@ out:
return err;
}
-static int __devinit twl4030_write_script(u8 address, struct twl4030_ins *script,
+static int twl4030_write_script(u8 address, struct twl4030_ins *script,
int len)
{
- int err;
+ int err = -EINVAL;
for (; len; len--, address++, script++) {
if (len == 1) {
@@ -183,74 +233,66 @@ static int __devinit twl4030_write_script(u8 address, struct twl4030_ins *script
return err;
}
-static int __devinit twl4030_config_wakeup3_sequence(u8 address)
+static int twl4030_config_wakeup3_sequence(u8 address)
{
int err;
u8 data;
/* Set SLEEP to ACTIVE SEQ address for P3 */
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
- R_SEQ_ADD_S2A3);
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, address, R_SEQ_ADD_S2A3);
if (err)
goto out;
/* P3 LVL_WAKEUP should be on LEVEL */
- err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
- R_P3_SW_EVENTS);
+ err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data, R_P3_SW_EVENTS);
if (err)
goto out;
- data |= LVL_WAKEUP;
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
- R_P3_SW_EVENTS);
+ data |= PWR_LVL_WAKEUP;
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, R_P3_SW_EVENTS);
out:
if (err)
pr_err("TWL4030 wakeup sequence for P3 config error\n");
return err;
}
-static int __devinit twl4030_config_wakeup12_sequence(u8 address)
+static int twl4030_config_wakeup12_sequence(u8 address)
{
int err = 0;
u8 data;
/* Set SLEEP to ACTIVE SEQ address for P1 and P2 */
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
- R_SEQ_ADD_S2A12);
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, address, R_SEQ_ADD_S2A12);
if (err)
goto out;
/* P1/P2 LVL_WAKEUP should be on LEVEL */
- err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
- R_P1_SW_EVENTS);
+ err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data, R_P1_SW_EVENTS);
if (err)
goto out;
- data |= LVL_WAKEUP;
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
- R_P1_SW_EVENTS);
+ data |= PWR_LVL_WAKEUP;
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, R_P1_SW_EVENTS);
if (err)
goto out;
- err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
- R_P2_SW_EVENTS);
+ err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data, R_P2_SW_EVENTS);
if (err)
goto out;
- data |= LVL_WAKEUP;
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
- R_P2_SW_EVENTS);
+ data |= PWR_LVL_WAKEUP;
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, R_P2_SW_EVENTS);
if (err)
goto out;
if (machine_is_omap_3430sdp() || machine_is_omap_ldp()) {
/* Disabling AC charger effect on sleep-active transitions */
- err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
- R_CFG_P1_TRANSITION);
+ err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data,
+ R_CFG_P1_TRANSITION);
if (err)
goto out;
data &= ~(1<<1);
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data ,
- R_CFG_P1_TRANSITION);
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data,
+ R_CFG_P1_TRANSITION);
if (err)
goto out;
}
@@ -262,13 +304,12 @@ out:
return err;
}
-static int __devinit twl4030_config_sleep_sequence(u8 address)
+static int twl4030_config_sleep_sequence(u8 address)
{
int err;
/* Set ACTIVE to SLEEP SEQ address in T2 memory*/
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
- R_SEQ_ADD_A2S);
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, address, R_SEQ_ADD_A2S);
if (err)
pr_err("TWL4030 sleep sequence config error\n");
@@ -276,55 +317,48 @@ static int __devinit twl4030_config_sleep_sequence(u8 address)
return err;
}
-static int __devinit twl4030_config_warmreset_sequence(u8 address)
+static int twl4030_config_warmreset_sequence(u8 address)
{
int err;
u8 rd_data;
/* Set WARM RESET SEQ address for P1 */
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
- R_SEQ_ADD_WARM);
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, address, R_SEQ_ADD_WARM);
if (err)
goto out;
/* P1/P2/P3 enable WARMRESET */
- err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
- R_P1_SW_EVENTS);
+ err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &rd_data, R_P1_SW_EVENTS);
if (err)
goto out;
- rd_data |= ENABLE_WARMRESET;
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
- R_P1_SW_EVENTS);
+ rd_data |= PWR_ENABLE_WARMRESET;
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, rd_data, R_P1_SW_EVENTS);
if (err)
goto out;
- err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
- R_P2_SW_EVENTS);
+ err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &rd_data, R_P2_SW_EVENTS);
if (err)
goto out;
- rd_data |= ENABLE_WARMRESET;
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
- R_P2_SW_EVENTS);
+ rd_data |= PWR_ENABLE_WARMRESET;
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, rd_data, R_P2_SW_EVENTS);
if (err)
goto out;
- err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
- R_P3_SW_EVENTS);
+ err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &rd_data, R_P3_SW_EVENTS);
if (err)
goto out;
- rd_data |= ENABLE_WARMRESET;
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
- R_P3_SW_EVENTS);
+ rd_data |= PWR_ENABLE_WARMRESET;
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, rd_data, R_P3_SW_EVENTS);
out:
if (err)
pr_err("TWL4030 warmreset seq config error\n");
return err;
}
-static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfig)
+static int twl4030_configure_resource(struct twl4030_resconfig *rconfig)
{
int rconfig_addr;
int err;
@@ -341,7 +375,7 @@ static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfi
rconfig_addr = res_config_addrs[rconfig->resource];
/* Set resource group */
- err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &grp,
+ err = twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &grp,
rconfig_addr + DEV_GRP_OFFSET);
if (err) {
pr_err("TWL4030 Resource %d group could not be read\n",
@@ -352,7 +386,7 @@ static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfi
if (rconfig->devgroup != TWL4030_RESCONFIG_UNDEF) {
grp &= ~DEV_GRP_MASK;
grp |= rconfig->devgroup << DEV_GRP_SHIFT;
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ err = twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER,
grp, rconfig_addr + DEV_GRP_OFFSET);
if (err < 0) {
pr_err("TWL4030 failed to program devgroup\n");
@@ -361,7 +395,7 @@ static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfi
}
/* Set resource types */
- err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &type,
+ err = twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &type,
rconfig_addr + TYPE_OFFSET);
if (err < 0) {
pr_err("TWL4030 Resource %d type could not be read\n",
@@ -379,7 +413,7 @@ static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfi
type |= rconfig->type2 << TYPE2_SHIFT;
}
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ err = twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER,
type, rconfig_addr + TYPE_OFFSET);
if (err < 0) {
pr_err("TWL4030 failed to program resource type\n");
@@ -387,7 +421,7 @@ static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfi
}
/* Set remap states */
- err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &remap,
+ err = twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &remap,
rconfig_addr + REMAP_OFFSET);
if (err < 0) {
pr_err("TWL4030 Resource %d remap could not be read\n",
@@ -405,7 +439,7 @@ static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfi
remap |= rconfig->remap_sleep << SLEEP_STATE_SHIFT;
}
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ err = twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER,
remap,
rconfig_addr + REMAP_OFFSET);
if (err < 0) {
@@ -416,7 +450,7 @@ static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfi
return 0;
}
-static int __devinit load_twl4030_script(struct twl4030_script *tscript,
+static int load_twl4030_script(struct twl4030_script *tscript,
u8 address)
{
int err;
@@ -438,6 +472,12 @@ static int __devinit load_twl4030_script(struct twl4030_script *tscript,
goto out;
}
if (tscript->flags & TWL4030_WAKEUP12_SCRIPT) {
+ /* Reset any existing sleep script to avoid hangs on reboot */
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, END_OF_SCRIPT,
+ R_SEQ_ADD_A2S);
+ if (err)
+ goto out;
+
err = twl4030_config_wakeup12_sequence(address);
if (err)
goto out;
@@ -463,55 +503,109 @@ int twl4030_remove_script(u8 flags)
{
int err = 0;
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
- TWL4030_PM_MASTER_KEY_CFG1,
- TWL4030_PM_MASTER_PROTECT_KEY);
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1,
+ TWL4030_PM_MASTER_PROTECT_KEY);
if (err) {
pr_err("twl4030: unable to unlock PROTECT_KEY\n");
return err;
}
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
- TWL4030_PM_MASTER_KEY_CFG2,
- TWL4030_PM_MASTER_PROTECT_KEY);
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2,
+ TWL4030_PM_MASTER_PROTECT_KEY);
if (err) {
pr_err("twl4030: unable to unlock PROTECT_KEY\n");
return err;
}
if (flags & TWL4030_WRST_SCRIPT) {
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
- R_SEQ_ADD_WARM);
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, END_OF_SCRIPT,
+ R_SEQ_ADD_WARM);
if (err)
return err;
}
if (flags & TWL4030_WAKEUP12_SCRIPT) {
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
- R_SEQ_ADD_S2A12);
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, END_OF_SCRIPT,
+ R_SEQ_ADD_S2A12);
if (err)
return err;
}
if (flags & TWL4030_WAKEUP3_SCRIPT) {
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
- R_SEQ_ADD_S2A3);
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, END_OF_SCRIPT,
+ R_SEQ_ADD_S2A3);
if (err)
return err;
}
if (flags & TWL4030_SLEEP_SCRIPT) {
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
- R_SEQ_ADD_A2S);
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, END_OF_SCRIPT,
+ R_SEQ_ADD_A2S);
if (err)
return err;
}
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
- TWL4030_PM_MASTER_PROTECT_KEY);
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0,
+ TWL4030_PM_MASTER_PROTECT_KEY);
if (err)
pr_err("TWL4030 Unable to relock registers\n");
return err;
}
+static int
+twl4030_power_configure_scripts(const struct twl4030_power_data *pdata)
+{
+ int err;
+ int i;
+ u8 address = twl4030_start_script_address;
+
+ for (i = 0; i < pdata->num; i++) {
+ err = load_twl4030_script(pdata->scripts[i], address);
+ if (err)
+ return err;
+ address += pdata->scripts[i]->size;
+ }
+
+ return 0;
+}
+
+static void twl4030_patch_rconfig(struct twl4030_resconfig *common,
+ struct twl4030_resconfig *board)
+{
+ while (common->resource) {
+ struct twl4030_resconfig *b = board;
+
+ while (b->resource) {
+ if (b->resource == common->resource) {
+ *common = *b;
+ break;
+ }
+ b++;
+ }
+ common++;
+ }
+}
+
+static int
+twl4030_power_configure_resources(const struct twl4030_power_data *pdata)
+{
+ struct twl4030_resconfig *resconfig = pdata->resource_config;
+ struct twl4030_resconfig *boardconf = pdata->board_config;
+ int err;
+
+ if (resconfig) {
+ if (boardconf)
+ twl4030_patch_rconfig(resconfig, boardconf);
+
+ while (resconfig->resource) {
+ err = twl4030_configure_resource(resconfig);
+ if (err)
+ return err;
+ resconfig++;
+ }
+ }
+
+ return 0;
+}
+
/*
* In master mode, start the power off sequence.
* After a successful execution, TWL shuts down the power to the SoC
@@ -521,60 +615,237 @@ void twl4030_power_off(void)
{
int err;
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, PWR_DEVOFF,
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, PWR_DEVOFF,
TWL4030_PM_MASTER_P1_SW_EVENTS);
if (err)
pr_err("TWL4030 Unable to power off\n");
}
-void __devinit twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
+static bool twl4030_power_use_poweroff(const struct twl4030_power_data *pdata,
+ struct device_node *node)
{
+ if (pdata && pdata->use_poweroff)
+ return true;
+
+ if (of_property_read_bool(node, "ti,use_poweroff"))
+ return true;
+
+ return false;
+}
+
+#ifdef CONFIG_OF
+
+/* Generic warm reset configuration for omap3 */
+
+static struct twl4030_ins omap3_wrst_seq[] = {
+ TWL_RESOURCE_OFF(RES_NRES_PWRON),
+ TWL_RESOURCE_OFF(RES_RESET),
+ TWL_RESOURCE_RESET(RES_MAIN_REF),
+ TWL_RESOURCE_GROUP_RESET(RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R2),
+ TWL_RESOURCE_RESET(RES_VUSB_3V1),
+ TWL_RESOURCE_GROUP_RESET(RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R1),
+ TWL_RESOURCE_GROUP_RESET(RES_GRP_RC, RES_TYPE_ALL, RES_TYPE2_R0),
+ TWL_RESOURCE_ON(RES_RESET),
+ TWL_RESOURCE_ON(RES_NRES_PWRON),
+};
+
+static struct twl4030_script omap3_wrst_script = {
+ .script = omap3_wrst_seq,
+ .size = ARRAY_SIZE(omap3_wrst_seq),
+ .flags = TWL4030_WRST_SCRIPT,
+};
+
+static struct twl4030_script *omap3_reset_scripts[] = {
+ &omap3_wrst_script,
+};
+
+static struct twl4030_resconfig omap3_rconfig[] = {
+ TWL_REMAP_SLEEP(RES_HFCLKOUT, DEV_GRP_P3, -1, -1),
+ TWL_REMAP_SLEEP(RES_VDD1, DEV_GRP_P1, -1, -1),
+ TWL_REMAP_SLEEP(RES_VDD2, DEV_GRP_P1, -1, -1),
+ { 0, 0 },
+};
+
+static struct twl4030_power_data omap3_reset = {
+ .scripts = omap3_reset_scripts,
+ .num = ARRAY_SIZE(omap3_reset_scripts),
+ .resource_config = omap3_rconfig,
+};
+
+/* Recommended generic default idle configuration for off-idle */
+
+/* Broadcast message to put res to sleep */
+static struct twl4030_ins omap3_idle_sleep_on_seq[] = {
+ TWL_RESOURCE_GROUP_SLEEP(RES_GRP_ALL, RES_TYPE_ALL, 0),
+};
+
+static struct twl4030_script omap3_idle_sleep_on_script = {
+ .script = omap3_idle_sleep_on_seq,
+ .size = ARRAY_SIZE(omap3_idle_sleep_on_seq),
+ .flags = TWL4030_SLEEP_SCRIPT,
+};
+
+/* Broadcast message to put res to active */
+static struct twl4030_ins omap3_idle_wakeup_p12_seq[] = {
+ TWL_RESOURCE_GROUP_ACTIVE(RES_GRP_ALL, RES_TYPE_ALL, 0),
+};
+
+static struct twl4030_script omap3_idle_wakeup_p12_script = {
+ .script = omap3_idle_wakeup_p12_seq,
+ .size = ARRAY_SIZE(omap3_idle_wakeup_p12_seq),
+ .flags = TWL4030_WAKEUP12_SCRIPT,
+};
+
+/* Broadcast message to put res to active */
+static struct twl4030_ins omap3_idle_wakeup_p3_seq[] = {
+ TWL_RESOURCE_SET_ACTIVE(RES_CLKEN, 0x37),
+ TWL_RESOURCE_GROUP_ACTIVE(RES_GRP_ALL, RES_TYPE_ALL, 0),
+};
+
+static struct twl4030_script omap3_idle_wakeup_p3_script = {
+ .script = omap3_idle_wakeup_p3_seq,
+ .size = ARRAY_SIZE(omap3_idle_wakeup_p3_seq),
+ .flags = TWL4030_WAKEUP3_SCRIPT,
+};
+
+static struct twl4030_script *omap3_idle_scripts[] = {
+ &omap3_idle_wakeup_p12_script,
+ &omap3_idle_wakeup_p3_script,
+ &omap3_wrst_script,
+ &omap3_idle_sleep_on_script,
+};
+
+/*
+ * Recommended configuration based on "Recommended Sleep
+ * Sequences for the Zoom Platform":
+ * http://omappedia.com/wiki/File:Recommended_Sleep_Sequences_Zoom.pdf
+ * Note that the type1 and type2 seem to be just the init order number
+ * for type1 and type2 groups as specified in the document mentioned
+ * above.
+ */
+static struct twl4030_resconfig omap3_idle_rconfig[] = {
+ TWL_REMAP_SLEEP(RES_VAUX1, DEV_GRP_NULL, 0, 0),
+ TWL_REMAP_SLEEP(RES_VAUX2, DEV_GRP_NULL, 0, 0),
+ TWL_REMAP_SLEEP(RES_VAUX3, DEV_GRP_NULL, 0, 0),
+ TWL_REMAP_SLEEP(RES_VAUX4, DEV_GRP_NULL, 0, 0),
+ TWL_REMAP_SLEEP(RES_VMMC1, DEV_GRP_NULL, 0, 0),
+ TWL_REMAP_SLEEP(RES_VMMC2, DEV_GRP_NULL, 0, 0),
+ TWL_REMAP_OFF(RES_VPLL1, DEV_GRP_P1, 3, 1),
+ TWL_REMAP_SLEEP(RES_VPLL2, DEV_GRP_P1, 0, 0),
+ TWL_REMAP_SLEEP(RES_VSIM, DEV_GRP_NULL, 0, 0),
+ TWL_REMAP_SLEEP(RES_VDAC, DEV_GRP_NULL, 0, 0),
+ TWL_REMAP_SLEEP(RES_VINTANA1, TWL_DEV_GRP_P123, 1, 2),
+ TWL_REMAP_SLEEP(RES_VINTANA2, TWL_DEV_GRP_P123, 0, 2),
+ TWL_REMAP_SLEEP(RES_VINTDIG, TWL_DEV_GRP_P123, 1, 2),
+ TWL_REMAP_SLEEP(RES_VIO, TWL_DEV_GRP_P123, 2, 2),
+ TWL_REMAP_OFF(RES_VDD1, DEV_GRP_P1, 4, 1),
+ TWL_REMAP_OFF(RES_VDD2, DEV_GRP_P1, 3, 1),
+ TWL_REMAP_SLEEP(RES_VUSB_1V5, DEV_GRP_NULL, 0, 0),
+ TWL_REMAP_SLEEP(RES_VUSB_1V8, DEV_GRP_NULL, 0, 0),
+ TWL_REMAP_SLEEP(RES_VUSB_3V1, TWL_DEV_GRP_P123, 0, 0),
+ /* Resource #20 USB charge pump skipped */
+ TWL_REMAP_SLEEP(RES_REGEN, TWL_DEV_GRP_P123, 2, 1),
+ TWL_REMAP_SLEEP(RES_NRES_PWRON, TWL_DEV_GRP_P123, 0, 1),
+ TWL_REMAP_SLEEP(RES_CLKEN, TWL_DEV_GRP_P123, 3, 2),
+ TWL_REMAP_SLEEP(RES_SYSEN, TWL_DEV_GRP_P123, 6, 1),
+ TWL_REMAP_SLEEP(RES_HFCLKOUT, DEV_GRP_P3, 0, 2),
+ TWL_REMAP_SLEEP(RES_32KCLKOUT, TWL_DEV_GRP_P123, 0, 0),
+ TWL_REMAP_SLEEP(RES_RESET, TWL_DEV_GRP_P123, 6, 0),
+ TWL_REMAP_SLEEP(RES_MAIN_REF, TWL_DEV_GRP_P123, 0, 0),
+ { /* Terminator */ },
+};
+
+static struct twl4030_power_data omap3_idle = {
+ .scripts = omap3_idle_scripts,
+ .num = ARRAY_SIZE(omap3_idle_scripts),
+ .resource_config = omap3_idle_rconfig,
+};
+
+/* Disable 32 KiHz oscillator during idle */
+static struct twl4030_resconfig osc_off_rconfig[] = {
+ TWL_REMAP_OFF(RES_CLKEN, DEV_GRP_P1 | DEV_GRP_P3, 3, 2),
+ { /* Terminator */ },
+};
+
+static struct twl4030_power_data osc_off_idle = {
+ .scripts = omap3_idle_scripts,
+ .num = ARRAY_SIZE(omap3_idle_scripts),
+ .resource_config = omap3_idle_rconfig,
+ .board_config = osc_off_rconfig,
+};
+
+static struct of_device_id twl4030_power_of_match[] = {
+ {
+ .compatible = "ti,twl4030-power-reset",
+ .data = &omap3_reset,
+ },
+ {
+ .compatible = "ti,twl4030-power-idle",
+ .data = &omap3_idle,
+ },
+ {
+ .compatible = "ti,twl4030-power-idle-osc-off",
+ .data = &osc_off_idle,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, twl4030_power_of_match);
+#endif /* CONFIG_OF */
+
+static int twl4030_power_probe(struct platform_device *pdev)
+{
+ const struct twl4030_power_data *pdata = dev_get_platdata(&pdev->dev);
+ struct device_node *node = pdev->dev.of_node;
+ const struct of_device_id *match;
int err = 0;
- int i;
- struct twl4030_resconfig *resconfig;
- u8 val, address = twl4030_start_script_address;
+ int err2 = 0;
+ u8 val;
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
- TWL4030_PM_MASTER_KEY_CFG1,
- TWL4030_PM_MASTER_PROTECT_KEY);
- if (err)
- goto unlock;
+ if (!pdata && !node) {
+ dev_err(&pdev->dev, "Platform data is missing\n");
+ return -EINVAL;
+ }
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
- TWL4030_PM_MASTER_KEY_CFG2,
- TWL4030_PM_MASTER_PROTECT_KEY);
- if (err)
- goto unlock;
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1,
+ TWL4030_PM_MASTER_PROTECT_KEY);
+ err |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER,
+ TWL4030_PM_MASTER_KEY_CFG2,
+ TWL4030_PM_MASTER_PROTECT_KEY);
- for (i = 0; i < twl4030_scripts->num; i++) {
- err = load_twl4030_script(twl4030_scripts->scripts[i], address);
- if (err)
- goto load;
- address += twl4030_scripts->scripts[i]->size;
+ if (err) {
+ pr_err("TWL4030 Unable to unlock registers\n");
+ return err;
}
- resconfig = twl4030_scripts->resource_config;
- if (resconfig) {
- while (resconfig->resource) {
- err = twl4030_configure_resource(resconfig);
- if (err)
- goto resource;
- resconfig++;
+ match = of_match_device(of_match_ptr(twl4030_power_of_match),
+ &pdev->dev);
+ if (match && match->data)
+ pdata = match->data;
+ if (pdata) {
+ err = twl4030_power_configure_scripts(pdata);
+ if (err) {
+ pr_err("TWL4030 failed to load scripts\n");
+ goto relock;
+ }
+ err = twl4030_power_configure_resources(pdata);
+ if (err) {
+ pr_err("TWL4030 failed to configure resource\n");
+ goto relock;
}
}
/* Board has to be wired properly to use this feature */
- if (twl4030_scripts->use_poweroff && !pm_power_off) {
+ if (twl4030_power_use_poweroff(pdata, node) && !pm_power_off) {
/* Default for SEQ_OFFSYNC is set, lets ensure this */
- err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &val,
+ err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &val,
TWL4030_PM_MASTER_CFG_P123_TRANSITION);
if (err) {
pr_warning("TWL4030 Unable to read registers\n");
} else if (!(val & SEQ_OFFSYNC)) {
val |= SEQ_OFFSYNC;
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, val,
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, val,
TWL4030_PM_MASTER_CFG_P123_TRANSITION);
if (err) {
pr_err("TWL4030 Unable to setup SEQ_OFFSYNC\n");
@@ -586,22 +857,35 @@ void __devinit twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
}
relock:
- err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
- TWL4030_PM_MASTER_PROTECT_KEY);
- if (err)
+ err2 = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0,
+ TWL4030_PM_MASTER_PROTECT_KEY);
+ if (err2) {
pr_err("TWL4030 Unable to relock registers\n");
- return;
+ return err2;
+ }
-unlock:
- if (err)
- pr_err("TWL4030 Unable to unlock registers\n");
- return;
-load:
- if (err)
- pr_err("TWL4030 failed to load scripts\n");
- return;
-resource:
- if (err)
- pr_err("TWL4030 failed to configure resource\n");
- return;
+ return err;
}
+
+static int twl4030_power_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver twl4030_power_driver = {
+ .driver = {
+ .name = "twl4030_power",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(twl4030_power_of_match),
+ },
+ .probe = twl4030_power_probe,
+ .remove = twl4030_power_remove,
+};
+
+module_platform_driver(twl4030_power_driver);
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_AUTHOR("Texas Instruments, Inc.");
+MODULE_DESCRIPTION("Power management for TWL4030");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:twl4030_power");
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c
index b76902f1e44..a6bb17d908b 100644
--- a/drivers/mfd/twl6030-irq.c
+++ b/drivers/mfd/twl6030-irq.c
@@ -31,7 +31,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/init.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -41,6 +40,7 @@
#include <linux/suspend.h>
#include <linux/of.h>
#include <linux/irqdomain.h>
+#include <linux/of_device.h>
#include "twl-core.h"
@@ -84,39 +84,77 @@ static int twl6030_interrupt_mapping[24] = {
CHARGERFAULT_INTR_OFFSET, /* Bit 22 INT_CHRG */
RSV_INTR_OFFSET, /* Bit 23 Reserved */
};
+
+static int twl6032_interrupt_mapping[24] = {
+ PWR_INTR_OFFSET, /* Bit 0 PWRON */
+ PWR_INTR_OFFSET, /* Bit 1 RPWRON */
+ PWR_INTR_OFFSET, /* Bit 2 SYS_VLOW */
+ RTC_INTR_OFFSET, /* Bit 3 RTC_ALARM */
+ RTC_INTR_OFFSET, /* Bit 4 RTC_PERIOD */
+ HOTDIE_INTR_OFFSET, /* Bit 5 HOT_DIE */
+ SMPSLDO_INTR_OFFSET, /* Bit 6 VXXX_SHORT */
+ PWR_INTR_OFFSET, /* Bit 7 SPDURATION */
+
+ PWR_INTR_OFFSET, /* Bit 8 WATCHDOG */
+ BATDETECT_INTR_OFFSET, /* Bit 9 BAT */
+ SIMDETECT_INTR_OFFSET, /* Bit 10 SIM */
+ MMCDETECT_INTR_OFFSET, /* Bit 11 MMC */
+ MADC_INTR_OFFSET, /* Bit 12 GPADC_RT_EOC */
+ MADC_INTR_OFFSET, /* Bit 13 GPADC_SW_EOC */
+ GASGAUGE_INTR_OFFSET, /* Bit 14 CC_EOC */
+ GASGAUGE_INTR_OFFSET, /* Bit 15 CC_AUTOCAL */
+
+ USBOTG_INTR_OFFSET, /* Bit 16 ID_WKUP */
+ USBOTG_INTR_OFFSET, /* Bit 17 VBUS_WKUP */
+ USBOTG_INTR_OFFSET, /* Bit 18 ID */
+ USB_PRES_INTR_OFFSET, /* Bit 19 VBUS */
+ CHARGER_INTR_OFFSET, /* Bit 20 CHRG_CTRL */
+ CHARGERFAULT_INTR_OFFSET, /* Bit 21 EXT_CHRG */
+ CHARGERFAULT_INTR_OFFSET, /* Bit 22 INT_CHRG */
+ RSV_INTR_OFFSET, /* Bit 23 Reserved */
+};
+
/*----------------------------------------------------------------------*/
-static unsigned twl6030_irq_base;
-static int twl_irq;
-static bool twl_irq_wake_enabled;
+struct twl6030_irq {
+ unsigned int irq_base;
+ int twl_irq;
+ bool irq_wake_enabled;
+ atomic_t wakeirqs;
+ struct notifier_block pm_nb;
+ struct irq_chip irq_chip;
+ struct irq_domain *irq_domain;
+ const int *irq_mapping_tbl;
+};
-static struct completion irq_event;
-static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0);
+static struct twl6030_irq *twl6030_irq;
static int twl6030_irq_pm_notifier(struct notifier_block *notifier,
unsigned long pm_event, void *unused)
{
int chained_wakeups;
+ struct twl6030_irq *pdata = container_of(notifier, struct twl6030_irq,
+ pm_nb);
switch (pm_event) {
case PM_SUSPEND_PREPARE:
- chained_wakeups = atomic_read(&twl6030_wakeirqs);
+ chained_wakeups = atomic_read(&pdata->wakeirqs);
- if (chained_wakeups && !twl_irq_wake_enabled) {
- if (enable_irq_wake(twl_irq))
+ if (chained_wakeups && !pdata->irq_wake_enabled) {
+ if (enable_irq_wake(pdata->twl_irq))
pr_err("twl6030 IRQ wake enable failed\n");
else
- twl_irq_wake_enabled = true;
- } else if (!chained_wakeups && twl_irq_wake_enabled) {
- disable_irq_wake(twl_irq);
- twl_irq_wake_enabled = false;
+ pdata->irq_wake_enabled = true;
+ } else if (!chained_wakeups && pdata->irq_wake_enabled) {
+ disable_irq_wake(pdata->twl_irq);
+ pdata->irq_wake_enabled = false;
}
- disable_irq(twl_irq);
+ disable_irq(pdata->twl_irq);
break;
case PM_POST_SUSPEND:
- enable_irq(twl_irq);
+ enable_irq(pdata->twl_irq);
break;
default:
@@ -126,124 +164,79 @@ static int twl6030_irq_pm_notifier(struct notifier_block *notifier,
return NOTIFY_DONE;
}
-static struct notifier_block twl6030_irq_pm_notifier_block = {
- .notifier_call = twl6030_irq_pm_notifier,
-};
-
/*
- * This thread processes interrupts reported by the Primary Interrupt Handler.
- */
-static int twl6030_irq_thread(void *data)
+* Threaded irq handler for the twl6030 interrupt.
+* We query the interrupt controller in the twl6030 to determine
+* which module is generating the interrupt request and call
+* handle_nested_irq for that module.
+*/
+static irqreturn_t twl6030_irq_thread(int irq, void *data)
{
- long irq = (long)data;
- static unsigned i2c_errors;
- static const unsigned max_i2c_errors = 100;
- int ret;
-
- while (!kthread_should_stop()) {
- int i;
- union {
+ int i, ret;
+ union {
u8 bytes[4];
- u32 int_sts;
- } sts;
-
- /* Wait for IRQ, then read PIH irq status (also blocking) */
- wait_for_completion_interruptible(&irq_event);
-
- /* read INT_STS_A, B and C in one shot using a burst read */
- ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes,
- REG_INT_STS_A, 3);
- if (ret) {
- pr_warning("twl6030: I2C error %d reading PIH ISR\n",
- ret);
- if (++i2c_errors >= max_i2c_errors) {
- printk(KERN_ERR "Maximum I2C error count"
- " exceeded. Terminating %s.\n",
- __func__);
- break;
- }
- complete(&irq_event);
- continue;
- }
-
-
+ __le32 int_sts;
+ } sts;
+ u32 int_sts; /* sts.int_sts converted to CPU endianness */
+ struct twl6030_irq *pdata = data;
+
+ /* read INT_STS_A, B and C in one shot using a burst read */
+ ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes, REG_INT_STS_A, 3);
+ if (ret) {
+ pr_warn("twl6030_irq: I2C error %d reading PIH ISR\n", ret);
+ return IRQ_HANDLED;
+ }
- sts.bytes[3] = 0; /* Only 24 bits are valid*/
+ sts.bytes[3] = 0; /* Only 24 bits are valid*/
- /*
- * Since VBUS status bit is not reliable for VBUS disconnect
- * use CHARGER VBUS detection status bit instead.
- */
- if (sts.bytes[2] & 0x10)
- sts.bytes[2] |= 0x08;
-
- for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) {
- local_irq_disable();
- if (sts.int_sts & 0x1) {
- int module_irq = twl6030_irq_base +
- twl6030_interrupt_mapping[i];
- generic_handle_irq(module_irq);
-
- }
- local_irq_enable();
+ /*
+ * Since VBUS status bit is not reliable for VBUS disconnect
+ * use CHARGER VBUS detection status bit instead.
+ */
+ if (sts.bytes[2] & 0x10)
+ sts.bytes[2] |= 0x08;
+
+ int_sts = le32_to_cpu(sts.int_sts);
+ for (i = 0; int_sts; int_sts >>= 1, i++)
+ if (int_sts & 0x1) {
+ int module_irq =
+ irq_find_mapping(pdata->irq_domain,
+ pdata->irq_mapping_tbl[i]);
+ if (module_irq)
+ handle_nested_irq(module_irq);
+ else
+ pr_err("twl6030_irq: Unmapped PIH ISR %u detected\n",
+ i);
+ pr_debug("twl6030_irq: PIH ISR %u, virq%u\n",
+ i, module_irq);
}
- /*
- * NOTE:
- * Simulation confirms that documentation is wrong w.r.t the
- * interrupt status clear operation. A single *byte* write to
- * any one of STS_A to STS_C register results in all three
- * STS registers being reset. Since it does not matter which
- * value is written, all three registers are cleared on a
- * single byte write, so we just use 0x0 to clear.
- */
- ret = twl_i2c_write_u8(TWL_MODULE_PIH, 0x00, REG_INT_STS_A);
- if (ret)
- pr_warning("twl6030: I2C error in clearing PIH ISR\n");
-
- enable_irq(irq);
- }
-
- return 0;
-}
+ /*
+ * NOTE:
+ * Simulation confirms that documentation is wrong w.r.t the
+ * interrupt status clear operation. A single *byte* write to
+ * any one of STS_A to STS_C register results in all three
+ * STS registers being reset. Since it does not matter which
+ * value is written, all three registers are cleared on a
+ * single byte write, so we just use 0x0 to clear.
+ */
+ ret = twl_i2c_write_u8(TWL_MODULE_PIH, 0x00, REG_INT_STS_A);
+ if (ret)
+ pr_warn("twl6030_irq: I2C error in clearing PIH ISR\n");
-/*
- * handle_twl6030_int() is the desc->handle method for the twl6030 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 twl6030 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 irqreturn_t handle_twl6030_pih(int irq, void *devid)
-{
- disable_irq_nosync(irq);
- complete(devid);
return IRQ_HANDLED;
}
/*----------------------------------------------------------------------*/
-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 */
- irq_set_noprobe(irq);
-#endif
-}
-
static int twl6030_irq_set_wake(struct irq_data *d, unsigned int on)
{
+ struct twl6030_irq *pdata = irq_get_chip_data(d->irq);
+
if (on)
- atomic_inc(&twl6030_wakeirqs);
+ atomic_inc(&pdata->wakeirqs);
else
- atomic_dec(&twl6030_wakeirqs);
+ atomic_dec(&pdata->wakeirqs);
return 0;
}
@@ -318,7 +311,8 @@ int twl6030_mmc_card_detect_config(void)
return ret;
}
- return twl6030_irq_base + MMCDETECT_INTR_OFFSET;
+ return irq_find_mapping(twl6030_irq->irq_domain,
+ MMCDETECT_INTR_OFFSET);
}
EXPORT_SYMBOL(twl6030_mmc_card_detect_config);
@@ -347,99 +341,143 @@ int twl6030_mmc_card_detect(struct device *dev, int slot)
}
EXPORT_SYMBOL(twl6030_mmc_card_detect);
+static int twl6030_irq_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hwirq)
+{
+ struct twl6030_irq *pdata = d->host_data;
+
+ irq_set_chip_data(virq, pdata);
+ irq_set_chip_and_handler(virq, &pdata->irq_chip, handle_simple_irq);
+ irq_set_nested_thread(virq, true);
+ irq_set_parent(virq, pdata->twl_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(virq, IRQF_VALID);
+#else
+ /* same effect on other architectures */
+ irq_set_noprobe(virq);
+#endif
+
+ return 0;
+}
+
+static void twl6030_irq_unmap(struct irq_domain *d, unsigned int virq)
+{
+#ifdef CONFIG_ARM
+ set_irq_flags(virq, 0);
+#endif
+ irq_set_chip_and_handler(virq, NULL, NULL);
+ irq_set_chip_data(virq, NULL);
+}
+
+static struct irq_domain_ops twl6030_irq_domain_ops = {
+ .map = twl6030_irq_map,
+ .unmap = twl6030_irq_unmap,
+ .xlate = irq_domain_xlate_onetwocell,
+};
+
+static const struct of_device_id twl6030_of_match[] = {
+ {.compatible = "ti,twl6030", &twl6030_interrupt_mapping},
+ {.compatible = "ti,twl6032", &twl6032_interrupt_mapping},
+ { },
+};
+
int twl6030_init_irq(struct device *dev, int irq_num)
{
struct device_node *node = dev->of_node;
- int nr_irqs, irq_base, irq_end;
- struct task_struct *task;
- static struct irq_chip twl6030_irq_chip;
- int status = 0;
- int i;
- u8 mask[4];
+ int nr_irqs;
+ int status;
+ u8 mask[3];
+ const struct of_device_id *of_id;
+
+ of_id = of_match_device(twl6030_of_match, dev);
+ if (!of_id || !of_id->data) {
+ dev_err(dev, "Unknown TWL device model\n");
+ return -EINVAL;
+ }
nr_irqs = TWL6030_NR_IRQS;
- irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
- if (IS_ERR_VALUE(irq_base)) {
- dev_err(dev, "Fail to allocate IRQ descs\n");
- return irq_base;
+ twl6030_irq = devm_kzalloc(dev, sizeof(*twl6030_irq), GFP_KERNEL);
+ if (!twl6030_irq) {
+ dev_err(dev, "twl6030_irq: Memory allocation failed\n");
+ return -ENOMEM;
}
- irq_domain_add_legacy(node, nr_irqs, irq_base, 0,
- &irq_domain_simple_ops, NULL);
-
- irq_end = irq_base + nr_irqs;
-
+ mask[0] = 0xFF;
mask[1] = 0xFF;
mask[2] = 0xFF;
- mask[3] = 0xFF;
/* mask all int lines */
- twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_LINE_A, 3);
+ status = twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_LINE_A, 3);
/* mask all int sts */
- twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_STS_A, 3);
+ status |= twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_STS_A, 3);
/* clear INT_STS_A,B,C */
- twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_STS_A, 3);
+ status |= twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_STS_A, 3);
- twl6030_irq_base = irq_base;
+ if (status < 0) {
+ dev_err(dev, "I2C err writing TWL_MODULE_PIH: %d\n", status);
+ return status;
+ }
/*
* install an irq handler for each of the modules;
* clone dummy irq_chip since PIH can't *do* anything
*/
- twl6030_irq_chip = dummy_irq_chip;
- twl6030_irq_chip.name = "twl6030";
- twl6030_irq_chip.irq_set_type = NULL;
- twl6030_irq_chip.irq_set_wake = twl6030_irq_set_wake;
-
- for (i = irq_base; i < irq_end; i++) {
- irq_set_chip_and_handler(i, &twl6030_irq_chip,
- handle_simple_irq);
- irq_set_chip_data(i, (void *)irq_num);
- activate_irq(i);
+ twl6030_irq->irq_chip = dummy_irq_chip;
+ twl6030_irq->irq_chip.name = "twl6030";
+ twl6030_irq->irq_chip.irq_set_type = NULL;
+ twl6030_irq->irq_chip.irq_set_wake = twl6030_irq_set_wake;
+
+ twl6030_irq->pm_nb.notifier_call = twl6030_irq_pm_notifier;
+ atomic_set(&twl6030_irq->wakeirqs, 0);
+ twl6030_irq->irq_mapping_tbl = of_id->data;
+
+ twl6030_irq->irq_domain =
+ irq_domain_add_linear(node, nr_irqs,
+ &twl6030_irq_domain_ops, twl6030_irq);
+ if (!twl6030_irq->irq_domain) {
+ dev_err(dev, "Can't add irq_domain\n");
+ return -ENOMEM;
}
- dev_info(dev, "PIH (irq %d) chaining IRQs %d..%d\n",
- irq_num, irq_base, irq_end);
+ dev_info(dev, "PIH (irq %d) nested IRQs\n", irq_num);
/* install an irq handler to demultiplex the TWL6030 interrupt */
- init_completion(&irq_event);
-
- status = request_irq(irq_num, handle_twl6030_pih, 0, "TWL6030-PIH",
- &irq_event);
+ status = request_threaded_irq(irq_num, NULL, twl6030_irq_thread,
+ IRQF_ONESHOT, "TWL6030-PIH", twl6030_irq);
if (status < 0) {
dev_err(dev, "could not claim irq %d: %d\n", irq_num, status);
goto fail_irq;
}
- task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");
- if (IS_ERR(task)) {
- dev_err(dev, "could not create irq %d thread!\n", irq_num);
- status = PTR_ERR(task);
- goto fail_kthread;
- }
-
- twl_irq = irq_num;
- register_pm_notifier(&twl6030_irq_pm_notifier_block);
- return irq_base;
-
-fail_kthread:
- free_irq(irq_num, &irq_event);
+ twl6030_irq->twl_irq = irq_num;
+ register_pm_notifier(&twl6030_irq->pm_nb);
+ return 0;
fail_irq:
- for (i = irq_base; i < irq_end; i++)
- irq_set_chip_and_handler(i, NULL, NULL);
-
+ irq_domain_remove(twl6030_irq->irq_domain);
return status;
}
int twl6030_exit_irq(void)
{
- unregister_pm_notifier(&twl6030_irq_pm_notifier_block);
-
- if (twl6030_irq_base) {
- pr_err("twl6030: can't yet clean up IRQs?\n");
- return -ENOSYS;
+ if (twl6030_irq && twl6030_irq->twl_irq) {
+ unregister_pm_notifier(&twl6030_irq->pm_nb);
+ free_irq(twl6030_irq->twl_irq, NULL);
+ /*
+ * TODO: IRQ domain and allocated nested IRQ descriptors
+ * should be freed somehow here. Now It can't be done, because
+ * child devices will not be deleted during removing of
+ * TWL Core driver and they will still contain allocated
+ * virt IRQs in their Resources tables.
+ * The same prevents us from using devm_request_threaded_irq()
+ * in this module.
+ */
}
return 0;
}
diff --git a/drivers/mfd/twl6040-irq.c b/drivers/mfd/twl6040-irq.c
deleted file mode 100644
index 4b42543da22..00000000000
--- a/drivers/mfd/twl6040-irq.c
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Interrupt controller support for TWL6040
- *
- * Author: Misael Lopez Cruz <misael.lopez@ti.com>
- *
- * Copyright: (C) 2011 Texas Instruments, Inc.
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/irq.h>
-#include <linux/of.h>
-#include <linux/irqdomain.h>
-#include <linux/interrupt.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/twl6040.h>
-
-struct twl6040_irq_data {
- int mask;
- int status;
-};
-
-static struct twl6040_irq_data twl6040_irqs[] = {
- {
- .mask = TWL6040_THMSK,
- .status = TWL6040_THINT,
- },
- {
- .mask = TWL6040_PLUGMSK,
- .status = TWL6040_PLUGINT | TWL6040_UNPLUGINT,
- },
- {
- .mask = TWL6040_HOOKMSK,
- .status = TWL6040_HOOKINT,
- },
- {
- .mask = TWL6040_HFMSK,
- .status = TWL6040_HFINT,
- },
- {
- .mask = TWL6040_VIBMSK,
- .status = TWL6040_VIBINT,
- },
- {
- .mask = TWL6040_READYMSK,
- .status = TWL6040_READYINT,
- },
-};
-
-static inline
-struct twl6040_irq_data *irq_to_twl6040_irq(struct twl6040 *twl6040,
- int irq)
-{
- return &twl6040_irqs[irq - twl6040->irq_base];
-}
-
-static void twl6040_irq_lock(struct irq_data *data)
-{
- struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data);
-
- mutex_lock(&twl6040->irq_mutex);
-}
-
-static void twl6040_irq_sync_unlock(struct irq_data *data)
-{
- struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data);
-
- /* write back to hardware any change in irq mask */
- if (twl6040->irq_masks_cur != twl6040->irq_masks_cache) {
- twl6040->irq_masks_cache = twl6040->irq_masks_cur;
- twl6040_reg_write(twl6040, TWL6040_REG_INTMR,
- twl6040->irq_masks_cur);
- }
-
- mutex_unlock(&twl6040->irq_mutex);
-}
-
-static void twl6040_irq_enable(struct irq_data *data)
-{
- struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data);
- struct twl6040_irq_data *irq_data = irq_to_twl6040_irq(twl6040,
- data->irq);
-
- twl6040->irq_masks_cur &= ~irq_data->mask;
-}
-
-static void twl6040_irq_disable(struct irq_data *data)
-{
- struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data);
- struct twl6040_irq_data *irq_data = irq_to_twl6040_irq(twl6040,
- data->irq);
-
- twl6040->irq_masks_cur |= irq_data->mask;
-}
-
-static struct irq_chip twl6040_irq_chip = {
- .name = "twl6040",
- .irq_bus_lock = twl6040_irq_lock,
- .irq_bus_sync_unlock = twl6040_irq_sync_unlock,
- .irq_enable = twl6040_irq_enable,
- .irq_disable = twl6040_irq_disable,
-};
-
-static irqreturn_t twl6040_irq_thread(int irq, void *data)
-{
- struct twl6040 *twl6040 = data;
- u8 intid;
- int i;
-
- intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
-
- /* apply masking and report (backwards to handle READYINT first) */
- for (i = ARRAY_SIZE(twl6040_irqs) - 1; i >= 0; i--) {
- if (twl6040->irq_masks_cur & twl6040_irqs[i].mask)
- intid &= ~twl6040_irqs[i].status;
- if (intid & twl6040_irqs[i].status)
- handle_nested_irq(twl6040->irq_base + i);
- }
-
- /* ack unmasked irqs */
- twl6040_reg_write(twl6040, TWL6040_REG_INTID, intid);
-
- return IRQ_HANDLED;
-}
-
-int twl6040_irq_init(struct twl6040 *twl6040)
-{
- struct device_node *node = twl6040->dev->of_node;
- int i, nr_irqs, irq_base, ret;
- u8 val;
-
- mutex_init(&twl6040->irq_mutex);
-
- /* mask the individual interrupt sources */
- twl6040->irq_masks_cur = TWL6040_ALLINT_MSK;
- twl6040->irq_masks_cache = TWL6040_ALLINT_MSK;
- twl6040_reg_write(twl6040, TWL6040_REG_INTMR, TWL6040_ALLINT_MSK);
-
- nr_irqs = ARRAY_SIZE(twl6040_irqs);
-
- irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
- if (IS_ERR_VALUE(irq_base)) {
- dev_err(twl6040->dev, "Fail to allocate IRQ descs\n");
- return irq_base;
- }
- twl6040->irq_base = irq_base;
-
- irq_domain_add_legacy(node, ARRAY_SIZE(twl6040_irqs), irq_base, 0,
- &irq_domain_simple_ops, NULL);
-
- /* Register them with genirq */
- for (i = irq_base; i < irq_base + nr_irqs; i++) {
- irq_set_chip_data(i, twl6040);
- irq_set_chip_and_handler(i, &twl6040_irq_chip,
- handle_level_irq);
- irq_set_nested_thread(i, 1);
-
- /* ARM needs us to explicitly flag the IRQ as valid
- * and will set them noprobe when we do so. */
-#ifdef CONFIG_ARM
- set_irq_flags(i, IRQF_VALID);
-#else
- irq_set_noprobe(i);
-#endif
- }
-
- ret = request_threaded_irq(twl6040->irq, NULL, twl6040_irq_thread,
- IRQF_ONESHOT, "twl6040", twl6040);
- if (ret) {
- dev_err(twl6040->dev, "failed to request IRQ %d: %d\n",
- twl6040->irq, ret);
- return ret;
- }
-
- /* reset interrupts */
- val = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
-
- /* interrupts cleared on write */
- twl6040_clear_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_INTCLRMODE);
-
- return 0;
-}
-EXPORT_SYMBOL(twl6040_irq_init);
-
-void twl6040_irq_exit(struct twl6040 *twl6040)
-{
- free_irq(twl6040->irq, twl6040);
-}
-EXPORT_SYMBOL(twl6040_irq_exit);
diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040.c
index 3f2a1cf02fc..ae26d84b3a5 100644
--- a/drivers/mfd/twl6040-core.c
+++ b/drivers/mfd/twl6040.c
@@ -37,7 +37,6 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
-#include <linux/err.h>
#include <linux/mfd/core.h>
#include <linux/mfd/twl6040.h>
#include <linux/regulator/consumer.h>
@@ -45,17 +44,65 @@
#define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1)
#define TWL6040_NUM_SUPPLIES (2)
-static bool twl6040_has_vibra(struct twl6040_platform_data *pdata,
- struct device_node *node)
-{
- if (pdata && pdata->vibra)
- return true;
+static struct reg_default twl6040_defaults[] = {
+ { 0x01, 0x4B }, /* REG_ASICID (ro) */
+ { 0x02, 0x00 }, /* REG_ASICREV (ro) */
+ { 0x03, 0x00 }, /* REG_INTID */
+ { 0x04, 0x00 }, /* REG_INTMR */
+ { 0x05, 0x00 }, /* REG_NCPCTRL */
+ { 0x06, 0x00 }, /* REG_LDOCTL */
+ { 0x07, 0x60 }, /* REG_HPPLLCTL */
+ { 0x08, 0x00 }, /* REG_LPPLLCTL */
+ { 0x09, 0x4A }, /* REG_LPPLLDIV */
+ { 0x0A, 0x00 }, /* REG_AMICBCTL */
+ { 0x0B, 0x00 }, /* REG_DMICBCTL */
+ { 0x0C, 0x00 }, /* REG_MICLCTL */
+ { 0x0D, 0x00 }, /* REG_MICRCTL */
+ { 0x0E, 0x00 }, /* REG_MICGAIN */
+ { 0x0F, 0x1B }, /* REG_LINEGAIN */
+ { 0x10, 0x00 }, /* REG_HSLCTL */
+ { 0x11, 0x00 }, /* REG_HSRCTL */
+ { 0x12, 0x00 }, /* REG_HSGAIN */
+ { 0x13, 0x00 }, /* REG_EARCTL */
+ { 0x14, 0x00 }, /* REG_HFLCTL */
+ { 0x15, 0x00 }, /* REG_HFLGAIN */
+ { 0x16, 0x00 }, /* REG_HFRCTL */
+ { 0x17, 0x00 }, /* REG_HFRGAIN */
+ { 0x18, 0x00 }, /* REG_VIBCTLL */
+ { 0x19, 0x00 }, /* REG_VIBDATL */
+ { 0x1A, 0x00 }, /* REG_VIBCTLR */
+ { 0x1B, 0x00 }, /* REG_VIBDATR */
+ { 0x1C, 0x00 }, /* REG_HKCTL1 */
+ { 0x1D, 0x00 }, /* REG_HKCTL2 */
+ { 0x1E, 0x00 }, /* REG_GPOCTL */
+ { 0x1F, 0x00 }, /* REG_ALB */
+ { 0x20, 0x00 }, /* REG_DLB */
+ /* 0x28, REG_TRIM1 */
+ /* 0x29, REG_TRIM2 */
+ /* 0x2A, REG_TRIM3 */
+ /* 0x2B, REG_HSOTRIM */
+ /* 0x2C, REG_HFOTRIM */
+ { 0x2D, 0x08 }, /* REG_ACCCTL */
+ { 0x2E, 0x00 }, /* REG_STATUS (ro) */
+};
+
+static struct reg_default twl6040_patch[] = {
+ /*
+ * Select I2C bus access to dual access registers
+ * Interrupt register is cleared on read
+ * Select fast mode for i2c (400KHz)
+ */
+ { TWL6040_REG_ACCCTL,
+ TWL6040_I2CSEL | TWL6040_INTCLRMODE | TWL6040_I2CMODE(1) },
+};
+
+static bool twl6040_has_vibra(struct device_node *node)
+{
#ifdef CONFIG_OF
if (of_find_node_by_name(node, "vibra"))
return true;
#endif
-
return false;
}
@@ -64,15 +111,9 @@ int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg)
int ret;
unsigned int val;
- /* Vibra control registers from cache */
- if (unlikely(reg == TWL6040_REG_VIBCTLL ||
- reg == TWL6040_REG_VIBCTLR)) {
- val = twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)];
- } else {
- ret = regmap_read(twl6040->regmap, reg, &val);
- if (ret < 0)
- return ret;
- }
+ ret = regmap_read(twl6040->regmap, reg, &val);
+ if (ret < 0)
+ return ret;
return val;
}
@@ -83,9 +124,6 @@ int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val)
int ret;
ret = regmap_write(twl6040->regmap, reg, val);
- /* Cache the vibra control registers */
- if (reg == TWL6040_REG_VIBCTLL || reg == TWL6040_REG_VIBCTLR)
- twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)] = val;
return ret;
}
@@ -104,7 +142,7 @@ int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
EXPORT_SYMBOL(twl6040_clear_bits);
/* twl6040 codec manual power-up sequence */
-static int twl6040_power_up(struct twl6040 *twl6040)
+static int twl6040_power_up_manual(struct twl6040 *twl6040)
{
u8 ldoctl, ncpctl, lppllctl;
int ret;
@@ -158,11 +196,12 @@ ncp_err:
ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+ dev_err(twl6040->dev, "manual power-up failed\n");
return ret;
}
/* twl6040 manual power-down sequence */
-static void twl6040_power_down(struct twl6040 *twl6040)
+static void twl6040_power_down_manual(struct twl6040 *twl6040)
{
u8 ncpctl, ldoctl, lppllctl;
@@ -192,45 +231,48 @@ static void twl6040_power_down(struct twl6040 *twl6040)
twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
}
-static irqreturn_t twl6040_naudint_handler(int irq, void *data)
+static irqreturn_t twl6040_readyint_handler(int irq, void *data)
{
struct twl6040 *twl6040 = data;
- u8 intid, status;
- intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
+ complete(&twl6040->ready);
- if (intid & TWL6040_READYINT)
- complete(&twl6040->ready);
+ return IRQ_HANDLED;
+}
- if (intid & TWL6040_THINT) {
- status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
- if (status & TWL6040_TSHUTDET) {
- dev_warn(twl6040->dev,
- "Thermal shutdown, powering-off");
- twl6040_power(twl6040, 0);
- } else {
- dev_warn(twl6040->dev,
- "Leaving thermal shutdown, powering-on");
- twl6040_power(twl6040, 1);
- }
+static irqreturn_t twl6040_thint_handler(int irq, void *data)
+{
+ struct twl6040 *twl6040 = data;
+ u8 status;
+
+ status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
+ if (status & TWL6040_TSHUTDET) {
+ dev_warn(twl6040->dev, "Thermal shutdown, powering-off");
+ twl6040_power(twl6040, 0);
+ } else {
+ dev_warn(twl6040->dev, "Leaving thermal shutdown, powering-on");
+ twl6040_power(twl6040, 1);
}
return IRQ_HANDLED;
}
-static int twl6040_power_up_completion(struct twl6040 *twl6040,
- int naudint)
+static int twl6040_power_up_automatic(struct twl6040 *twl6040)
{
int time_left;
- u8 intid;
+
+ gpio_set_value(twl6040->audpwron, 1);
time_left = wait_for_completion_timeout(&twl6040->ready,
msecs_to_jiffies(144));
if (!time_left) {
+ u8 intid;
+
+ dev_warn(twl6040->dev, "timeout waiting for READYINT\n");
intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
if (!(intid & TWL6040_READYINT)) {
- dev_err(twl6040->dev,
- "timeout waiting for READYINT\n");
+ dev_err(twl6040->dev, "automatic power-up failed\n");
+ gpio_set_value(twl6040->audpwron, 0);
return -ETIMEDOUT;
}
}
@@ -240,8 +282,6 @@ static int twl6040_power_up_completion(struct twl6040 *twl6040,
int twl6040_power(struct twl6040 *twl6040, int on)
{
- int audpwron = twl6040->audpwron;
- int naudint = twl6040->irq;
int ret = 0;
mutex_lock(&twl6040->mutex);
@@ -251,27 +291,30 @@ int twl6040_power(struct twl6040 *twl6040, int on)
if (twl6040->power_count++)
goto out;
- if (gpio_is_valid(audpwron)) {
- /* use AUDPWRON line */
- gpio_set_value(audpwron, 1);
- /* wait for power-up completion */
- ret = twl6040_power_up_completion(twl6040, naudint);
+ clk_prepare_enable(twl6040->clk32k);
+
+ /* Allow writes to the chip */
+ regcache_cache_only(twl6040->regmap, false);
+
+ if (gpio_is_valid(twl6040->audpwron)) {
+ /* use automatic power-up sequence */
+ ret = twl6040_power_up_automatic(twl6040);
if (ret) {
- dev_err(twl6040->dev,
- "automatic power-down failed\n");
twl6040->power_count = 0;
goto out;
}
} else {
/* use manual power-up sequence */
- ret = twl6040_power_up(twl6040);
+ ret = twl6040_power_up_manual(twl6040);
if (ret) {
- dev_err(twl6040->dev,
- "manual power-up failed\n");
twl6040->power_count = 0;
goto out;
}
}
+
+ /* Sync with the HW */
+ regcache_sync(twl6040->regmap);
+
/* Default PLL configuration after power up */
twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL;
twl6040->sysclk = 19200000;
@@ -288,18 +331,25 @@ int twl6040_power(struct twl6040 *twl6040, int on)
if (--twl6040->power_count)
goto out;
- if (gpio_is_valid(audpwron)) {
+ if (gpio_is_valid(twl6040->audpwron)) {
/* use AUDPWRON line */
- gpio_set_value(audpwron, 0);
+ gpio_set_value(twl6040->audpwron, 0);
/* power-down sequence latency */
usleep_range(500, 700);
} else {
/* use manual power-down sequence */
- twl6040_power_down(twl6040);
+ twl6040_power_down_manual(twl6040);
}
+
+ /* Set regmap to cache only and mark it as dirty */
+ regcache_cache_only(twl6040->regmap, true);
+ regcache_mark_dirty(twl6040->regmap);
+
twl6040->sysclk = 0;
twl6040->mclk = 0;
+
+ clk_disable_unprepare(twl6040->clk32k);
}
out:
@@ -391,12 +441,9 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
TWL6040_HPLLENA;
break;
case 19200000:
- /*
- * PLL disabled
- * (enable PLL if MCLK jitter quality
- * doesn't meet specification)
- */
- hppllctl |= TWL6040_MCLK_19200KHZ;
+ /* PLL enabled, bypass mode */
+ hppllctl |= TWL6040_MCLK_19200KHZ |
+ TWL6040_HPLLBP | TWL6040_HPLLENA;
break;
case 26000000:
/* PLL enabled, active mode */
@@ -404,9 +451,9 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
TWL6040_HPLLENA;
break;
case 38400000:
- /* PLL enabled, active mode */
+ /* PLL enabled, bypass mode */
hppllctl |= TWL6040_MCLK_38400KHZ |
- TWL6040_HPLLENA;
+ TWL6040_HPLLBP | TWL6040_HPLLENA;
break;
default:
dev_err(twl6040->dev,
@@ -466,9 +513,20 @@ EXPORT_SYMBOL(twl6040_get_sysclk);
/* Get the combined status of the vibra control register */
int twl6040_get_vibralr_status(struct twl6040 *twl6040)
{
+ unsigned int reg;
+ int ret;
u8 status;
- status = twl6040->vibra_ctrl_cache[0] | twl6040->vibra_ctrl_cache[1];
+ ret = regmap_read(twl6040->regmap, TWL6040_REG_VIBCTLL, &reg);
+ if (ret != 0)
+ return ret;
+ status = reg;
+
+ ret = regmap_read(twl6040->regmap, TWL6040_REG_VIBCTLR, &reg);
+ if (ret != 0)
+ return ret;
+ status |= reg;
+
status &= (TWL6040_VIBENA | TWL6040_VIBSEL);
return status;
@@ -495,25 +553,78 @@ static bool twl6040_readable_reg(struct device *dev, unsigned int reg)
return true;
}
+static bool twl6040_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TWL6040_REG_ASICID:
+ case TWL6040_REG_ASICREV:
+ case TWL6040_REG_INTID:
+ case TWL6040_REG_LPPLLCTL:
+ case TWL6040_REG_HPPLLCTL:
+ case TWL6040_REG_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool twl6040_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TWL6040_REG_ASICID:
+ case TWL6040_REG_ASICREV:
+ case TWL6040_REG_STATUS:
+ return false;
+ default:
+ return true;
+ }
+}
+
static struct regmap_config twl6040_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
+
+ .reg_defaults = twl6040_defaults,
+ .num_reg_defaults = ARRAY_SIZE(twl6040_defaults),
+
.max_register = TWL6040_REG_STATUS, /* 0x2e */
.readable_reg = twl6040_readable_reg,
+ .volatile_reg = twl6040_volatile_reg,
+ .writeable_reg = twl6040_writeable_reg,
+
+ .cache_type = REGCACHE_RBTREE,
};
-static int __devinit twl6040_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static const struct regmap_irq twl6040_irqs[] = {
+ { .reg_offset = 0, .mask = TWL6040_THINT, },
+ { .reg_offset = 0, .mask = TWL6040_PLUGINT | TWL6040_UNPLUGINT, },
+ { .reg_offset = 0, .mask = TWL6040_HOOKINT, },
+ { .reg_offset = 0, .mask = TWL6040_HFINT, },
+ { .reg_offset = 0, .mask = TWL6040_VIBINT, },
+ { .reg_offset = 0, .mask = TWL6040_READYINT, },
+};
+
+static struct regmap_irq_chip twl6040_irq_chip = {
+ .name = "twl6040",
+ .irqs = twl6040_irqs,
+ .num_irqs = ARRAY_SIZE(twl6040_irqs),
+
+ .num_regs = 1,
+ .status_base = TWL6040_REG_INTID,
+ .mask_base = TWL6040_REG_INTMR,
+};
+
+static int twl6040_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- struct twl6040_platform_data *pdata = client->dev.platform_data;
struct device_node *node = client->dev.of_node;
struct twl6040 *twl6040;
struct mfd_cell *cell = NULL;
int irq, ret, children = 0;
- if (!pdata && !node) {
- dev_err(&client->dev, "Platform data is missing\n");
+ if (!node) {
+ dev_err(&client->dev, "of node is missing\n");
return -EINVAL;
}
@@ -525,32 +636,34 @@ static int __devinit twl6040_probe(struct i2c_client *client,
twl6040 = devm_kzalloc(&client->dev, sizeof(struct twl6040),
GFP_KERNEL);
- if (!twl6040) {
- ret = -ENOMEM;
- goto err;
- }
+ if (!twl6040)
+ return -ENOMEM;
twl6040->regmap = devm_regmap_init_i2c(client, &twl6040_regmap_config);
- if (IS_ERR(twl6040->regmap)) {
- ret = PTR_ERR(twl6040->regmap);
- goto err;
- }
+ if (IS_ERR(twl6040->regmap))
+ return PTR_ERR(twl6040->regmap);
i2c_set_clientdata(client, twl6040);
+ twl6040->clk32k = devm_clk_get(&client->dev, "clk32k");
+ if (IS_ERR(twl6040->clk32k)) {
+ dev_info(&client->dev, "clk32k is not handled\n");
+ twl6040->clk32k = NULL;
+ }
+
twl6040->supplies[0].supply = "vio";
twl6040->supplies[1].supply = "v2v1";
- ret = regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES,
- twl6040->supplies);
+ ret = devm_regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES,
+ twl6040->supplies);
if (ret != 0) {
dev_err(&client->dev, "Failed to get supplies: %d\n", ret);
- goto regulator_get_err;
+ return ret;
}
ret = regulator_bulk_enable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
if (ret != 0) {
dev_err(&client->dev, "Failed to enable supplies: %d\n", ret);
- goto power_err;
+ return ret;
}
twl6040->dev = &client->dev;
@@ -559,63 +672,75 @@ static int __devinit twl6040_probe(struct i2c_client *client,
mutex_init(&twl6040->mutex);
init_completion(&twl6040->ready);
+ regmap_register_patch(twl6040->regmap, twl6040_patch,
+ ARRAY_SIZE(twl6040_patch));
+
twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
+ if (twl6040->rev < 0) {
+ dev_err(&client->dev, "Failed to read revision register: %d\n",
+ twl6040->rev);
+ goto gpio_err;
+ }
/* ERRATA: Automatic power-up is not possible in ES1.0 */
- if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) {
- if (pdata)
- twl6040->audpwron = pdata->audpwron_gpio;
- else
- twl6040->audpwron = of_get_named_gpio(node,
- "ti,audpwron-gpio", 0);
- } else
+ if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0)
+ twl6040->audpwron = of_get_named_gpio(node,
+ "ti,audpwron-gpio", 0);
+ else
twl6040->audpwron = -EINVAL;
if (gpio_is_valid(twl6040->audpwron)) {
- ret = gpio_request_one(twl6040->audpwron, GPIOF_OUT_INIT_LOW,
- "audpwron");
+ ret = devm_gpio_request_one(&client->dev, twl6040->audpwron,
+ GPIOF_OUT_INIT_LOW, "audpwron");
if (ret)
goto gpio_err;
+
+ /* Clear any pending interrupt */
+ twl6040_reg_read(twl6040, TWL6040_REG_INTID);
}
- /* codec interrupt */
- ret = twl6040_irq_init(twl6040);
- if (ret)
- goto irq_init_err;
+ ret = regmap_add_irq_chip(twl6040->regmap, twl6040->irq, IRQF_ONESHOT,
+ 0, &twl6040_irq_chip,&twl6040->irq_data);
+ if (ret < 0)
+ goto gpio_err;
+
+ twl6040->irq_ready = regmap_irq_get_virq(twl6040->irq_data,
+ TWL6040_IRQ_READY);
+ twl6040->irq_th = regmap_irq_get_virq(twl6040->irq_data,
+ TWL6040_IRQ_TH);
- ret = request_threaded_irq(twl6040->irq_base + TWL6040_IRQ_READY,
- NULL, twl6040_naudint_handler, IRQF_ONESHOT,
- "twl6040_irq_ready", twl6040);
+ ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_ready, NULL,
+ twl6040_readyint_handler, IRQF_ONESHOT,
+ "twl6040_irq_ready", twl6040);
if (ret) {
- dev_err(twl6040->dev, "READY IRQ request failed: %d\n",
- ret);
- goto irq_err;
+ dev_err(twl6040->dev, "READY IRQ request failed: %d\n", ret);
+ goto readyirq_err;
}
- /* dual-access registers controlled by I2C only */
- twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL);
+ ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_th, NULL,
+ twl6040_thint_handler, IRQF_ONESHOT,
+ "twl6040_irq_th", twl6040);
+ if (ret) {
+ dev_err(twl6040->dev, "Thermal IRQ request failed: %d\n", ret);
+ goto readyirq_err;
+ }
/*
* The main functionality of twl6040 to provide audio on OMAP4+ systems.
* We can add the ASoC codec child whenever this driver has been loaded.
- * The ASoC codec can work without pdata, pass the platform_data only if
- * it has been provided.
*/
- irq = twl6040->irq_base + TWL6040_IRQ_PLUG;
+ irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_PLUG);
cell = &twl6040->cells[children];
cell->name = "twl6040-codec";
twl6040_codec_rsrc[0].start = irq;
twl6040_codec_rsrc[0].end = irq;
cell->resources = twl6040_codec_rsrc;
cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc);
- if (pdata && pdata->codec) {
- cell->platform_data = pdata->codec;
- cell->pdata_size = sizeof(*pdata->codec);
- }
children++;
- if (twl6040_has_vibra(pdata, node)) {
- irq = twl6040->irq_base + TWL6040_IRQ_VIB;
+ /* Vibra input driver support */
+ if (twl6040_has_vibra(node)) {
+ irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_VIB);
cell = &twl6040->cells[children];
cell->name = "twl6040-vibra";
@@ -623,71 +748,44 @@ static int __devinit twl6040_probe(struct i2c_client *client,
twl6040_vibra_rsrc[0].end = irq;
cell->resources = twl6040_vibra_rsrc;
cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc);
-
- if (pdata && pdata->vibra) {
- cell->platform_data = pdata->vibra;
- cell->pdata_size = sizeof(*pdata->vibra);
- }
children++;
}
- /*
- * Enable the GPO driver in the following cases:
- * DT booted kernel or legacy boot with valid gpo platform_data
- */
- if (!pdata || (pdata && pdata->gpo)) {
- cell = &twl6040->cells[children];
- cell->name = "twl6040-gpo";
+ /* GPO support */
+ cell = &twl6040->cells[children];
+ cell->name = "twl6040-gpo";
+ children++;
- if (pdata) {
- cell->platform_data = pdata->gpo;
- cell->pdata_size = sizeof(*pdata->gpo);
- }
- children++;
- }
+ /* The chip is powered down so mark regmap to cache only and dirty */
+ regcache_cache_only(twl6040->regmap, true);
+ regcache_mark_dirty(twl6040->regmap);
ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children,
NULL, 0, NULL);
if (ret)
- goto mfd_err;
+ goto readyirq_err;
return 0;
-mfd_err:
- free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040);
-irq_err:
- twl6040_irq_exit(twl6040);
-irq_init_err:
- if (gpio_is_valid(twl6040->audpwron))
- gpio_free(twl6040->audpwron);
+readyirq_err:
+ regmap_del_irq_chip(twl6040->irq, twl6040->irq_data);
gpio_err:
regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
-power_err:
- regulator_bulk_free(TWL6040_NUM_SUPPLIES, twl6040->supplies);
-regulator_get_err:
- i2c_set_clientdata(client, NULL);
-err:
return ret;
}
-static int __devexit twl6040_remove(struct i2c_client *client)
+static int twl6040_remove(struct i2c_client *client)
{
struct twl6040 *twl6040 = i2c_get_clientdata(client);
if (twl6040->power_count)
twl6040_power(twl6040, 0);
- if (gpio_is_valid(twl6040->audpwron))
- gpio_free(twl6040->audpwron);
-
- free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040);
- twl6040_irq_exit(twl6040);
+ regmap_del_irq_chip(twl6040->irq, twl6040->irq_data);
mfd_remove_devices(&client->dev);
- i2c_set_clientdata(client, NULL);
regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
- regulator_bulk_free(TWL6040_NUM_SUPPLIES, twl6040->supplies);
return 0;
}
@@ -705,7 +803,7 @@ static struct i2c_driver twl6040_driver = {
.owner = THIS_MODULE,
},
.probe = twl6040_probe,
- .remove = __devexit_p(twl6040_remove),
+ .remove = twl6040_remove,
.id_table = twl6040_i2c_id,
};
diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c
index daf69527ed8..ebb20edf9c1 100644
--- a/drivers/mfd/ucb1400_core.c
+++ b/drivers/mfd/ucb1400_core.c
@@ -52,7 +52,7 @@ static int ucb1400_core_probe(struct device *dev)
struct ucb1400_ts ucb_ts;
struct ucb1400_gpio ucb_gpio;
struct snd_ac97 *ac97;
- struct ucb1400_pdata *pdata = dev->platform_data;
+ struct ucb1400_pdata *pdata = dev_get_platdata(dev);
memset(&ucb_ts, 0, sizeof(ucb_ts));
memset(&ucb_gpio, 0, sizeof(ucb_gpio));
@@ -75,6 +75,11 @@ static int ucb1400_core_probe(struct device *dev)
/* GPIO */
ucb_gpio.ac97 = ac97;
+ if (pdata) {
+ ucb_gpio.gpio_setup = pdata->gpio_setup;
+ ucb_gpio.gpio_teardown = pdata->gpio_teardown;
+ ucb_gpio.gpio_offset = pdata->gpio_offset;
+ }
ucb->ucb1400_gpio = platform_device_alloc("ucb1400_gpio", -1);
if (!ucb->ucb1400_gpio) {
err = -ENOMEM;
diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c
index 70f02daeb22..153d595afaa 100644
--- a/drivers/mfd/ucb1x00-core.c
+++ b/drivers/mfd/ucb1x00-core.c
@@ -393,22 +393,24 @@ static struct irq_chip ucb1x00_irqchip = {
static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv)
{
struct ucb1x00_dev *dev;
- int ret = -ENOMEM;
+ int ret;
dev = kmalloc(sizeof(struct ucb1x00_dev), GFP_KERNEL);
- if (dev) {
- dev->ucb = ucb;
- dev->drv = drv;
-
- ret = drv->add(dev);
-
- if (ret == 0) {
- list_add_tail(&dev->dev_node, &ucb->devs);
- list_add_tail(&dev->drv_node, &drv->devs);
- } else {
- kfree(dev);
- }
+ if (!dev)
+ return -ENOMEM;
+
+ dev->ucb = ucb;
+ dev->drv = drv;
+
+ ret = drv->add(dev);
+ if (ret) {
+ kfree(dev);
+ return ret;
}
+
+ list_add_tail(&dev->dev_node, &ucb->devs);
+ list_add_tail(&dev->drv_node, &drv->devs);
+
return ret;
}
@@ -551,6 +553,7 @@ static int ucb1x00_probe(struct mcp *mcp)
if (ucb->irq_base < 0) {
dev_err(&ucb->dev, "unable to allocate 16 irqs: %d\n",
ucb->irq_base);
+ ret = ucb->irq_base;
goto err_irq_alloc;
}
@@ -669,9 +672,10 @@ void ucb1x00_unregister_driver(struct ucb1x00_driver *drv)
mutex_unlock(&ucb1x00_mutex);
}
+#ifdef CONFIG_PM_SLEEP
static int ucb1x00_suspend(struct device *dev)
{
- struct ucb1x00_plat_data *pdata = dev->platform_data;
+ struct ucb1x00_plat_data *pdata = dev_get_platdata(dev);
struct ucb1x00 *ucb = dev_get_drvdata(dev);
struct ucb1x00_dev *udev;
@@ -703,7 +707,7 @@ static int ucb1x00_suspend(struct device *dev)
static int ucb1x00_resume(struct device *dev)
{
- struct ucb1x00_plat_data *pdata = dev->platform_data;
+ struct ucb1x00_plat_data *pdata = dev_get_platdata(dev);
struct ucb1x00 *ucb = dev_get_drvdata(dev);
struct ucb1x00_dev *udev;
@@ -736,10 +740,9 @@ static int ucb1x00_resume(struct device *dev)
mutex_unlock(&ucb1x00_mutex);
return 0;
}
+#endif
-static const struct dev_pm_ops ucb1x00_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(ucb1x00_suspend, ucb1x00_resume)
-};
+static SIMPLE_DEV_PM_OPS(ucb1x00_pm_ops, ucb1x00_suspend, ucb1x00_resume);
static struct mcp_driver ucb1x00_driver = {
.drv = {
diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c
new file mode 100644
index 00000000000..9e21e4fc959
--- /dev/null
+++ b/drivers/mfd/vexpress-sysreg.c
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ *
+ * 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.
+ *
+ * Copyright (C) 2012 ARM Limited
+ */
+
+#include <linux/basic_mmio_gpio.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mfd/core.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_data/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/vexpress.h>
+
+#define SYS_ID 0x000
+#define SYS_SW 0x004
+#define SYS_LED 0x008
+#define SYS_100HZ 0x024
+#define SYS_FLAGSSET 0x030
+#define SYS_FLAGSCLR 0x034
+#define SYS_NVFLAGS 0x038
+#define SYS_NVFLAGSSET 0x038
+#define SYS_NVFLAGSCLR 0x03c
+#define SYS_MCI 0x048
+#define SYS_FLASH 0x04c
+#define SYS_CFGSW 0x058
+#define SYS_24MHZ 0x05c
+#define SYS_MISC 0x060
+#define SYS_DMA 0x064
+#define SYS_PROCID0 0x084
+#define SYS_PROCID1 0x088
+#define SYS_CFGDATA 0x0a0
+#define SYS_CFGCTRL 0x0a4
+#define SYS_CFGSTAT 0x0a8
+
+#define SYS_HBI_MASK 0xfff
+#define SYS_PROCIDx_HBI_SHIFT 0
+
+#define SYS_MCI_CARDIN (1 << 0)
+#define SYS_MCI_WPROT (1 << 1)
+
+#define SYS_MISC_MASTERSITE (1 << 14)
+
+
+static void __iomem *__vexpress_sysreg_base;
+
+static void __iomem *vexpress_sysreg_base(void)
+{
+ if (!__vexpress_sysreg_base) {
+ struct device_node *node = of_find_compatible_node(NULL, NULL,
+ "arm,vexpress-sysreg");
+
+ __vexpress_sysreg_base = of_iomap(node, 0);
+ }
+
+ WARN_ON(!__vexpress_sysreg_base);
+
+ return __vexpress_sysreg_base;
+}
+
+
+static int vexpress_sysreg_get_master(void)
+{
+ if (readl(vexpress_sysreg_base() + SYS_MISC) & SYS_MISC_MASTERSITE)
+ return VEXPRESS_SITE_DB2;
+
+ return VEXPRESS_SITE_DB1;
+}
+
+void vexpress_flags_set(u32 data)
+{
+ writel(~0, vexpress_sysreg_base() + SYS_FLAGSCLR);
+ writel(data, vexpress_sysreg_base() + SYS_FLAGSSET);
+}
+
+unsigned int vexpress_get_mci_cardin(struct device *dev)
+{
+ return readl(vexpress_sysreg_base() + SYS_MCI) & SYS_MCI_CARDIN;
+}
+
+u32 vexpress_get_procid(int site)
+{
+ if (site == VEXPRESS_SITE_MASTER)
+ site = vexpress_sysreg_get_master();
+
+ return readl(vexpress_sysreg_base() + (site == VEXPRESS_SITE_DB1 ?
+ SYS_PROCID0 : SYS_PROCID1));
+}
+
+void __iomem *vexpress_get_24mhz_clock_base(void)
+{
+ return vexpress_sysreg_base() + SYS_24MHZ;
+}
+
+
+void __init vexpress_sysreg_early_init(void __iomem *base)
+{
+ __vexpress_sysreg_base = base;
+
+ vexpress_config_set_master(vexpress_sysreg_get_master());
+}
+
+
+/* The sysreg block is just a random collection of various functions... */
+
+static struct syscon_platform_data vexpress_sysreg_sys_id_pdata = {
+ .label = "sys_id",
+};
+
+static struct bgpio_pdata vexpress_sysreg_sys_led_pdata = {
+ .label = "sys_led",
+ .base = -1,
+ .ngpio = 8,
+};
+
+static struct bgpio_pdata vexpress_sysreg_sys_mci_pdata = {
+ .label = "sys_mci",
+ .base = -1,
+ .ngpio = 2,
+};
+
+static struct bgpio_pdata vexpress_sysreg_sys_flash_pdata = {
+ .label = "sys_flash",
+ .base = -1,
+ .ngpio = 1,
+};
+
+static struct syscon_platform_data vexpress_sysreg_sys_misc_pdata = {
+ .label = "sys_misc",
+};
+
+static struct syscon_platform_data vexpress_sysreg_sys_procid_pdata = {
+ .label = "sys_procid",
+};
+
+static struct mfd_cell vexpress_sysreg_cells[] = {
+ {
+ .name = "syscon",
+ .num_resources = 1,
+ .resources = (struct resource []) {
+ DEFINE_RES_MEM(SYS_ID, 0x4),
+ },
+ .platform_data = &vexpress_sysreg_sys_id_pdata,
+ .pdata_size = sizeof(vexpress_sysreg_sys_id_pdata),
+ }, {
+ .name = "basic-mmio-gpio",
+ .of_compatible = "arm,vexpress-sysreg,sys_led",
+ .num_resources = 1,
+ .resources = (struct resource []) {
+ DEFINE_RES_MEM_NAMED(SYS_LED, 0x4, "dat"),
+ },
+ .platform_data = &vexpress_sysreg_sys_led_pdata,
+ .pdata_size = sizeof(vexpress_sysreg_sys_led_pdata),
+ }, {
+ .name = "basic-mmio-gpio",
+ .of_compatible = "arm,vexpress-sysreg,sys_mci",
+ .num_resources = 1,
+ .resources = (struct resource []) {
+ DEFINE_RES_MEM_NAMED(SYS_MCI, 0x4, "dat"),
+ },
+ .platform_data = &vexpress_sysreg_sys_mci_pdata,
+ .pdata_size = sizeof(vexpress_sysreg_sys_mci_pdata),
+ }, {
+ .name = "basic-mmio-gpio",
+ .of_compatible = "arm,vexpress-sysreg,sys_flash",
+ .num_resources = 1,
+ .resources = (struct resource []) {
+ DEFINE_RES_MEM_NAMED(SYS_FLASH, 0x4, "dat"),
+ },
+ .platform_data = &vexpress_sysreg_sys_flash_pdata,
+ .pdata_size = sizeof(vexpress_sysreg_sys_flash_pdata),
+ }, {
+ .name = "syscon",
+ .num_resources = 1,
+ .resources = (struct resource []) {
+ DEFINE_RES_MEM(SYS_MISC, 0x4),
+ },
+ .platform_data = &vexpress_sysreg_sys_misc_pdata,
+ .pdata_size = sizeof(vexpress_sysreg_sys_misc_pdata),
+ }, {
+ .name = "syscon",
+ .num_resources = 1,
+ .resources = (struct resource []) {
+ DEFINE_RES_MEM(SYS_PROCID0, 0x8),
+ },
+ .platform_data = &vexpress_sysreg_sys_procid_pdata,
+ .pdata_size = sizeof(vexpress_sysreg_sys_procid_pdata),
+ }, {
+ .name = "vexpress-syscfg",
+ .num_resources = 1,
+ .resources = (struct resource []) {
+ DEFINE_RES_MEM(SYS_CFGDATA, 0xc),
+ },
+ }
+};
+
+static int vexpress_sysreg_probe(struct platform_device *pdev)
+{
+ struct resource *mem;
+ void __iomem *base;
+ struct bgpio_chip *mmc_gpio_chip;
+ u32 dt_hbi;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem)
+ return -EINVAL;
+
+ base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!base)
+ return -ENOMEM;
+
+ vexpress_config_set_master(vexpress_sysreg_get_master());
+
+ /* Confirm board type against DT property, if available */
+ if (of_property_read_u32(of_allnodes, "arm,hbi", &dt_hbi) == 0) {
+ u32 id = vexpress_get_procid(VEXPRESS_SITE_MASTER);
+ u32 hbi = (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK;
+
+ if (WARN_ON(dt_hbi != hbi))
+ dev_warn(&pdev->dev, "DT HBI (%x) is not matching hardware (%x)!\n",
+ dt_hbi, hbi);
+ }
+
+ /*
+ * Duplicated SYS_MCI pseudo-GPIO controller for compatibility with
+ * older trees using sysreg node for MMC control lines.
+ */
+ mmc_gpio_chip = devm_kzalloc(&pdev->dev, sizeof(*mmc_gpio_chip),
+ GFP_KERNEL);
+ if (!mmc_gpio_chip)
+ return -ENOMEM;
+ bgpio_init(mmc_gpio_chip, &pdev->dev, 0x4, base + SYS_MCI,
+ NULL, NULL, NULL, NULL, 0);
+ mmc_gpio_chip->gc.ngpio = 2;
+ gpiochip_add(&mmc_gpio_chip->gc);
+
+ return mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
+ vexpress_sysreg_cells,
+ ARRAY_SIZE(vexpress_sysreg_cells), mem, 0, NULL);
+}
+
+static const struct of_device_id vexpress_sysreg_match[] = {
+ { .compatible = "arm,vexpress-sysreg", },
+ {},
+};
+
+static struct platform_driver vexpress_sysreg_driver = {
+ .driver = {
+ .name = "vexpress-sysreg",
+ .of_match_table = vexpress_sysreg_match,
+ },
+ .probe = vexpress_sysreg_probe,
+};
+
+static int __init vexpress_sysreg_init(void)
+{
+ struct device_node *node;
+
+ /* Need the sysreg early, before any other device... */
+ for_each_matching_node(node, vexpress_sysreg_match)
+ of_platform_device_create(node, NULL, NULL);
+
+ return platform_driver_register(&vexpress_sysreg_driver);
+}
+core_initcall(vexpress_sysreg_init);
diff --git a/drivers/mfd/viperboard.c b/drivers/mfd/viperboard.c
new file mode 100644
index 00000000000..e00f5340ed8
--- /dev/null
+++ b/drivers/mfd/viperboard.c
@@ -0,0 +1,137 @@
+/*
+ * Nano River Technologies viperboard driver
+ *
+ * This is the core driver for the viperboard. There are cell drivers
+ * available for I2C, ADC and both GPIOs. SPI is not yet supported.
+ * The drivers do not support all features the board exposes. See user
+ * manual of the viperboard.
+ *
+ * (C) 2012 by Lemonage GmbH
+ * Author: Lars Poeschel <poeschel@lemonage.de>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/viperboard.h>
+
+#include <linux/usb.h>
+
+
+static const struct usb_device_id vprbrd_table[] = {
+ { USB_DEVICE(0x2058, 0x1005) }, /* Nano River Technologies */
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, vprbrd_table);
+
+static const struct mfd_cell vprbrd_devs[] = {
+ {
+ .name = "viperboard-gpio",
+ },
+ {
+ .name = "viperboard-i2c",
+ },
+ {
+ .name = "viperboard-adc",
+ },
+};
+
+static int vprbrd_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct vprbrd *vb;
+
+ u16 version = 0;
+ int pipe, ret;
+
+ /* allocate memory for our device state and initialize it */
+ vb = kzalloc(sizeof(*vb), GFP_KERNEL);
+ if (vb == NULL) {
+ dev_err(&interface->dev, "Out of memory\n");
+ return -ENOMEM;
+ }
+
+ mutex_init(&vb->lock);
+
+ vb->usb_dev = usb_get_dev(interface_to_usbdev(interface));
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, vb);
+ dev_set_drvdata(&vb->pdev.dev, vb);
+
+ /* get version information, major first, minor then */
+ pipe = usb_rcvctrlpipe(vb->usb_dev, 0);
+ ret = usb_control_msg(vb->usb_dev, pipe, VPRBRD_USB_REQUEST_MAJOR,
+ VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, vb->buf, 1,
+ VPRBRD_USB_TIMEOUT_MS);
+ if (ret == 1)
+ version = vb->buf[0];
+
+ ret = usb_control_msg(vb->usb_dev, pipe, VPRBRD_USB_REQUEST_MINOR,
+ VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, vb->buf, 1,
+ VPRBRD_USB_TIMEOUT_MS);
+ if (ret == 1) {
+ version <<= 8;
+ version = version | vb->buf[0];
+ }
+
+ dev_info(&interface->dev,
+ "version %x.%02x found at bus %03d address %03d\n",
+ version >> 8, version & 0xff,
+ vb->usb_dev->bus->busnum, vb->usb_dev->devnum);
+
+ ret = mfd_add_devices(&interface->dev, -1, vprbrd_devs,
+ ARRAY_SIZE(vprbrd_devs), NULL, 0, NULL);
+ if (ret != 0) {
+ dev_err(&interface->dev, "Failed to add mfd devices to core.");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ if (vb) {
+ usb_put_dev(vb->usb_dev);
+ kfree(vb);
+ }
+
+ return ret;
+}
+
+static void vprbrd_disconnect(struct usb_interface *interface)
+{
+ struct vprbrd *vb = usb_get_intfdata(interface);
+
+ mfd_remove_devices(&interface->dev);
+ usb_set_intfdata(interface, NULL);
+ usb_put_dev(vb->usb_dev);
+ kfree(vb);
+
+ dev_dbg(&interface->dev, "disconnected\n");
+}
+
+static struct usb_driver vprbrd_driver = {
+ .name = "viperboard",
+ .probe = vprbrd_probe,
+ .disconnect = vprbrd_disconnect,
+ .id_table = vprbrd_table,
+};
+
+module_usb_driver(vprbrd_driver);
+
+MODULE_DESCRIPTION("Nano River Technologies viperboard mfd core driver");
+MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/vx855.c b/drivers/mfd/vx855.c
index b9a636d44c7..84f01da4875 100644
--- a/drivers/mfd/vx855.c
+++ b/drivers/mfd/vx855.c
@@ -60,7 +60,7 @@ static struct resource vx855_gpio_resources[] = {
},
};
-static struct mfd_cell vx855_cells[] = {
+static const struct mfd_cell vx855_cells[] = {
{
.name = "vx855_gpio",
.num_resources = ARRAY_SIZE(vx855_gpio_resources),
@@ -72,7 +72,7 @@ static struct mfd_cell vx855_cells[] = {
},
};
-static __devinit int vx855_probe(struct pci_dev *pdev,
+static int vx855_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int ret;
@@ -112,13 +112,13 @@ out:
return ret;
}
-static void __devexit vx855_remove(struct pci_dev *pdev)
+static void vx855_remove(struct pci_dev *pdev)
{
mfd_remove_devices(&pdev->dev);
pci_disable_device(pdev);
}
-static DEFINE_PCI_DEVICE_TABLE(vx855_pci_tbl) = {
+static const struct pci_device_id vx855_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) },
{ 0, }
};
@@ -128,7 +128,7 @@ static struct pci_driver vx855_pci_driver = {
.name = "vx855",
.id_table = vx855_pci_tbl,
.probe = vx855_probe,
- .remove = __devexit_p(vx855_remove),
+ .remove = vx855_remove,
};
module_pci_driver(vx855_pci_driver);
diff --git a/drivers/mfd/wl1273-core.c b/drivers/mfd/wl1273-core.c
index 86e0e4309fc..f7c52d90104 100644
--- a/drivers/mfd/wl1273-core.c
+++ b/drivers/mfd/wl1273-core.c
@@ -172,20 +172,17 @@ static int wl1273_fm_set_volume(struct wl1273_core *core, unsigned int volume)
static int wl1273_core_remove(struct i2c_client *client)
{
- struct wl1273_core *core = i2c_get_clientdata(client);
-
dev_dbg(&client->dev, "%s\n", __func__);
mfd_remove_devices(&client->dev);
- kfree(core);
return 0;
}
-static int __devinit wl1273_core_probe(struct i2c_client *client,
+static int wl1273_core_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct wl1273_fm_platform_data *pdata = client->dev.platform_data;
+ struct wl1273_fm_platform_data *pdata = dev_get_platdata(&client->dev);
struct wl1273_core *core;
struct mfd_cell *cell;
int children = 0;
@@ -203,7 +200,7 @@ static int __devinit wl1273_core_probe(struct i2c_client *client,
return -EINVAL;
}
- core = kzalloc(sizeof(*core), GFP_KERNEL);
+ core = devm_kzalloc(&client->dev, sizeof(*core), GFP_KERNEL);
if (!core)
return -ENOMEM;
@@ -249,7 +246,6 @@ static int __devinit wl1273_core_probe(struct i2c_client *client,
err:
pdata->free_resources();
- kfree(core);
dev_dbg(&client->dev, "%s\n", __func__);
@@ -262,7 +258,7 @@ static struct i2c_driver wl1273_core_driver = {
},
.probe = wl1273_core_probe,
.id_table = wl1273_driver_id_table,
- .remove = __devexit_p(wl1273_core_remove),
+ .remove = wl1273_core_remove,
};
static int __init wl1273_core_init(void)
diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c
index 01b9255ed63..c8a993bd17a 100644
--- a/drivers/mfd/wm5102-tables.c
+++ b/drivers/mfd/wm5102-tables.c
@@ -10,6 +10,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/device.h>
#include <linux/module.h>
#include <linux/mfd/arizona/core.h>
@@ -43,6 +44,7 @@ static const struct reg_default wm5102_reva_patch[] = {
{ 0x479, 0x0A30 },
{ 0x47B, 0x0810 },
{ 0x47D, 0x0510 },
+ { 0x4D1, 0x017F },
{ 0x500, 0x000D },
{ 0x507, 0x1820 },
{ 0x508, 0x1820 },
@@ -52,541 +54,56 @@ static const struct reg_default wm5102_reva_patch[] = {
{ 0x580, 0x000D },
{ 0x587, 0x1820 },
{ 0x588, 0x1820 },
- { 0x101, 0x8140 },
- { 0x3000, 0x2225 },
- { 0x3001, 0x3a03 },
- { 0x3002, 0x0225 },
- { 0x3003, 0x0801 },
- { 0x3004, 0x6249 },
- { 0x3005, 0x0c04 },
- { 0x3006, 0x0225 },
- { 0x3007, 0x5901 },
- { 0x3008, 0xe249 },
- { 0x3009, 0x030d },
- { 0x300a, 0x0249 },
- { 0x300b, 0x2c01 },
- { 0x300c, 0xe249 },
- { 0x300d, 0x4342 },
- { 0x300e, 0xe249 },
- { 0x300f, 0x73c0 },
- { 0x3010, 0x4249 },
- { 0x3011, 0x0c00 },
- { 0x3012, 0x0225 },
- { 0x3013, 0x1f01 },
- { 0x3014, 0x0225 },
- { 0x3015, 0x1e01 },
- { 0x3016, 0x0225 },
- { 0x3017, 0xfa00 },
- { 0x3018, 0x0000 },
- { 0x3019, 0xf000 },
- { 0x301a, 0x0000 },
- { 0x301b, 0xf000 },
- { 0x301c, 0x0000 },
- { 0x301d, 0xf000 },
- { 0x301e, 0x0000 },
- { 0x301f, 0xf000 },
- { 0x3020, 0x0000 },
- { 0x3021, 0xf000 },
- { 0x3022, 0x0000 },
- { 0x3023, 0xf000 },
- { 0x3024, 0x0000 },
- { 0x3025, 0xf000 },
- { 0x3026, 0x0000 },
- { 0x3027, 0xf000 },
- { 0x3028, 0x0000 },
- { 0x3029, 0xf000 },
- { 0x302a, 0x0000 },
- { 0x302b, 0xf000 },
- { 0x302c, 0x0000 },
- { 0x302d, 0xf000 },
- { 0x302e, 0x0000 },
- { 0x302f, 0xf000 },
- { 0x3030, 0x0225 },
- { 0x3031, 0x1a01 },
- { 0x3032, 0x0225 },
- { 0x3033, 0x1e00 },
- { 0x3034, 0x0225 },
- { 0x3035, 0x1f00 },
- { 0x3036, 0x6225 },
- { 0x3037, 0xf800 },
- { 0x3038, 0x0000 },
- { 0x3039, 0xf000 },
- { 0x303a, 0x0000 },
- { 0x303b, 0xf000 },
- { 0x303c, 0x0000 },
- { 0x303d, 0xf000 },
- { 0x303e, 0x0000 },
- { 0x303f, 0xf000 },
- { 0x3040, 0x2226 },
- { 0x3041, 0x3a03 },
- { 0x3042, 0x0226 },
- { 0x3043, 0x0801 },
- { 0x3044, 0x6249 },
- { 0x3045, 0x0c06 },
- { 0x3046, 0x0226 },
- { 0x3047, 0x5901 },
- { 0x3048, 0xe249 },
- { 0x3049, 0x030d },
- { 0x304a, 0x0249 },
- { 0x304b, 0x2c01 },
- { 0x304c, 0xe249 },
- { 0x304d, 0x4342 },
- { 0x304e, 0xe249 },
- { 0x304f, 0x73c0 },
- { 0x3050, 0x4249 },
- { 0x3051, 0x0c00 },
- { 0x3052, 0x0226 },
- { 0x3053, 0x1f01 },
- { 0x3054, 0x0226 },
- { 0x3055, 0x1e01 },
- { 0x3056, 0x0226 },
- { 0x3057, 0xfa00 },
- { 0x3058, 0x0000 },
- { 0x3059, 0xf000 },
- { 0x305a, 0x0000 },
- { 0x305b, 0xf000 },
- { 0x305c, 0x0000 },
- { 0x305d, 0xf000 },
- { 0x305e, 0x0000 },
- { 0x305f, 0xf000 },
- { 0x3060, 0x0000 },
- { 0x3061, 0xf000 },
- { 0x3062, 0x0000 },
- { 0x3063, 0xf000 },
- { 0x3064, 0x0000 },
- { 0x3065, 0xf000 },
- { 0x3066, 0x0000 },
- { 0x3067, 0xf000 },
- { 0x3068, 0x0000 },
- { 0x3069, 0xf000 },
- { 0x306a, 0x0000 },
- { 0x306b, 0xf000 },
- { 0x306c, 0x0000 },
- { 0x306d, 0xf000 },
- { 0x306e, 0x0000 },
- { 0x306f, 0xf000 },
- { 0x3070, 0x0226 },
- { 0x3071, 0x1a01 },
- { 0x3072, 0x0226 },
- { 0x3073, 0x1e00 },
- { 0x3074, 0x0226 },
- { 0x3075, 0x1f00 },
- { 0x3076, 0x6226 },
- { 0x3077, 0xf800 },
- { 0x3078, 0x0000 },
- { 0x3079, 0xf000 },
- { 0x307a, 0x0000 },
- { 0x307b, 0xf000 },
- { 0x307c, 0x0000 },
- { 0x307d, 0xf000 },
- { 0x307e, 0x0000 },
- { 0x307f, 0xf000 },
- { 0x3080, 0x2227 },
- { 0x3081, 0x3a03 },
- { 0x3082, 0x0227 },
- { 0x3083, 0x0801 },
- { 0x3084, 0x6255 },
- { 0x3085, 0x0c04 },
- { 0x3086, 0x0227 },
- { 0x3087, 0x5901 },
- { 0x3088, 0xe255 },
- { 0x3089, 0x030d },
- { 0x308a, 0x0255 },
- { 0x308b, 0x2c01 },
- { 0x308c, 0xe255 },
- { 0x308d, 0x4342 },
- { 0x308e, 0xe255 },
- { 0x308f, 0x73c0 },
- { 0x3090, 0x4255 },
- { 0x3091, 0x0c00 },
- { 0x3092, 0x0227 },
- { 0x3093, 0x1f01 },
- { 0x3094, 0x0227 },
- { 0x3095, 0x1e01 },
- { 0x3096, 0x0227 },
- { 0x3097, 0xfa00 },
- { 0x3098, 0x0000 },
- { 0x3099, 0xf000 },
- { 0x309a, 0x0000 },
- { 0x309b, 0xf000 },
- { 0x309c, 0x0000 },
- { 0x309d, 0xf000 },
- { 0x309e, 0x0000 },
- { 0x309f, 0xf000 },
- { 0x30a0, 0x0000 },
- { 0x30a1, 0xf000 },
- { 0x30a2, 0x0000 },
- { 0x30a3, 0xf000 },
- { 0x30a4, 0x0000 },
- { 0x30a5, 0xf000 },
- { 0x30a6, 0x0000 },
- { 0x30a7, 0xf000 },
- { 0x30a8, 0x0000 },
- { 0x30a9, 0xf000 },
- { 0x30aa, 0x0000 },
- { 0x30ab, 0xf000 },
- { 0x30ac, 0x0000 },
- { 0x30ad, 0xf000 },
- { 0x30ae, 0x0000 },
- { 0x30af, 0xf000 },
- { 0x30b0, 0x0227 },
- { 0x30b1, 0x1a01 },
- { 0x30b2, 0x0227 },
- { 0x30b3, 0x1e00 },
- { 0x30b4, 0x0227 },
- { 0x30b5, 0x1f00 },
- { 0x30b6, 0x6227 },
- { 0x30b7, 0xf800 },
- { 0x30b8, 0x0000 },
- { 0x30b9, 0xf000 },
- { 0x30ba, 0x0000 },
- { 0x30bb, 0xf000 },
- { 0x30bc, 0x0000 },
- { 0x30bd, 0xf000 },
- { 0x30be, 0x0000 },
- { 0x30bf, 0xf000 },
- { 0x30c0, 0x2228 },
- { 0x30c1, 0x3a03 },
- { 0x30c2, 0x0228 },
- { 0x30c3, 0x0801 },
- { 0x30c4, 0x6255 },
- { 0x30c5, 0x0c06 },
- { 0x30c6, 0x0228 },
- { 0x30c7, 0x5901 },
- { 0x30c8, 0xe255 },
- { 0x30c9, 0x030d },
- { 0x30ca, 0x0255 },
- { 0x30cb, 0x2c01 },
- { 0x30cc, 0xe255 },
- { 0x30cd, 0x4342 },
- { 0x30ce, 0xe255 },
- { 0x30cf, 0x73c0 },
- { 0x30d0, 0x4255 },
- { 0x30d1, 0x0c00 },
- { 0x30d2, 0x0228 },
- { 0x30d3, 0x1f01 },
- { 0x30d4, 0x0228 },
- { 0x30d5, 0x1e01 },
- { 0x30d6, 0x0228 },
- { 0x30d7, 0xfa00 },
- { 0x30d8, 0x0000 },
- { 0x30d9, 0xf000 },
- { 0x30da, 0x0000 },
- { 0x30db, 0xf000 },
- { 0x30dc, 0x0000 },
- { 0x30dd, 0xf000 },
- { 0x30de, 0x0000 },
- { 0x30df, 0xf000 },
- { 0x30e0, 0x0000 },
- { 0x30e1, 0xf000 },
- { 0x30e2, 0x0000 },
- { 0x30e3, 0xf000 },
- { 0x30e4, 0x0000 },
- { 0x30e5, 0xf000 },
- { 0x30e6, 0x0000 },
- { 0x30e7, 0xf000 },
- { 0x30e8, 0x0000 },
- { 0x30e9, 0xf000 },
- { 0x30ea, 0x0000 },
- { 0x30eb, 0xf000 },
- { 0x30ec, 0x0000 },
- { 0x30ed, 0xf000 },
- { 0x30ee, 0x0000 },
- { 0x30ef, 0xf000 },
- { 0x30f0, 0x0228 },
- { 0x30f1, 0x1a01 },
- { 0x30f2, 0x0228 },
- { 0x30f3, 0x1e00 },
- { 0x30f4, 0x0228 },
- { 0x30f5, 0x1f00 },
- { 0x30f6, 0x6228 },
- { 0x30f7, 0xf800 },
- { 0x30f8, 0x0000 },
- { 0x30f9, 0xf000 },
- { 0x30fa, 0x0000 },
- { 0x30fb, 0xf000 },
- { 0x30fc, 0x0000 },
- { 0x30fd, 0xf000 },
- { 0x30fe, 0x0000 },
- { 0x30ff, 0xf000 },
- { 0x3100, 0x222b },
- { 0x3101, 0x3a03 },
- { 0x3102, 0x222b },
- { 0x3103, 0x5803 },
- { 0x3104, 0xe26f },
- { 0x3105, 0x030d },
- { 0x3106, 0x626f },
- { 0x3107, 0x2c01 },
- { 0x3108, 0xe26f },
- { 0x3109, 0x4342 },
- { 0x310a, 0xe26f },
- { 0x310b, 0x73c0 },
- { 0x310c, 0x026f },
- { 0x310d, 0x0c00 },
- { 0x310e, 0x022b },
- { 0x310f, 0x1f01 },
- { 0x3110, 0x022b },
- { 0x3111, 0x1e01 },
- { 0x3112, 0x022b },
- { 0x3113, 0xfa00 },
- { 0x3114, 0x0000 },
- { 0x3115, 0xf000 },
- { 0x3116, 0x0000 },
- { 0x3117, 0xf000 },
- { 0x3118, 0x0000 },
- { 0x3119, 0xf000 },
- { 0x311a, 0x0000 },
- { 0x311b, 0xf000 },
- { 0x311c, 0x0000 },
- { 0x311d, 0xf000 },
- { 0x311e, 0x0000 },
- { 0x311f, 0xf000 },
- { 0x3120, 0x022b },
- { 0x3121, 0x0a01 },
- { 0x3122, 0x022b },
- { 0x3123, 0x1e00 },
- { 0x3124, 0x022b },
- { 0x3125, 0x1f00 },
- { 0x3126, 0x622b },
- { 0x3127, 0xf800 },
- { 0x3128, 0x0000 },
- { 0x3129, 0xf000 },
- { 0x312a, 0x0000 },
- { 0x312b, 0xf000 },
- { 0x312c, 0x0000 },
- { 0x312d, 0xf000 },
- { 0x312e, 0x0000 },
- { 0x312f, 0xf000 },
- { 0x3130, 0x0000 },
- { 0x3131, 0xf000 },
- { 0x3132, 0x0000 },
- { 0x3133, 0xf000 },
- { 0x3134, 0x0000 },
- { 0x3135, 0xf000 },
- { 0x3136, 0x0000 },
- { 0x3137, 0xf000 },
- { 0x3138, 0x0000 },
- { 0x3139, 0xf000 },
- { 0x313a, 0x0000 },
- { 0x313b, 0xf000 },
- { 0x313c, 0x0000 },
- { 0x313d, 0xf000 },
- { 0x313e, 0x0000 },
- { 0x313f, 0xf000 },
- { 0x3140, 0x0000 },
- { 0x3141, 0xf000 },
- { 0x3142, 0x0000 },
- { 0x3143, 0xf000 },
- { 0x3144, 0x0000 },
- { 0x3145, 0xf000 },
- { 0x3146, 0x0000 },
- { 0x3147, 0xf000 },
- { 0x3148, 0x0000 },
- { 0x3149, 0xf000 },
- { 0x314a, 0x0000 },
- { 0x314b, 0xf000 },
- { 0x314c, 0x0000 },
- { 0x314d, 0xf000 },
- { 0x314e, 0x0000 },
- { 0x314f, 0xf000 },
- { 0x3150, 0x0000 },
- { 0x3151, 0xf000 },
- { 0x3152, 0x0000 },
- { 0x3153, 0xf000 },
- { 0x3154, 0x0000 },
- { 0x3155, 0xf000 },
- { 0x3156, 0x0000 },
- { 0x3157, 0xf000 },
- { 0x3158, 0x0000 },
- { 0x3159, 0xf000 },
- { 0x315a, 0x0000 },
- { 0x315b, 0xf000 },
- { 0x315c, 0x0000 },
- { 0x315d, 0xf000 },
- { 0x315e, 0x0000 },
- { 0x315f, 0xf000 },
- { 0x3160, 0x0000 },
- { 0x3161, 0xf000 },
- { 0x3162, 0x0000 },
- { 0x3163, 0xf000 },
- { 0x3164, 0x0000 },
- { 0x3165, 0xf000 },
- { 0x3166, 0x0000 },
- { 0x3167, 0xf000 },
- { 0x3168, 0x0000 },
- { 0x3169, 0xf000 },
- { 0x316a, 0x0000 },
- { 0x316b, 0xf000 },
- { 0x316c, 0x0000 },
- { 0x316d, 0xf000 },
- { 0x316e, 0x0000 },
- { 0x316f, 0xf000 },
- { 0x3170, 0x0000 },
- { 0x3171, 0xf000 },
- { 0x3172, 0x0000 },
- { 0x3173, 0xf000 },
- { 0x3174, 0x0000 },
- { 0x3175, 0xf000 },
- { 0x3176, 0x0000 },
- { 0x3177, 0xf000 },
- { 0x3178, 0x0000 },
- { 0x3179, 0xf000 },
- { 0x317a, 0x0000 },
- { 0x317b, 0xf000 },
- { 0x317c, 0x0000 },
- { 0x317d, 0xf000 },
- { 0x317e, 0x0000 },
- { 0x317f, 0xf000 },
- { 0x3180, 0x2001 },
- { 0x3181, 0xf101 },
- { 0x3182, 0x0000 },
- { 0x3183, 0xf000 },
- { 0x3184, 0x0000 },
- { 0x3185, 0xf000 },
- { 0x3186, 0x0000 },
- { 0x3187, 0xf000 },
- { 0x3188, 0x0000 },
- { 0x3189, 0xf000 },
- { 0x318a, 0x0000 },
- { 0x318b, 0xf000 },
- { 0x318c, 0x0000 },
- { 0x318d, 0xf000 },
- { 0x318e, 0x0000 },
- { 0x318f, 0xf000 },
- { 0x3190, 0x0000 },
- { 0x3191, 0xf000 },
- { 0x3192, 0x0000 },
- { 0x3193, 0xf000 },
- { 0x3194, 0x0000 },
- { 0x3195, 0xf000 },
- { 0x3196, 0x0000 },
- { 0x3197, 0xf000 },
- { 0x3198, 0x0000 },
- { 0x3199, 0xf000 },
- { 0x319a, 0x0000 },
- { 0x319b, 0xf000 },
- { 0x319c, 0x0000 },
- { 0x319d, 0xf000 },
- { 0x319e, 0x0000 },
- { 0x319f, 0xf000 },
- { 0x31a0, 0x0000 },
- { 0x31a1, 0xf000 },
- { 0x31a2, 0x0000 },
- { 0x31a3, 0xf000 },
- { 0x31a4, 0x0000 },
- { 0x31a5, 0xf000 },
- { 0x31a6, 0x0000 },
- { 0x31a7, 0xf000 },
- { 0x31a8, 0x0000 },
- { 0x31a9, 0xf000 },
- { 0x31aa, 0x0000 },
- { 0x31ab, 0xf000 },
- { 0x31ac, 0x0000 },
- { 0x31ad, 0xf000 },
- { 0x31ae, 0x0000 },
- { 0x31af, 0xf000 },
- { 0x31b0, 0x0000 },
- { 0x31b1, 0xf000 },
- { 0x31b2, 0x0000 },
- { 0x31b3, 0xf000 },
- { 0x31b4, 0x0000 },
- { 0x31b5, 0xf000 },
- { 0x31b6, 0x0000 },
- { 0x31b7, 0xf000 },
- { 0x31b8, 0x0000 },
- { 0x31b9, 0xf000 },
- { 0x31ba, 0x0000 },
- { 0x31bb, 0xf000 },
- { 0x31bc, 0x0000 },
- { 0x31bd, 0xf000 },
- { 0x31be, 0x0000 },
- { 0x31bf, 0xf000 },
- { 0x31c0, 0x0000 },
- { 0x31c1, 0xf000 },
- { 0x31c2, 0x0000 },
- { 0x31c3, 0xf000 },
- { 0x31c4, 0x0000 },
- { 0x31c5, 0xf000 },
- { 0x31c6, 0x0000 },
- { 0x31c7, 0xf000 },
- { 0x31c8, 0x0000 },
- { 0x31c9, 0xf000 },
- { 0x31ca, 0x0000 },
- { 0x31cb, 0xf000 },
- { 0x31cc, 0x0000 },
- { 0x31cd, 0xf000 },
- { 0x31ce, 0x0000 },
- { 0x31cf, 0xf000 },
- { 0x31d0, 0x0000 },
- { 0x31d1, 0xf000 },
- { 0x31d2, 0x0000 },
- { 0x31d3, 0xf000 },
- { 0x31d4, 0x0000 },
- { 0x31d5, 0xf000 },
- { 0x31d6, 0x0000 },
- { 0x31d7, 0xf000 },
- { 0x31d8, 0x0000 },
- { 0x31d9, 0xf000 },
- { 0x31da, 0x0000 },
- { 0x31db, 0xf000 },
- { 0x31dc, 0x0000 },
- { 0x31dd, 0xf000 },
- { 0x31de, 0x0000 },
- { 0x31df, 0xf000 },
- { 0x31e0, 0x0000 },
- { 0x31e1, 0xf000 },
- { 0x31e2, 0x0000 },
- { 0x31e3, 0xf000 },
- { 0x31e4, 0x0000 },
- { 0x31e5, 0xf000 },
- { 0x31e6, 0x0000 },
- { 0x31e7, 0xf000 },
- { 0x31e8, 0x0000 },
- { 0x31e9, 0xf000 },
- { 0x31ea, 0x0000 },
- { 0x31eb, 0xf000 },
- { 0x31ec, 0x0000 },
- { 0x31ed, 0xf000 },
- { 0x31ee, 0x0000 },
- { 0x31ef, 0xf000 },
- { 0x31f0, 0x0000 },
- { 0x31f1, 0xf000 },
- { 0x31f2, 0x0000 },
- { 0x31f3, 0xf000 },
- { 0x31f4, 0x0000 },
- { 0x31f5, 0xf000 },
- { 0x31f6, 0x0000 },
- { 0x31f7, 0xf000 },
- { 0x31f8, 0x0000 },
- { 0x31f9, 0xf000 },
- { 0x31fa, 0x0000 },
- { 0x31fb, 0xf000 },
- { 0x31fc, 0x0000 },
- { 0x31fd, 0xf000 },
- { 0x31fe, 0x0000 },
- { 0x31ff, 0xf000 },
- { 0x024d, 0xff50 },
- { 0x0252, 0xff50 },
- { 0x0259, 0x0112 },
- { 0x025e, 0x0112 },
- { 0x101, 0x0304 },
+ { 0x80, 0x0000 },
+};
+
+static const struct reg_default wm5102_revb_patch[] = {
+ { 0x19, 0x0001 },
+ { 0x80, 0x0003 },
+ { 0x081, 0xE022 },
+ { 0x410, 0x6080 },
+ { 0x418, 0xa080 },
+ { 0x420, 0xa080 },
+ { 0x428, 0xe000 },
+ { 0x442, 0x3F0A },
+ { 0x443, 0xDC1F },
+ { 0x4B0, 0x0066 },
+ { 0x458, 0x000b },
+ { 0x212, 0x0000 },
+ { 0x171, 0x0000 },
+ { 0x35E, 0x000C },
+ { 0x2D4, 0x0000 },
+ { 0x4DC, 0x0900 },
{ 0x80, 0x0000 },
};
/* We use a function so we can use ARRAY_SIZE() */
int wm5102_patch(struct arizona *arizona)
{
+ const struct reg_default *wm5102_patch;
+ int patch_size;
+
switch (arizona->rev) {
case 0:
- return regmap_register_patch(arizona->regmap,
- wm5102_reva_patch,
- ARRAY_SIZE(wm5102_reva_patch));
+ wm5102_patch = wm5102_reva_patch;
+ patch_size = ARRAY_SIZE(wm5102_reva_patch);
default:
- return 0;
+ wm5102_patch = wm5102_revb_patch;
+ patch_size = ARRAY_SIZE(wm5102_revb_patch);
}
+
+ return regmap_multi_reg_write_bypassed(arizona->regmap,
+ wm5102_patch,
+ patch_size);
}
static const struct regmap_irq wm5102_aod_irqs[ARIZONA_NUM_IRQ] = {
+ [ARIZONA_IRQ_MICD_CLAMP_FALL] = {
+ .mask = ARIZONA_MICD_CLAMP_FALL_EINT1
+ },
+ [ARIZONA_IRQ_MICD_CLAMP_RISE] = {
+ .mask = ARIZONA_MICD_CLAMP_RISE_EINT1
+ },
[ARIZONA_IRQ_GP5_FALL] = { .mask = ARIZONA_GP5_FALL_EINT1 },
[ARIZONA_IRQ_GP5_RISE] = { .mask = ARIZONA_GP5_RISE_EINT1 },
[ARIZONA_IRQ_JD_FALL] = { .mask = ARIZONA_JD1_FALL_EINT1 },
@@ -599,6 +116,7 @@ const struct regmap_irq_chip wm5102_aod = {
.mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1,
.ack_base = ARIZONA_AOD_IRQ1,
.wake_base = ARIZONA_WAKE_CONTROL,
+ .wake_invert = 1,
.num_regs = 1,
.irqs = wm5102_aod_irqs,
.num_irqs = ARRAY_SIZE(wm5102_aod_irqs),
@@ -727,11 +245,9 @@ const struct regmap_irq_chip wm5102_irq = {
static const struct reg_default wm5102_reg_default[] = {
{ 0x00000008, 0x0019 }, /* R8 - Ctrl IF SPI CFG 1 */
{ 0x00000009, 0x0001 }, /* R9 - Ctrl IF I2C1 CFG 1 */
- { 0x0000000D, 0x0000 }, /* R13 - Ctrl IF Status 1 */
{ 0x00000016, 0x0000 }, /* R22 - Write Sequencer Ctrl 0 */
{ 0x00000017, 0x0000 }, /* R23 - Write Sequencer Ctrl 1 */
{ 0x00000018, 0x0000 }, /* R24 - Write Sequencer Ctrl 2 */
- { 0x0000001A, 0x0000 }, /* R26 - Write Sequencer PROM */
{ 0x00000020, 0x0000 }, /* R32 - Tone Generator 1 */
{ 0x00000021, 0x1000 }, /* R33 - Tone Generator 2 */
{ 0x00000022, 0x0000 }, /* R34 - Tone Generator 3 */
@@ -746,12 +262,14 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */
{ 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */
{ 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */
- { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 1 */
- { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 2 */
- { 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 3 */
- { 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 4 */
- { 0x0000006C, 0x01FF }, /* R108 - Always On Triggers Sequence Select 5 */
- { 0x0000006D, 0x01FF }, /* R109 - Always On Triggers Sequence Select 6 */
+ { 0x00000066, 0x01FF }, /* R102 - Always On Triggers Sequence Select 1 */
+ { 0x00000067, 0x01FF }, /* R103 - Always On Triggers Sequence Select 2 */
+ { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 3 */
+ { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 4 */
+ { 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 5 */
+ { 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 6 */
+ { 0x0000006E, 0x01FF }, /* R110 - Trigger Sequence Select 32 */
+ { 0x0000006F, 0x01FF }, /* R111 - Trigger Sequence Select 33 */
{ 0x00000070, 0x0000 }, /* R112 - Comfort Noise Generator */
{ 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */
{ 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */
@@ -761,13 +279,14 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000095, 0x0000 }, /* R149 - Haptics phase 2 duration */
{ 0x00000096, 0x0000 }, /* R150 - Haptics phase 3 intensity */
{ 0x00000097, 0x0000 }, /* R151 - Haptics phase 3 duration */
- { 0x00000100, 0x0001 }, /* R256 - Clock 32k 1 */
+ { 0x00000100, 0x0002 }, /* R256 - Clock 32k 1 */
{ 0x00000101, 0x0304 }, /* R257 - System Clock 1 */
{ 0x00000102, 0x0011 }, /* R258 - Sample rate 1 */
{ 0x00000103, 0x0011 }, /* R259 - Sample rate 2 */
{ 0x00000104, 0x0011 }, /* R260 - Sample rate 3 */
{ 0x00000112, 0x0305 }, /* R274 - Async clock 1 */
{ 0x00000113, 0x0011 }, /* R275 - Async sample rate 1 */
+ { 0x00000114, 0x0011 }, /* R276 - Async sample rate 2 */
{ 0x00000149, 0x0000 }, /* R329 - Output system clock */
{ 0x0000014A, 0x0000 }, /* R330 - Output async clock */
{ 0x00000152, 0x0000 }, /* R338 - Rate Estimator 1 */
@@ -775,19 +294,23 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000154, 0x0000 }, /* R340 - Rate Estimator 3 */
{ 0x00000155, 0x0000 }, /* R341 - Rate Estimator 4 */
{ 0x00000156, 0x0000 }, /* R342 - Rate Estimator 5 */
- { 0x00000171, 0x0000 }, /* R369 - FLL1 Control 1 */
+ { 0x00000161, 0x0000 }, /* R353 - Dynamic Frequency Scaling 1 */
+ { 0x00000171, 0x0000 }, /* R369 - FLL1 Control 1 */
{ 0x00000172, 0x0008 }, /* R370 - FLL1 Control 2 */
{ 0x00000173, 0x0018 }, /* R371 - FLL1 Control 3 */
{ 0x00000174, 0x007D }, /* R372 - FLL1 Control 4 */
{ 0x00000175, 0x0004 }, /* R373 - FLL1 Control 5 */
{ 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */
{ 0x00000177, 0x0181 }, /* R375 - FLL1 Loop Filter Test 1 */
+ { 0x00000178, 0x0000 }, /* R376 - FLL1 NCO Test 0 */
+ { 0x00000179, 0x0000 }, /* R377 - FLL1 Control 7 */
{ 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */
{ 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */
{ 0x00000183, 0x0000 }, /* R387 - FLL1 Synchroniser 3 */
{ 0x00000184, 0x0000 }, /* R388 - FLL1 Synchroniser 4 */
{ 0x00000185, 0x0000 }, /* R389 - FLL1 Synchroniser 5 */
{ 0x00000186, 0x0000 }, /* R390 - FLL1 Synchroniser 6 */
+ { 0x00000187, 0x0001 }, /* R391 - FLL1 Synchroniser 7 */
{ 0x00000189, 0x0000 }, /* R393 - FLL1 Spread Spectrum */
{ 0x0000018A, 0x0004 }, /* R394 - FLL1 GPIO Clock */
{ 0x00000191, 0x0000 }, /* R401 - FLL2 Control 1 */
@@ -797,25 +320,38 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000195, 0x0004 }, /* R405 - FLL2 Control 5 */
{ 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */
{ 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */
+ { 0x00000198, 0x0000 }, /* R408 - FLL2 NCO Test 0 */
+ { 0x00000199, 0x0000 }, /* R409 - FLL2 Control 7 */
{ 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */
{ 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */
{ 0x000001A3, 0x0000 }, /* R419 - FLL2 Synchroniser 3 */
{ 0x000001A4, 0x0000 }, /* R420 - FLL2 Synchroniser 4 */
{ 0x000001A5, 0x0000 }, /* R421 - FLL2 Synchroniser 5 */
{ 0x000001A6, 0x0000 }, /* R422 - FLL2 Synchroniser 6 */
+ { 0x000001A7, 0x0001 }, /* R423 - FLL2 Synchroniser 7 */
{ 0x000001A9, 0x0000 }, /* R425 - FLL2 Spread Spectrum */
{ 0x000001AA, 0x0004 }, /* R426 - FLL2 GPIO Clock */
{ 0x00000200, 0x0006 }, /* R512 - Mic Charge Pump 1 */
{ 0x00000210, 0x00D4 }, /* R528 - LDO1 Control 1 */
+ { 0x00000212, 0x0000 }, /* R530 - LDO1 Control 2 */
{ 0x00000213, 0x0344 }, /* R531 - LDO2 Control 1 */
{ 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */
{ 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */
{ 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */
+ { 0x00000225, 0x0400 }, /* R549 - HP Ctrl 1L */
+ { 0x00000226, 0x0400 }, /* R550 - HP Ctrl 1R */
{ 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */
{ 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */
+ { 0x0000029C, 0x0000 }, /* R668 - Headphone Detect 2 */
+ { 0x0000029F, 0x0000 }, /* R671 - Headphone Detect Test */
+ { 0x000002A2, 0x0000 }, /* R674 - Micd clamp control */
{ 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */
{ 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */
{ 0x000002A5, 0x0000 }, /* R677 - Mic Detect 3 */
+ { 0x000002A6, 0x3737 }, /* R678 - Mic Detect Level 1 */
+ { 0x000002A7, 0x372C }, /* R679 - Mic Detect Level 2 */
+ { 0x000002A8, 0x1422 }, /* R680 - Mic Detect Level 3 */
+ { 0x000002A9, 0x030A }, /* R681 - Mic Detect Level 4 */
{ 0x000002C3, 0x0000 }, /* R707 - Mic noise mix control 1 */
{ 0x000002CB, 0x0000 }, /* R715 - Isolation control */
{ 0x000002D3, 0x0000 }, /* R723 - Jack detect analogue */
@@ -843,53 +379,47 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000400, 0x0000 }, /* R1024 - Output Enables 1 */
{ 0x00000408, 0x0000 }, /* R1032 - Output Rate 1 */
{ 0x00000409, 0x0022 }, /* R1033 - Output Volume Ramp */
- { 0x00000410, 0x0080 }, /* R1040 - Output Path Config 1L */
+ { 0x00000410, 0x6080 }, /* R1040 - Output Path Config 1L */
{ 0x00000411, 0x0180 }, /* R1041 - DAC Digital Volume 1L */
- { 0x00000412, 0x0080 }, /* R1042 - DAC Volume Limit 1L */
+ { 0x00000412, 0x0081 }, /* R1042 - DAC Volume Limit 1L */
{ 0x00000413, 0x0001 }, /* R1043 - Noise Gate Select 1L */
{ 0x00000414, 0x0080 }, /* R1044 - Output Path Config 1R */
{ 0x00000415, 0x0180 }, /* R1045 - DAC Digital Volume 1R */
- { 0x00000416, 0x0080 }, /* R1046 - DAC Volume Limit 1R */
+ { 0x00000416, 0x0081 }, /* R1046 - DAC Volume Limit 1R */
{ 0x00000417, 0x0002 }, /* R1047 - Noise Gate Select 1R */
- { 0x00000418, 0x0080 }, /* R1048 - Output Path Config 2L */
+ { 0x00000418, 0xA080 }, /* R1048 - Output Path Config 2L */
{ 0x00000419, 0x0180 }, /* R1049 - DAC Digital Volume 2L */
- { 0x0000041A, 0x0080 }, /* R1050 - DAC Volume Limit 2L */
+ { 0x0000041A, 0x0081 }, /* R1050 - DAC Volume Limit 2L */
{ 0x0000041B, 0x0004 }, /* R1051 - Noise Gate Select 2L */
{ 0x0000041C, 0x0080 }, /* R1052 - Output Path Config 2R */
{ 0x0000041D, 0x0180 }, /* R1053 - DAC Digital Volume 2R */
- { 0x0000041E, 0x0080 }, /* R1054 - DAC Volume Limit 2R */
+ { 0x0000041E, 0x0081 }, /* R1054 - DAC Volume Limit 2R */
{ 0x0000041F, 0x0008 }, /* R1055 - Noise Gate Select 2R */
- { 0x00000420, 0x0080 }, /* R1056 - Output Path Config 3L */
+ { 0x00000420, 0xA080 }, /* R1056 - Output Path Config 3L */
{ 0x00000421, 0x0180 }, /* R1057 - DAC Digital Volume 3L */
- { 0x00000422, 0x0080 }, /* R1058 - DAC Volume Limit 3L */
+ { 0x00000422, 0x0081 }, /* R1058 - DAC Volume Limit 3L */
{ 0x00000423, 0x0010 }, /* R1059 - Noise Gate Select 3L */
- { 0x00000424, 0x0080 }, /* R1060 - Output Path Config 3R */
- { 0x00000425, 0x0180 }, /* R1061 - DAC Digital Volume 3R */
- { 0x00000426, 0x0080 }, /* R1062 - DAC Volume Limit 3R */
- { 0x00000428, 0x0000 }, /* R1064 - Output Path Config 4L */
+ { 0x00000428, 0xE000 }, /* R1064 - Output Path Config 4L */
{ 0x00000429, 0x0180 }, /* R1065 - DAC Digital Volume 4L */
- { 0x0000042A, 0x0080 }, /* R1066 - Out Volume 4L */
+ { 0x0000042A, 0x0081 }, /* R1066 - Out Volume 4L */
{ 0x0000042B, 0x0040 }, /* R1067 - Noise Gate Select 4L */
- { 0x0000042C, 0x0000 }, /* R1068 - Output Path Config 4R */
{ 0x0000042D, 0x0180 }, /* R1069 - DAC Digital Volume 4R */
- { 0x0000042E, 0x0080 }, /* R1070 - Out Volume 4R */
+ { 0x0000042E, 0x0081 }, /* R1070 - Out Volume 4R */
{ 0x0000042F, 0x0080 }, /* R1071 - Noise Gate Select 4R */
{ 0x00000430, 0x0000 }, /* R1072 - Output Path Config 5L */
{ 0x00000431, 0x0180 }, /* R1073 - DAC Digital Volume 5L */
- { 0x00000432, 0x0080 }, /* R1074 - DAC Volume Limit 5L */
+ { 0x00000432, 0x0081 }, /* R1074 - DAC Volume Limit 5L */
{ 0x00000433, 0x0100 }, /* R1075 - Noise Gate Select 5L */
- { 0x00000434, 0x0000 }, /* R1076 - Output Path Config 5R */
{ 0x00000435, 0x0180 }, /* R1077 - DAC Digital Volume 5R */
- { 0x00000436, 0x0080 }, /* R1078 - DAC Volume Limit 5R */
- { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */
+ { 0x00000436, 0x0081 }, /* R1078 - DAC Volume Limit 5R */
+ { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */
+ { 0x00000440, 0x8FFF }, /* R1088 - DRE Enable */
+ { 0x00000442, 0x3F0A }, /* R1090 - DRE Control 2 */
+ { 0x00000443, 0xDC1F }, /* R1090 - DRE Control 3 */
{ 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */
- { 0x00000458, 0x0001 }, /* R1112 - Noise Gate Control */
+ { 0x00000458, 0x000B }, /* R1112 - Noise Gate Control */
{ 0x00000490, 0x0069 }, /* R1168 - PDM SPK1 CTRL 1 */
{ 0x00000491, 0x0000 }, /* R1169 - PDM SPK1 CTRL 2 */
- { 0x000004DC, 0x0000 }, /* R1244 - DAC comp 1 */
- { 0x000004DD, 0x0000 }, /* R1245 - DAC comp 2 */
- { 0x000004DE, 0x0000 }, /* R1246 - DAC comp 3 */
- { 0x000004DF, 0x0000 }, /* R1247 - DAC comp 4 */
{ 0x00000500, 0x000C }, /* R1280 - AIF1 BCLK Ctrl */
{ 0x00000501, 0x0008 }, /* R1281 - AIF1 Tx Pin Ctrl */
{ 0x00000502, 0x0000 }, /* R1282 - AIF1 Rx Pin Ctrl */
@@ -917,7 +447,6 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000518, 0x0007 }, /* R1304 - AIF1 Frame Ctrl 18 */
{ 0x00000519, 0x0000 }, /* R1305 - AIF1 Tx Enables */
{ 0x0000051A, 0x0000 }, /* R1306 - AIF1 Rx Enables */
- { 0x0000051B, 0x0000 }, /* R1307 - AIF1 Force Write */
{ 0x00000540, 0x000C }, /* R1344 - AIF2 BCLK Ctrl */
{ 0x00000541, 0x0008 }, /* R1345 - AIF2 Tx Pin Ctrl */
{ 0x00000542, 0x0000 }, /* R1346 - AIF2 Rx Pin Ctrl */
@@ -933,7 +462,6 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000552, 0x0001 }, /* R1362 - AIF2 Frame Ctrl 12 */
{ 0x00000559, 0x0000 }, /* R1369 - AIF2 Tx Enables */
{ 0x0000055A, 0x0000 }, /* R1370 - AIF2 Rx Enables */
- { 0x0000055B, 0x0000 }, /* R1371 - AIF2 Force Write */
{ 0x00000580, 0x000C }, /* R1408 - AIF3 BCLK Ctrl */
{ 0x00000581, 0x0008 }, /* R1409 - AIF3 Tx Pin Ctrl */
{ 0x00000582, 0x0000 }, /* R1410 - AIF3 Rx Pin Ctrl */
@@ -949,7 +477,6 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000592, 0x0001 }, /* R1426 - AIF3 Frame Ctrl 12 */
{ 0x00000599, 0x0000 }, /* R1433 - AIF3 Tx Enables */
{ 0x0000059A, 0x0000 }, /* R1434 - AIF3 Rx Enables */
- { 0x0000059B, 0x0000 }, /* R1435 - AIF3 Force Write */
{ 0x000005E3, 0x0004 }, /* R1507 - SLIMbus Framer Ref Gear */
{ 0x000005E5, 0x0000 }, /* R1509 - SLIMbus Rates 1 */
{ 0x000005E6, 0x0000 }, /* R1510 - SLIMbus Rates 2 */
@@ -1273,22 +800,6 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x000008CD, 0x0080 }, /* R2253 - DRC1RMIX Input 3 Volume */
{ 0x000008CE, 0x0000 }, /* R2254 - DRC1RMIX Input 4 Source */
{ 0x000008CF, 0x0080 }, /* R2255 - DRC1RMIX Input 4 Volume */
- { 0x000008D0, 0x0000 }, /* R2256 - DRC2LMIX Input 1 Source */
- { 0x000008D1, 0x0080 }, /* R2257 - DRC2LMIX Input 1 Volume */
- { 0x000008D2, 0x0000 }, /* R2258 - DRC2LMIX Input 2 Source */
- { 0x000008D3, 0x0080 }, /* R2259 - DRC2LMIX Input 2 Volume */
- { 0x000008D4, 0x0000 }, /* R2260 - DRC2LMIX Input 3 Source */
- { 0x000008D5, 0x0080 }, /* R2261 - DRC2LMIX Input 3 Volume */
- { 0x000008D6, 0x0000 }, /* R2262 - DRC2LMIX Input 4 Source */
- { 0x000008D7, 0x0080 }, /* R2263 - DRC2LMIX Input 4 Volume */
- { 0x000008D8, 0x0000 }, /* R2264 - DRC2RMIX Input 1 Source */
- { 0x000008D9, 0x0080 }, /* R2265 - DRC2RMIX Input 1 Volume */
- { 0x000008DA, 0x0000 }, /* R2266 - DRC2RMIX Input 2 Source */
- { 0x000008DB, 0x0080 }, /* R2267 - DRC2RMIX Input 2 Volume */
- { 0x000008DC, 0x0000 }, /* R2268 - DRC2RMIX Input 3 Source */
- { 0x000008DD, 0x0080 }, /* R2269 - DRC2RMIX Input 3 Volume */
- { 0x000008DE, 0x0000 }, /* R2270 - DRC2RMIX Input 4 Source */
- { 0x000008DF, 0x0080 }, /* R2271 - DRC2RMIX Input 4 Volume */
{ 0x00000900, 0x0000 }, /* R2304 - HPLP1MIX Input 1 Source */
{ 0x00000901, 0x0080 }, /* R2305 - HPLP1MIX Input 1 Volume */
{ 0x00000902, 0x0000 }, /* R2306 - HPLP1MIX Input 2 Source */
@@ -1380,7 +891,6 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000D1B, 0xFFFF }, /* R3355 - IRQ2 Status 4 Mask */
{ 0x00000D1C, 0xFFFF }, /* R3356 - IRQ2 Status 5 Mask */
{ 0x00000D1F, 0x0000 }, /* R3359 - IRQ2 Control */
- { 0x00000D41, 0x0000 }, /* R3393 - ADSP2 IRQ0 */
{ 0x00000D53, 0xFFFF }, /* R3411 - AOD IRQ Mask IRQ1 */
{ 0x00000D54, 0xFFFF }, /* R3412 - AOD IRQ Mask IRQ2 */
{ 0x00000D56, 0x0000 }, /* R3414 - Jack detect debounce */
@@ -1475,11 +985,6 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000E82, 0x0018 }, /* R3714 - DRC1 ctrl3 */
{ 0x00000E83, 0x0000 }, /* R3715 - DRC1 ctrl4 */
{ 0x00000E84, 0x0000 }, /* R3716 - DRC1 ctrl5 */
- { 0x00000E89, 0x0018 }, /* R3721 - DRC2 ctrl1 */
- { 0x00000E8A, 0x0933 }, /* R3722 - DRC2 ctrl2 */
- { 0x00000E8B, 0x0018 }, /* R3723 - DRC2 ctrl3 */
- { 0x00000E8C, 0x0000 }, /* R3724 - DRC2 ctrl4 */
- { 0x00000E8D, 0x0000 }, /* R3725 - DRC2 ctrl5 */
{ 0x00000EC0, 0x0000 }, /* R3776 - HPLPF1_1 */
{ 0x00000EC1, 0x0000 }, /* R3777 - HPLPF1_2 */
{ 0x00000EC4, 0x0000 }, /* R3780 - HPLPF2_1 */
@@ -1490,16 +995,12 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000ECD, 0x0000 }, /* R3789 - HPLPF4_2 */
{ 0x00000EE0, 0x0000 }, /* R3808 - ASRC_ENABLE */
{ 0x00000EE2, 0x0000 }, /* R3810 - ASRC_RATE1 */
- { 0x00000EE3, 0x4000 }, /* R3811 - ASRC_RATE2 */
{ 0x00000EF0, 0x0000 }, /* R3824 - ISRC 1 CTRL 1 */
{ 0x00000EF1, 0x0000 }, /* R3825 - ISRC 1 CTRL 2 */
{ 0x00000EF2, 0x0000 }, /* R3826 - ISRC 1 CTRL 3 */
{ 0x00000EF3, 0x0000 }, /* R3827 - ISRC 2 CTRL 1 */
{ 0x00000EF4, 0x0000 }, /* R3828 - ISRC 2 CTRL 2 */
{ 0x00000EF5, 0x0000 }, /* R3829 - ISRC 2 CTRL 3 */
- { 0x00000EF6, 0x0000 }, /* R3830 - ISRC 3 CTRL 1 */
- { 0x00000EF7, 0x0000 }, /* R3831 - ISRC 3 CTRL 2 */
- { 0x00000EF8, 0x0000 }, /* R3832 - ISRC 3 CTRL 3 */
{ 0x00001100, 0x0010 }, /* R4352 - DSP1 Control 1 */
{ 0x00001101, 0x0000 }, /* R4353 - DSP1 Clocking 1 */
};
@@ -1536,6 +1037,8 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_7:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_8:
case ARIZONA_COMFORT_NOISE_GENERATOR:
case ARIZONA_HAPTICS_CONTROL_1:
case ARIZONA_HAPTICS_CONTROL_2:
@@ -1564,6 +1067,7 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_RATE_ESTIMATOR_3:
case ARIZONA_RATE_ESTIMATOR_4:
case ARIZONA_RATE_ESTIMATOR_5:
+ case ARIZONA_DYNAMIC_FREQUENCY_SCALING_1:
case ARIZONA_FLL1_CONTROL_1:
case ARIZONA_FLL1_CONTROL_2:
case ARIZONA_FLL1_CONTROL_3:
@@ -1571,12 +1075,15 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_FLL1_CONTROL_5:
case ARIZONA_FLL1_CONTROL_6:
case ARIZONA_FLL1_LOOP_FILTER_TEST_1:
+ case ARIZONA_FLL1_NCO_TEST_0:
+ case ARIZONA_FLL1_CONTROL_7:
case ARIZONA_FLL1_SYNCHRONISER_1:
case ARIZONA_FLL1_SYNCHRONISER_2:
case ARIZONA_FLL1_SYNCHRONISER_3:
case ARIZONA_FLL1_SYNCHRONISER_4:
case ARIZONA_FLL1_SYNCHRONISER_5:
case ARIZONA_FLL1_SYNCHRONISER_6:
+ case ARIZONA_FLL1_SYNCHRONISER_7:
case ARIZONA_FLL1_SPREAD_SPECTRUM:
case ARIZONA_FLL1_GPIO_CLOCK:
case ARIZONA_FLL2_CONTROL_1:
@@ -1586,16 +1093,20 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_FLL2_CONTROL_5:
case ARIZONA_FLL2_CONTROL_6:
case ARIZONA_FLL2_LOOP_FILTER_TEST_1:
+ case ARIZONA_FLL2_NCO_TEST_0:
+ case ARIZONA_FLL2_CONTROL_7:
case ARIZONA_FLL2_SYNCHRONISER_1:
case ARIZONA_FLL2_SYNCHRONISER_2:
case ARIZONA_FLL2_SYNCHRONISER_3:
case ARIZONA_FLL2_SYNCHRONISER_4:
case ARIZONA_FLL2_SYNCHRONISER_5:
case ARIZONA_FLL2_SYNCHRONISER_6:
+ case ARIZONA_FLL2_SYNCHRONISER_7:
case ARIZONA_FLL2_SPREAD_SPECTRUM:
case ARIZONA_FLL2_GPIO_CLOCK:
case ARIZONA_MIC_CHARGE_PUMP_1:
case ARIZONA_LDO1_CONTROL_1:
+ case ARIZONA_LDO1_CONTROL_2:
case ARIZONA_LDO2_CONTROL_1:
case ARIZONA_MIC_BIAS_CTRL_1:
case ARIZONA_MIC_BIAS_CTRL_2:
@@ -1603,9 +1114,15 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_ACCESSORY_DETECT_MODE_1:
case ARIZONA_HEADPHONE_DETECT_1:
case ARIZONA_HEADPHONE_DETECT_2:
+ case ARIZONA_HP_DACVAL:
+ case ARIZONA_MICD_CLAMP_CONTROL:
case ARIZONA_MIC_DETECT_1:
case ARIZONA_MIC_DETECT_2:
case ARIZONA_MIC_DETECT_3:
+ case ARIZONA_MIC_DETECT_LEVEL_1:
+ case ARIZONA_MIC_DETECT_LEVEL_2:
+ case ARIZONA_MIC_DETECT_LEVEL_3:
+ case ARIZONA_MIC_DETECT_LEVEL_4:
case ARIZONA_MIC_NOISE_MIX_CONTROL_1:
case ARIZONA_ISOLATION_CONTROL:
case ARIZONA_JACK_DETECT_ANALOGUE:
@@ -1673,10 +1190,15 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_DAC_DIGITAL_VOLUME_5R:
case ARIZONA_DAC_VOLUME_LIMIT_5R:
case ARIZONA_NOISE_GATE_SELECT_5R:
+ case ARIZONA_DRE_ENABLE:
+ case ARIZONA_DRE_CONTROL_2:
+ case ARIZONA_DRE_CONTROL_3:
case ARIZONA_DAC_AEC_CONTROL_1:
case ARIZONA_NOISE_GATE_CONTROL:
case ARIZONA_PDM_SPK1_CTRL_1:
case ARIZONA_PDM_SPK1_CTRL_2:
+ case ARIZONA_SPK_CTRL_2:
+ case ARIZONA_SPK_CTRL_3:
case ARIZONA_DAC_COMP_1:
case ARIZONA_DAC_COMP_2:
case ARIZONA_DAC_COMP_3:
@@ -2319,9 +1841,37 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP1_CLOCKING_1:
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
+ case ARIZONA_DSP1_STATUS_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_1:
+ case ARIZONA_DSP1_WDMA_BUFFER_2:
+ case ARIZONA_DSP1_WDMA_BUFFER_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_5:
+ case ARIZONA_DSP1_WDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_BUFFER_7:
+ case ARIZONA_DSP1_WDMA_BUFFER_8:
+ case ARIZONA_DSP1_RDMA_BUFFER_1:
+ case ARIZONA_DSP1_RDMA_BUFFER_2:
+ case ARIZONA_DSP1_RDMA_BUFFER_3:
+ case ARIZONA_DSP1_RDMA_BUFFER_4:
+ case ARIZONA_DSP1_RDMA_BUFFER_5:
+ case ARIZONA_DSP1_RDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_CONFIG_1:
+ case ARIZONA_DSP1_WDMA_CONFIG_2:
+ case ARIZONA_DSP1_RDMA_CONFIG_1:
+ case ARIZONA_DSP1_SCRATCH_0:
+ case ARIZONA_DSP1_SCRATCH_1:
+ case ARIZONA_DSP1_SCRATCH_2:
+ case ARIZONA_DSP1_SCRATCH_3:
return true;
default:
- return false;
+ if ((reg >= 0x100000 && reg < 0x106000) ||
+ (reg >= 0x180000 && reg < 0x180800) ||
+ (reg >= 0x190000 && reg < 0x194800) ||
+ (reg >= 0x1a8000 && reg < 0x1a9800))
+ return true;
+ else
+ return false;
}
}
@@ -2331,11 +1881,16 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg)
case ARIZONA_SOFTWARE_RESET:
case ARIZONA_DEVICE_REVISION:
case ARIZONA_OUTPUT_STATUS_1:
+ case ARIZONA_RAW_OUTPUT_STATUS_1:
+ case ARIZONA_SLIMBUS_RX_PORT_STATUS:
+ case ARIZONA_SLIMBUS_TX_PORT_STATUS:
case ARIZONA_SAMPLE_RATE_1_STATUS:
case ARIZONA_SAMPLE_RATE_2_STATUS:
case ARIZONA_SAMPLE_RATE_3_STATUS:
case ARIZONA_HAPTICS_STATUS:
case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_FLL1_NCO_TEST_0:
+ case ARIZONA_FLL2_NCO_TEST_0:
case ARIZONA_FX_CTRL2:
case ARIZONA_INTERRUPT_STATUS_1:
case ARIZONA_INTERRUPT_STATUS_2:
@@ -2359,22 +1914,54 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg)
case ARIZONA_AOD_IRQ1:
case ARIZONA_AOD_IRQ2:
case ARIZONA_AOD_IRQ_RAW_STATUS:
+ case ARIZONA_DSP1_CLOCKING_1:
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
+ case ARIZONA_DSP1_STATUS_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_1:
+ case ARIZONA_DSP1_WDMA_BUFFER_2:
+ case ARIZONA_DSP1_WDMA_BUFFER_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_5:
+ case ARIZONA_DSP1_WDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_BUFFER_7:
+ case ARIZONA_DSP1_WDMA_BUFFER_8:
+ case ARIZONA_DSP1_RDMA_BUFFER_1:
+ case ARIZONA_DSP1_RDMA_BUFFER_2:
+ case ARIZONA_DSP1_RDMA_BUFFER_3:
+ case ARIZONA_DSP1_RDMA_BUFFER_4:
+ case ARIZONA_DSP1_RDMA_BUFFER_5:
+ case ARIZONA_DSP1_RDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_CONFIG_1:
+ case ARIZONA_DSP1_WDMA_CONFIG_2:
+ case ARIZONA_DSP1_RDMA_CONFIG_1:
+ case ARIZONA_DSP1_SCRATCH_0:
+ case ARIZONA_DSP1_SCRATCH_1:
+ case ARIZONA_DSP1_SCRATCH_2:
+ case ARIZONA_DSP1_SCRATCH_3:
case ARIZONA_HEADPHONE_DETECT_2:
+ case ARIZONA_HP_DACVAL:
case ARIZONA_MIC_DETECT_3:
return true;
default:
- return false;
+ if ((reg >= 0x100000 && reg < 0x106000) ||
+ (reg >= 0x180000 && reg < 0x180800) ||
+ (reg >= 0x190000 && reg < 0x194800) ||
+ (reg >= 0x1a8000 && reg < 0x1a9800))
+ return true;
+ else
+ return false;
}
}
+#define WM5102_MAX_REGISTER 0x1a9800
+
const struct regmap_config wm5102_spi_regmap = {
.reg_bits = 32,
.pad_bits = 16,
.val_bits = 16,
- .max_register = ARIZONA_DSP1_STATUS_2,
+ .max_register = WM5102_MAX_REGISTER,
.readable_reg = wm5102_readable_register,
.volatile_reg = wm5102_volatile_register,
@@ -2388,7 +1975,7 @@ const struct regmap_config wm5102_i2c_regmap = {
.reg_bits = 32,
.val_bits = 16,
- .max_register = ARIZONA_DSP1_STATUS_2,
+ .max_register = WM5102_MAX_REGISTER,
.readable_reg = wm5102_readable_register,
.volatile_reg = wm5102_volatile_register,
diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c
index adda6b10b90..41a7f6fb780 100644
--- a/drivers/mfd/wm5110-tables.c
+++ b/drivers/mfd/wm5110-tables.c
@@ -14,6 +14,7 @@
#include <linux/mfd/arizona/core.h>
#include <linux/mfd/arizona/registers.h>
+#include <linux/device.h>
#include "arizona.h"
@@ -223,6 +224,31 @@ static const struct reg_default wm5110_revb_patch[] = {
{ 0x80, 0x0 },
};
+static const struct reg_default wm5110_revd_patch[] = {
+ { 0x80, 0x3 },
+ { 0x80, 0x3 },
+ { 0x393, 0x27 },
+ { 0x394, 0x27 },
+ { 0x395, 0x27 },
+ { 0x396, 0x27 },
+ { 0x397, 0x27 },
+ { 0x398, 0x26 },
+ { 0x221, 0x90 },
+ { 0x211, 0x8 },
+ { 0x36c, 0x1fb },
+ { 0x26e, 0x64 },
+ { 0x26f, 0xea },
+ { 0x270, 0x1f16 },
+ { 0x51b, 0x1 },
+ { 0x55b, 0x1 },
+ { 0x59b, 0x1 },
+ { 0x4f0, 0x633 },
+ { 0x441, 0xc059 },
+ { 0x209, 0x27 },
+ { 0x80, 0x0 },
+ { 0x80, 0x0 },
+};
+
/* We use a function so we can use ARRAY_SIZE() */
int wm5110_patch(struct arizona *arizona)
{
@@ -235,7 +261,10 @@ int wm5110_patch(struct arizona *arizona)
return regmap_register_patch(arizona->regmap,
wm5110_revb_patch,
ARRAY_SIZE(wm5110_revb_patch));
-
+ case 3:
+ return regmap_register_patch(arizona->regmap,
+ wm5110_revd_patch,
+ ARRAY_SIZE(wm5110_revd_patch));
default:
return 0;
}
@@ -243,6 +272,12 @@ int wm5110_patch(struct arizona *arizona)
EXPORT_SYMBOL_GPL(wm5110_patch);
static const struct regmap_irq wm5110_aod_irqs[ARIZONA_NUM_IRQ] = {
+ [ARIZONA_IRQ_MICD_CLAMP_FALL] = {
+ .mask = ARIZONA_MICD_CLAMP_FALL_EINT1
+ },
+ [ARIZONA_IRQ_MICD_CLAMP_RISE] = {
+ .mask = ARIZONA_MICD_CLAMP_RISE_EINT1
+ },
[ARIZONA_IRQ_GP5_FALL] = { .mask = ARIZONA_GP5_FALL_EINT1 },
[ARIZONA_IRQ_GP5_RISE] = { .mask = ARIZONA_GP5_RISE_EINT1 },
[ARIZONA_IRQ_JD_FALL] = { .mask = ARIZONA_JD1_FALL_EINT1 },
@@ -255,6 +290,7 @@ const struct regmap_irq_chip wm5110_aod = {
.mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1,
.ack_base = ARIZONA_AOD_IRQ1,
.wake_base = ARIZONA_WAKE_CONTROL,
+ .wake_invert = 1,
.num_regs = 1,
.irqs = wm5110_aod_irqs,
.num_irqs = ARRAY_SIZE(wm5110_aod_irqs),
@@ -432,10 +468,12 @@ static const struct reg_default wm5110_reg_default[] = {
{ 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */
{ 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */
{ 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */
- { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 1 */
- { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 2 */
- { 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 3 */
- { 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 4 */
+ { 0x00000066, 0x01FF }, /* R102 - Always On Triggers Sequence Select 1 */
+ { 0x00000067, 0x01FF }, /* R103 - Always On Triggers Sequence Select 2 */
+ { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 3 */
+ { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 4 */
+ { 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 5 */
+ { 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 6 */
{ 0x00000070, 0x0000 }, /* R112 - Comfort Noise Generator */
{ 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */
{ 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */
@@ -467,12 +505,14 @@ static const struct reg_default wm5110_reg_default[] = {
{ 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */
{ 0x00000177, 0x0281 }, /* R375 - FLL1 Loop Filter Test 1 */
{ 0x00000178, 0x0000 }, /* R376 - FLL1 NCO Test 0 */
+ { 0x00000179, 0x0000 }, /* R376 - FLL1 Control 7 */
{ 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */
{ 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */
{ 0x00000183, 0x0000 }, /* R387 - FLL1 Synchroniser 3 */
{ 0x00000184, 0x0000 }, /* R388 - FLL1 Synchroniser 4 */
{ 0x00000185, 0x0000 }, /* R389 - FLL1 Synchroniser 5 */
{ 0x00000186, 0x0000 }, /* R390 - FLL1 Synchroniser 6 */
+ { 0x00000187, 0x0001 }, /* R390 - FLL1 Synchroniser 7 */
{ 0x00000189, 0x0000 }, /* R393 - FLL1 Spread Spectrum */
{ 0x0000018A, 0x0004 }, /* R394 - FLL1 GPIO Clock */
{ 0x00000191, 0x0000 }, /* R401 - FLL2 Control 1 */
@@ -483,30 +523,40 @@ static const struct reg_default wm5110_reg_default[] = {
{ 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */
{ 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */
{ 0x00000198, 0x0000 }, /* R408 - FLL2 NCO Test 0 */
+ { 0x00000199, 0x0000 }, /* R408 - FLL2 Control 7 */
{ 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */
{ 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */
{ 0x000001A3, 0x0000 }, /* R419 - FLL2 Synchroniser 3 */
{ 0x000001A4, 0x0000 }, /* R420 - FLL2 Synchroniser 4 */
{ 0x000001A5, 0x0000 }, /* R421 - FLL2 Synchroniser 5 */
{ 0x000001A6, 0x0000 }, /* R422 - FLL2 Synchroniser 6 */
+ { 0x000001A7, 0x0001 }, /* R422 - FLL2 Synchroniser 7 */
{ 0x000001A9, 0x0000 }, /* R425 - FLL2 Spread Spectrum */
{ 0x000001AA, 0x0004 }, /* R426 - FLL2 GPIO Clock */
{ 0x00000200, 0x0006 }, /* R512 - Mic Charge Pump 1 */
{ 0x00000210, 0x0184 }, /* R528 - LDO1 Control 1 */
- { 0x00000213, 0x0344 }, /* R531 - LDO2 Control 1 */
+ { 0x00000213, 0x03E4 }, /* R531 - LDO2 Control 1 */
{ 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */
{ 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */
{ 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */
{ 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */
- { 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */
+ { 0x0000029B, 0x0028 }, /* R667 - Headphone Detect 1 */
{ 0x0000029C, 0x0000 }, /* R668 - Headphone Detect 2 */
+ { 0x000002A2, 0x0000 }, /* R674 - Micd clamp control */
{ 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */
{ 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */
+ { 0x000002A5, 0x0000 }, /* R677 - Mic Detect 3 */
+ { 0x000002A6, 0x3737 }, /* R678 - Mic Detect Level 1 */
+ { 0x000002A7, 0x372C }, /* R679 - Mic Detect Level 2 */
+ { 0x000002A8, 0x1422 }, /* R680 - Mic Detect Level 3 */
+ { 0x000002A9, 0x300A }, /* R681 - Mic Detect Level 4 */
{ 0x000002C3, 0x0000 }, /* R707 - Mic noise mix control 1 */
+ { 0x000002CB, 0x0000 }, /* R715 - Isolation control */
{ 0x000002D3, 0x0000 }, /* R723 - Jack detect analogue */
{ 0x00000300, 0x0000 }, /* R768 - Input Enables */
{ 0x00000308, 0x0000 }, /* R776 - Input Rate */
{ 0x00000309, 0x0022 }, /* R777 - Input Volume Ramp */
+ { 0x0000030C, 0x0002 }, /* R780 - HPF Control */
{ 0x00000310, 0x2080 }, /* R784 - IN1L Control */
{ 0x00000311, 0x0180 }, /* R785 - ADC Digital Volume 1L */
{ 0x00000312, 0x0000 }, /* R786 - DMIC1L Control */
@@ -528,6 +578,7 @@ static const struct reg_default wm5110_reg_default[] = {
{ 0x00000328, 0x2000 }, /* R808 - IN4L Control */
{ 0x00000329, 0x0180 }, /* R809 - ADC Digital Volume 4L */
{ 0x0000032A, 0x0000 }, /* R810 - DMIC4L Control */
+ { 0x0000032C, 0x0000 }, /* R812 - IN4R Control */
{ 0x0000032D, 0x0180 }, /* R813 - ADC Digital Volume 4R */
{ 0x0000032E, 0x0000 }, /* R814 - DMIC4R Control */
{ 0x00000400, 0x0000 }, /* R1024 - Output Enables 1 */
@@ -581,14 +632,18 @@ static const struct reg_default wm5110_reg_default[] = {
{ 0x0000043D, 0x0180 }, /* R1085 - DAC Digital Volume 6R */
{ 0x0000043E, 0x0080 }, /* R1086 - DAC Volume Limit 6R */
{ 0x0000043F, 0x0800 }, /* R1087 - Noise Gate Select 6R */
+ { 0x00000440, 0x8FFF }, /* R1088 - DRE Enable */
{ 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */
- { 0x00000458, 0x0001 }, /* R1112 - Noise Gate Control */
+ { 0x00000458, 0x0000 }, /* R1112 - Noise Gate Control */
{ 0x00000480, 0x0040 }, /* R1152 - Class W ANC Threshold 1 */
{ 0x00000481, 0x0040 }, /* R1153 - Class W ANC Threshold 2 */
{ 0x00000490, 0x0069 }, /* R1168 - PDM SPK1 CTRL 1 */
{ 0x00000491, 0x0000 }, /* R1169 - PDM SPK1 CTRL 2 */
{ 0x00000492, 0x0069 }, /* R1170 - PDM SPK2 CTRL 1 */
{ 0x00000493, 0x0000 }, /* R1171 - PDM SPK2 CTRL 2 */
+ { 0x000004A0, 0x3480 }, /* R1184 - HP1 Short Circuit Ctrl */
+ { 0x000004A1, 0x3480 }, /* R1185 - HP2 Short Circuit Ctrl */
+ { 0x000004A2, 0x3480 }, /* R1186 - HP3 Short Circuit Ctrl */
{ 0x00000500, 0x000C }, /* R1280 - AIF1 BCLK Ctrl */
{ 0x00000501, 0x0008 }, /* R1281 - AIF1 Tx Pin Ctrl */
{ 0x00000502, 0x0000 }, /* R1282 - AIF1 Rx Pin Ctrl */
@@ -865,6 +920,38 @@ static const struct reg_default wm5110_reg_default[] = {
{ 0x0000074D, 0x0080 }, /* R1869 - AIF2TX2MIX Input 3 Volume */
{ 0x0000074E, 0x0000 }, /* R1870 - AIF2TX2MIX Input 4 Source */
{ 0x0000074F, 0x0080 }, /* R1871 - AIF2TX2MIX Input 4 Volume */
+ { 0x00000750, 0x0000 }, /* R1872 - AIF2TX3MIX Input 1 Source */
+ { 0x00000751, 0x0080 }, /* R1873 - AIF2TX3MIX Input 1 Volume */
+ { 0x00000752, 0x0000 }, /* R1874 - AIF2TX3MIX Input 2 Source */
+ { 0x00000753, 0x0080 }, /* R1875 - AIF2TX3MIX Input 2 Volume */
+ { 0x00000754, 0x0000 }, /* R1876 - AIF2TX3MIX Input 3 Source */
+ { 0x00000755, 0x0080 }, /* R1877 - AIF2TX3MIX Input 3 Volume */
+ { 0x00000756, 0x0000 }, /* R1878 - AIF2TX3MIX Input 4 Source */
+ { 0x00000757, 0x0080 }, /* R1879 - AIF2TX3MIX Input 4 Volume */
+ { 0x00000758, 0x0000 }, /* R1880 - AIF2TX4MIX Input 1 Source */
+ { 0x00000759, 0x0080 }, /* R1881 - AIF2TX4MIX Input 1 Volume */
+ { 0x0000075A, 0x0000 }, /* R1882 - AIF2TX4MIX Input 2 Source */
+ { 0x0000075B, 0x0080 }, /* R1883 - AIF2TX4MIX Input 2 Volume */
+ { 0x0000075C, 0x0000 }, /* R1884 - AIF2TX4MIX Input 3 Source */
+ { 0x0000075D, 0x0080 }, /* R1885 - AIF2TX4MIX Input 3 Volume */
+ { 0x0000075E, 0x0000 }, /* R1886 - AIF2TX4MIX Input 4 Source */
+ { 0x0000075F, 0x0080 }, /* R1887 - AIF2TX4MIX Input 4 Volume */
+ { 0x00000760, 0x0000 }, /* R1888 - AIF2TX5MIX Input 1 Source */
+ { 0x00000761, 0x0080 }, /* R1889 - AIF2TX5MIX Input 1 Volume */
+ { 0x00000762, 0x0000 }, /* R1890 - AIF2TX5MIX Input 2 Source */
+ { 0x00000763, 0x0080 }, /* R1891 - AIF2TX5MIX Input 2 Volume */
+ { 0x00000764, 0x0000 }, /* R1892 - AIF2TX5MIX Input 3 Source */
+ { 0x00000765, 0x0080 }, /* R1893 - AIF2TX5MIX Input 3 Volume */
+ { 0x00000766, 0x0000 }, /* R1894 - AIF2TX5MIX Input 4 Source */
+ { 0x00000767, 0x0080 }, /* R1895 - AIF2TX5MIX Input 4 Volume */
+ { 0x00000768, 0x0000 }, /* R1896 - AIF2TX6MIX Input 1 Source */
+ { 0x00000769, 0x0080 }, /* R1897 - AIF2TX6MIX Input 1 Volume */
+ { 0x0000076A, 0x0000 }, /* R1898 - AIF2TX6MIX Input 2 Source */
+ { 0x0000076B, 0x0080 }, /* R1899 - AIF2TX6MIX Input 2 Volume */
+ { 0x0000076C, 0x0000 }, /* R1900 - AIF2TX6MIX Input 3 Source */
+ { 0x0000076D, 0x0080 }, /* R1901 - AIF2TX6MIX Input 3 Volume */
+ { 0x0000076E, 0x0000 }, /* R1902 - AIF2TX6MIX Input 4 Source */
+ { 0x0000076F, 0x0080 }, /* R1903 - AIF2TX6MIX Input 4 Volume */
{ 0x00000780, 0x0000 }, /* R1920 - AIF3TX1MIX Input 1 Source */
{ 0x00000781, 0x0080 }, /* R1921 - AIF3TX1MIX Input 1 Volume */
{ 0x00000782, 0x0000 }, /* R1922 - AIF3TX1MIX Input 2 Source */
@@ -1194,7 +1281,6 @@ static const struct reg_default wm5110_reg_default[] = {
{ 0x00000D1B, 0xFFFF }, /* R3355 - IRQ2 Status 4 Mask */
{ 0x00000D1C, 0xFFFF }, /* R3356 - IRQ2 Status 5 Mask */
{ 0x00000D1F, 0x0000 }, /* R3359 - IRQ2 Control */
- { 0x00000D50, 0x0000 }, /* R3408 - AOD wkup and trig */
{ 0x00000D53, 0xFFFF }, /* R3411 - AOD IRQ Mask IRQ1 */
{ 0x00000D54, 0xFFFF }, /* R3412 - AOD IRQ Mask IRQ2 */
{ 0x00000D56, 0x0000 }, /* R3414 - Jack detect debounce */
@@ -1326,6 +1412,64 @@ static const struct reg_default wm5110_reg_default[] = {
{ 0x00001404, 0x0000 }, /* R5124 - DSP4 Status 1 */
};
+static bool wm5110_is_rev_b_adsp_memory(unsigned int reg)
+{
+ if ((reg >= 0x100000 && reg < 0x103000) ||
+ (reg >= 0x180000 && reg < 0x181000) ||
+ (reg >= 0x190000 && reg < 0x192000) ||
+ (reg >= 0x1a8000 && reg < 0x1a9000) ||
+ (reg >= 0x200000 && reg < 0x209000) ||
+ (reg >= 0x280000 && reg < 0x281000) ||
+ (reg >= 0x290000 && reg < 0x29a000) ||
+ (reg >= 0x2a8000 && reg < 0x2aa000) ||
+ (reg >= 0x300000 && reg < 0x30f000) ||
+ (reg >= 0x380000 && reg < 0x382000) ||
+ (reg >= 0x390000 && reg < 0x39e000) ||
+ (reg >= 0x3a8000 && reg < 0x3b6000) ||
+ (reg >= 0x400000 && reg < 0x403000) ||
+ (reg >= 0x480000 && reg < 0x481000) ||
+ (reg >= 0x490000 && reg < 0x492000) ||
+ (reg >= 0x4a8000 && reg < 0x4a9000))
+ return true;
+ else
+ return false;
+}
+
+static bool wm5110_is_rev_d_adsp_memory(unsigned int reg)
+{
+ if ((reg >= 0x100000 && reg < 0x106000) ||
+ (reg >= 0x180000 && reg < 0x182000) ||
+ (reg >= 0x190000 && reg < 0x198000) ||
+ (reg >= 0x1a8000 && reg < 0x1aa000) ||
+ (reg >= 0x200000 && reg < 0x20f000) ||
+ (reg >= 0x280000 && reg < 0x282000) ||
+ (reg >= 0x290000 && reg < 0x29c000) ||
+ (reg >= 0x2a6000 && reg < 0x2b4000) ||
+ (reg >= 0x300000 && reg < 0x30f000) ||
+ (reg >= 0x380000 && reg < 0x382000) ||
+ (reg >= 0x390000 && reg < 0x3a2000) ||
+ (reg >= 0x3a6000 && reg < 0x3b4000) ||
+ (reg >= 0x400000 && reg < 0x406000) ||
+ (reg >= 0x480000 && reg < 0x482000) ||
+ (reg >= 0x490000 && reg < 0x498000) ||
+ (reg >= 0x4a8000 && reg < 0x4aa000))
+ return true;
+ else
+ return false;
+}
+
+static bool wm5110_is_adsp_memory(struct device *dev, unsigned int reg)
+{
+ struct arizona *arizona = dev_get_drvdata(dev);
+
+ switch (arizona->rev) {
+ case 0 ... 2:
+ return wm5110_is_rev_b_adsp_memory(reg);
+ default:
+ return wm5110_is_rev_d_adsp_memory(reg);
+ }
+}
+
static bool wm5110_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -1357,6 +1501,8 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6:
case ARIZONA_COMFORT_NOISE_GENERATOR:
case ARIZONA_HAPTICS_CONTROL_1:
case ARIZONA_HAPTICS_CONTROL_2:
@@ -1391,6 +1537,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_FLL1_CONTROL_4:
case ARIZONA_FLL1_CONTROL_5:
case ARIZONA_FLL1_CONTROL_6:
+ case ARIZONA_FLL1_CONTROL_7:
case ARIZONA_FLL1_LOOP_FILTER_TEST_1:
case ARIZONA_FLL1_NCO_TEST_0:
case ARIZONA_FLL1_SYNCHRONISER_1:
@@ -1399,6 +1546,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_FLL1_SYNCHRONISER_4:
case ARIZONA_FLL1_SYNCHRONISER_5:
case ARIZONA_FLL1_SYNCHRONISER_6:
+ case ARIZONA_FLL1_SYNCHRONISER_7:
case ARIZONA_FLL1_SPREAD_SPECTRUM:
case ARIZONA_FLL1_GPIO_CLOCK:
case ARIZONA_FLL2_CONTROL_1:
@@ -1407,6 +1555,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_FLL2_CONTROL_4:
case ARIZONA_FLL2_CONTROL_5:
case ARIZONA_FLL2_CONTROL_6:
+ case ARIZONA_FLL2_CONTROL_7:
case ARIZONA_FLL2_LOOP_FILTER_TEST_1:
case ARIZONA_FLL2_NCO_TEST_0:
case ARIZONA_FLL2_SYNCHRONISER_1:
@@ -1415,6 +1564,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_FLL2_SYNCHRONISER_4:
case ARIZONA_FLL2_SYNCHRONISER_5:
case ARIZONA_FLL2_SYNCHRONISER_6:
+ case ARIZONA_FLL2_SYNCHRONISER_7:
case ARIZONA_FLL2_SPREAD_SPECTRUM:
case ARIZONA_FLL2_GPIO_CLOCK:
case ARIZONA_MIC_CHARGE_PUMP_1:
@@ -1426,15 +1576,22 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_ACCESSORY_DETECT_MODE_1:
case ARIZONA_HEADPHONE_DETECT_1:
case ARIZONA_HEADPHONE_DETECT_2:
+ case ARIZONA_MICD_CLAMP_CONTROL:
case ARIZONA_MIC_DETECT_1:
case ARIZONA_MIC_DETECT_2:
case ARIZONA_MIC_DETECT_3:
+ case ARIZONA_MIC_DETECT_LEVEL_1:
+ case ARIZONA_MIC_DETECT_LEVEL_2:
+ case ARIZONA_MIC_DETECT_LEVEL_3:
+ case ARIZONA_MIC_DETECT_LEVEL_4:
case ARIZONA_MIC_NOISE_MIX_CONTROL_1:
+ case ARIZONA_ISOLATION_CONTROL:
case ARIZONA_JACK_DETECT_ANALOGUE:
case ARIZONA_INPUT_ENABLES:
case ARIZONA_INPUT_ENABLES_STATUS:
case ARIZONA_INPUT_RATE:
case ARIZONA_INPUT_VOLUME_RAMP:
+ case ARIZONA_HPF_CONTROL:
case ARIZONA_IN1L_CONTROL:
case ARIZONA_ADC_DIGITAL_VOLUME_1L:
case ARIZONA_DMIC1L_CONTROL:
@@ -1456,6 +1613,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_IN4L_CONTROL:
case ARIZONA_ADC_DIGITAL_VOLUME_4L:
case ARIZONA_DMIC4L_CONTROL:
+ case ARIZONA_IN4R_CONTROL:
case ARIZONA_ADC_DIGITAL_VOLUME_4R:
case ARIZONA_DMIC4R_CONTROL:
case ARIZONA_OUTPUT_ENABLES_1:
@@ -1511,12 +1669,16 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_DAC_DIGITAL_VOLUME_6R:
case ARIZONA_DAC_VOLUME_LIMIT_6R:
case ARIZONA_NOISE_GATE_SELECT_6R:
+ case ARIZONA_DRE_ENABLE:
case ARIZONA_DAC_AEC_CONTROL_1:
case ARIZONA_NOISE_GATE_CONTROL:
case ARIZONA_PDM_SPK1_CTRL_1:
case ARIZONA_PDM_SPK1_CTRL_2:
case ARIZONA_PDM_SPK2_CTRL_1:
case ARIZONA_PDM_SPK2_CTRL_2:
+ case ARIZONA_HP1_SHORT_CIRCUIT_CTRL:
+ case ARIZONA_HP2_SHORT_CIRCUIT_CTRL:
+ case ARIZONA_HP3_SHORT_CIRCUIT_CTRL:
case ARIZONA_AIF1_BCLK_CTRL:
case ARIZONA_AIF1_TX_PIN_CTRL:
case ARIZONA_AIF1_RX_PIN_CTRL:
@@ -1795,6 +1957,38 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_AIF2TX2MIX_INPUT_3_VOLUME:
case ARIZONA_AIF2TX2MIX_INPUT_4_SOURCE:
case ARIZONA_AIF2TX2MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF2TX3MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF2TX3MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF2TX3MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF2TX3MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF2TX3MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF2TX3MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF2TX3MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF2TX4MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF2TX4MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF2TX4MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF2TX4MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF2TX4MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF2TX4MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF2TX4MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF2TX5MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF2TX5MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF2TX5MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF2TX5MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF2TX5MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF2TX5MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF2TX5MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF2TX6MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF2TX6MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF2TX6MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF2TX6MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF2TX6MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF2TX6MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF2TX6MIX_INPUT_4_VOLUME:
case ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE:
case ARIZONA_AIF3TX1MIX_INPUT_1_VOLUME:
case ARIZONA_AIF3TX1MIX_INPUT_2_SOURCE:
@@ -2272,21 +2466,125 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP1_CLOCKING_1:
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
+ case ARIZONA_DSP1_STATUS_3:
+ case ARIZONA_DSP1_STATUS_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_1:
+ case ARIZONA_DSP1_WDMA_BUFFER_2:
+ case ARIZONA_DSP1_WDMA_BUFFER_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_5:
+ case ARIZONA_DSP1_WDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_BUFFER_7:
+ case ARIZONA_DSP1_WDMA_BUFFER_8:
+ case ARIZONA_DSP1_RDMA_BUFFER_1:
+ case ARIZONA_DSP1_RDMA_BUFFER_2:
+ case ARIZONA_DSP1_RDMA_BUFFER_3:
+ case ARIZONA_DSP1_RDMA_BUFFER_4:
+ case ARIZONA_DSP1_RDMA_BUFFER_5:
+ case ARIZONA_DSP1_RDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_CONFIG_1:
+ case ARIZONA_DSP1_WDMA_CONFIG_2:
+ case ARIZONA_DSP1_WDMA_OFFSET_1:
+ case ARIZONA_DSP1_RDMA_CONFIG_1:
+ case ARIZONA_DSP1_RDMA_OFFSET_1:
+ case ARIZONA_DSP1_EXTERNAL_START_SELECT_1:
+ case ARIZONA_DSP1_SCRATCH_0:
+ case ARIZONA_DSP1_SCRATCH_1:
+ case ARIZONA_DSP1_SCRATCH_2:
+ case ARIZONA_DSP1_SCRATCH_3:
case ARIZONA_DSP2_CONTROL_1:
case ARIZONA_DSP2_CLOCKING_1:
case ARIZONA_DSP2_STATUS_1:
case ARIZONA_DSP2_STATUS_2:
+ case ARIZONA_DSP2_STATUS_3:
+ case ARIZONA_DSP2_STATUS_4:
+ case ARIZONA_DSP2_WDMA_BUFFER_1:
+ case ARIZONA_DSP2_WDMA_BUFFER_2:
+ case ARIZONA_DSP2_WDMA_BUFFER_3:
+ case ARIZONA_DSP2_WDMA_BUFFER_4:
+ case ARIZONA_DSP2_WDMA_BUFFER_5:
+ case ARIZONA_DSP2_WDMA_BUFFER_6:
+ case ARIZONA_DSP2_WDMA_BUFFER_7:
+ case ARIZONA_DSP2_WDMA_BUFFER_8:
+ case ARIZONA_DSP2_RDMA_BUFFER_1:
+ case ARIZONA_DSP2_RDMA_BUFFER_2:
+ case ARIZONA_DSP2_RDMA_BUFFER_3:
+ case ARIZONA_DSP2_RDMA_BUFFER_4:
+ case ARIZONA_DSP2_RDMA_BUFFER_5:
+ case ARIZONA_DSP2_RDMA_BUFFER_6:
+ case ARIZONA_DSP2_WDMA_CONFIG_1:
+ case ARIZONA_DSP2_WDMA_CONFIG_2:
+ case ARIZONA_DSP2_WDMA_OFFSET_1:
+ case ARIZONA_DSP2_RDMA_CONFIG_1:
+ case ARIZONA_DSP2_RDMA_OFFSET_1:
+ case ARIZONA_DSP2_EXTERNAL_START_SELECT_1:
+ case ARIZONA_DSP2_SCRATCH_0:
+ case ARIZONA_DSP2_SCRATCH_1:
+ case ARIZONA_DSP2_SCRATCH_2:
+ case ARIZONA_DSP2_SCRATCH_3:
case ARIZONA_DSP3_CONTROL_1:
case ARIZONA_DSP3_CLOCKING_1:
case ARIZONA_DSP3_STATUS_1:
case ARIZONA_DSP3_STATUS_2:
+ case ARIZONA_DSP3_STATUS_3:
+ case ARIZONA_DSP3_STATUS_4:
+ case ARIZONA_DSP3_WDMA_BUFFER_1:
+ case ARIZONA_DSP3_WDMA_BUFFER_2:
+ case ARIZONA_DSP3_WDMA_BUFFER_3:
+ case ARIZONA_DSP3_WDMA_BUFFER_4:
+ case ARIZONA_DSP3_WDMA_BUFFER_5:
+ case ARIZONA_DSP3_WDMA_BUFFER_6:
+ case ARIZONA_DSP3_WDMA_BUFFER_7:
+ case ARIZONA_DSP3_WDMA_BUFFER_8:
+ case ARIZONA_DSP3_RDMA_BUFFER_1:
+ case ARIZONA_DSP3_RDMA_BUFFER_2:
+ case ARIZONA_DSP3_RDMA_BUFFER_3:
+ case ARIZONA_DSP3_RDMA_BUFFER_4:
+ case ARIZONA_DSP3_RDMA_BUFFER_5:
+ case ARIZONA_DSP3_RDMA_BUFFER_6:
+ case ARIZONA_DSP3_WDMA_CONFIG_1:
+ case ARIZONA_DSP3_WDMA_CONFIG_2:
+ case ARIZONA_DSP3_WDMA_OFFSET_1:
+ case ARIZONA_DSP3_RDMA_CONFIG_1:
+ case ARIZONA_DSP3_RDMA_OFFSET_1:
+ case ARIZONA_DSP3_EXTERNAL_START_SELECT_1:
+ case ARIZONA_DSP3_SCRATCH_0:
+ case ARIZONA_DSP3_SCRATCH_1:
+ case ARIZONA_DSP3_SCRATCH_2:
+ case ARIZONA_DSP3_SCRATCH_3:
case ARIZONA_DSP4_CONTROL_1:
case ARIZONA_DSP4_CLOCKING_1:
case ARIZONA_DSP4_STATUS_1:
case ARIZONA_DSP4_STATUS_2:
+ case ARIZONA_DSP4_STATUS_3:
+ case ARIZONA_DSP4_STATUS_4:
+ case ARIZONA_DSP4_WDMA_BUFFER_1:
+ case ARIZONA_DSP4_WDMA_BUFFER_2:
+ case ARIZONA_DSP4_WDMA_BUFFER_3:
+ case ARIZONA_DSP4_WDMA_BUFFER_4:
+ case ARIZONA_DSP4_WDMA_BUFFER_5:
+ case ARIZONA_DSP4_WDMA_BUFFER_6:
+ case ARIZONA_DSP4_WDMA_BUFFER_7:
+ case ARIZONA_DSP4_WDMA_BUFFER_8:
+ case ARIZONA_DSP4_RDMA_BUFFER_1:
+ case ARIZONA_DSP4_RDMA_BUFFER_2:
+ case ARIZONA_DSP4_RDMA_BUFFER_3:
+ case ARIZONA_DSP4_RDMA_BUFFER_4:
+ case ARIZONA_DSP4_RDMA_BUFFER_5:
+ case ARIZONA_DSP4_RDMA_BUFFER_6:
+ case ARIZONA_DSP4_WDMA_CONFIG_1:
+ case ARIZONA_DSP4_WDMA_CONFIG_2:
+ case ARIZONA_DSP4_WDMA_OFFSET_1:
+ case ARIZONA_DSP4_RDMA_CONFIG_1:
+ case ARIZONA_DSP4_RDMA_OFFSET_1:
+ case ARIZONA_DSP4_EXTERNAL_START_SELECT_1:
+ case ARIZONA_DSP4_SCRATCH_0:
+ case ARIZONA_DSP4_SCRATCH_1:
+ case ARIZONA_DSP4_SCRATCH_2:
+ case ARIZONA_DSP4_SCRATCH_3:
return true;
default:
- return false;
+ return wm5110_is_adsp_memory(dev, reg);
}
}
@@ -2325,32 +2623,143 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg)
case ARIZONA_INTERRUPT_RAW_STATUS_7:
case ARIZONA_INTERRUPT_RAW_STATUS_8:
case ARIZONA_IRQ_PIN_STATUS:
+ case ARIZONA_AOD_WKUP_AND_TRIG:
case ARIZONA_AOD_IRQ1:
case ARIZONA_AOD_IRQ2:
+ case ARIZONA_AOD_IRQ_RAW_STATUS:
+ case ARIZONA_FX_CTRL2:
case ARIZONA_ASRC_STATUS:
case ARIZONA_DSP_STATUS:
- case ARIZONA_DSP1_CONTROL_1:
- case ARIZONA_DSP1_CLOCKING_1:
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
+ case ARIZONA_DSP1_STATUS_3:
+ case ARIZONA_DSP1_STATUS_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_1:
+ case ARIZONA_DSP1_WDMA_BUFFER_2:
+ case ARIZONA_DSP1_WDMA_BUFFER_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_5:
+ case ARIZONA_DSP1_WDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_BUFFER_7:
+ case ARIZONA_DSP1_WDMA_BUFFER_8:
+ case ARIZONA_DSP1_RDMA_BUFFER_1:
+ case ARIZONA_DSP1_RDMA_BUFFER_2:
+ case ARIZONA_DSP1_RDMA_BUFFER_3:
+ case ARIZONA_DSP1_RDMA_BUFFER_4:
+ case ARIZONA_DSP1_RDMA_BUFFER_5:
+ case ARIZONA_DSP1_RDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_CONFIG_1:
+ case ARIZONA_DSP1_WDMA_CONFIG_2:
+ case ARIZONA_DSP1_WDMA_OFFSET_1:
+ case ARIZONA_DSP1_RDMA_CONFIG_1:
+ case ARIZONA_DSP1_RDMA_OFFSET_1:
+ case ARIZONA_DSP1_EXTERNAL_START_SELECT_1:
+ case ARIZONA_DSP1_SCRATCH_0:
+ case ARIZONA_DSP1_SCRATCH_1:
+ case ARIZONA_DSP1_SCRATCH_2:
+ case ARIZONA_DSP1_SCRATCH_3:
+ case ARIZONA_DSP1_CLOCKING_1:
case ARIZONA_DSP2_STATUS_1:
case ARIZONA_DSP2_STATUS_2:
+ case ARIZONA_DSP2_STATUS_3:
+ case ARIZONA_DSP2_STATUS_4:
+ case ARIZONA_DSP2_WDMA_BUFFER_1:
+ case ARIZONA_DSP2_WDMA_BUFFER_2:
+ case ARIZONA_DSP2_WDMA_BUFFER_3:
+ case ARIZONA_DSP2_WDMA_BUFFER_4:
+ case ARIZONA_DSP2_WDMA_BUFFER_5:
+ case ARIZONA_DSP2_WDMA_BUFFER_6:
+ case ARIZONA_DSP2_WDMA_BUFFER_7:
+ case ARIZONA_DSP2_WDMA_BUFFER_8:
+ case ARIZONA_DSP2_RDMA_BUFFER_1:
+ case ARIZONA_DSP2_RDMA_BUFFER_2:
+ case ARIZONA_DSP2_RDMA_BUFFER_3:
+ case ARIZONA_DSP2_RDMA_BUFFER_4:
+ case ARIZONA_DSP2_RDMA_BUFFER_5:
+ case ARIZONA_DSP2_RDMA_BUFFER_6:
+ case ARIZONA_DSP2_WDMA_CONFIG_1:
+ case ARIZONA_DSP2_WDMA_CONFIG_2:
+ case ARIZONA_DSP2_WDMA_OFFSET_1:
+ case ARIZONA_DSP2_RDMA_CONFIG_1:
+ case ARIZONA_DSP2_RDMA_OFFSET_1:
+ case ARIZONA_DSP2_EXTERNAL_START_SELECT_1:
+ case ARIZONA_DSP2_SCRATCH_0:
+ case ARIZONA_DSP2_SCRATCH_1:
+ case ARIZONA_DSP2_SCRATCH_2:
+ case ARIZONA_DSP2_SCRATCH_3:
+ case ARIZONA_DSP2_CLOCKING_1:
case ARIZONA_DSP3_STATUS_1:
case ARIZONA_DSP3_STATUS_2:
+ case ARIZONA_DSP3_STATUS_3:
+ case ARIZONA_DSP3_STATUS_4:
+ case ARIZONA_DSP3_WDMA_BUFFER_1:
+ case ARIZONA_DSP3_WDMA_BUFFER_2:
+ case ARIZONA_DSP3_WDMA_BUFFER_3:
+ case ARIZONA_DSP3_WDMA_BUFFER_4:
+ case ARIZONA_DSP3_WDMA_BUFFER_5:
+ case ARIZONA_DSP3_WDMA_BUFFER_6:
+ case ARIZONA_DSP3_WDMA_BUFFER_7:
+ case ARIZONA_DSP3_WDMA_BUFFER_8:
+ case ARIZONA_DSP3_RDMA_BUFFER_1:
+ case ARIZONA_DSP3_RDMA_BUFFER_2:
+ case ARIZONA_DSP3_RDMA_BUFFER_3:
+ case ARIZONA_DSP3_RDMA_BUFFER_4:
+ case ARIZONA_DSP3_RDMA_BUFFER_5:
+ case ARIZONA_DSP3_RDMA_BUFFER_6:
+ case ARIZONA_DSP3_WDMA_CONFIG_1:
+ case ARIZONA_DSP3_WDMA_CONFIG_2:
+ case ARIZONA_DSP3_WDMA_OFFSET_1:
+ case ARIZONA_DSP3_RDMA_CONFIG_1:
+ case ARIZONA_DSP3_RDMA_OFFSET_1:
+ case ARIZONA_DSP3_EXTERNAL_START_SELECT_1:
+ case ARIZONA_DSP3_SCRATCH_0:
+ case ARIZONA_DSP3_SCRATCH_1:
+ case ARIZONA_DSP3_SCRATCH_2:
+ case ARIZONA_DSP3_SCRATCH_3:
+ case ARIZONA_DSP3_CLOCKING_1:
case ARIZONA_DSP4_STATUS_1:
case ARIZONA_DSP4_STATUS_2:
+ case ARIZONA_DSP4_STATUS_3:
+ case ARIZONA_DSP4_STATUS_4:
+ case ARIZONA_DSP4_WDMA_BUFFER_1:
+ case ARIZONA_DSP4_WDMA_BUFFER_2:
+ case ARIZONA_DSP4_WDMA_BUFFER_3:
+ case ARIZONA_DSP4_WDMA_BUFFER_4:
+ case ARIZONA_DSP4_WDMA_BUFFER_5:
+ case ARIZONA_DSP4_WDMA_BUFFER_6:
+ case ARIZONA_DSP4_WDMA_BUFFER_7:
+ case ARIZONA_DSP4_WDMA_BUFFER_8:
+ case ARIZONA_DSP4_RDMA_BUFFER_1:
+ case ARIZONA_DSP4_RDMA_BUFFER_2:
+ case ARIZONA_DSP4_RDMA_BUFFER_3:
+ case ARIZONA_DSP4_RDMA_BUFFER_4:
+ case ARIZONA_DSP4_RDMA_BUFFER_5:
+ case ARIZONA_DSP4_RDMA_BUFFER_6:
+ case ARIZONA_DSP4_WDMA_CONFIG_1:
+ case ARIZONA_DSP4_WDMA_CONFIG_2:
+ case ARIZONA_DSP4_WDMA_OFFSET_1:
+ case ARIZONA_DSP4_RDMA_CONFIG_1:
+ case ARIZONA_DSP4_RDMA_OFFSET_1:
+ case ARIZONA_DSP4_EXTERNAL_START_SELECT_1:
+ case ARIZONA_DSP4_SCRATCH_0:
+ case ARIZONA_DSP4_SCRATCH_1:
+ case ARIZONA_DSP4_SCRATCH_2:
+ case ARIZONA_DSP4_SCRATCH_3:
+ case ARIZONA_DSP4_CLOCKING_1:
return true;
default:
- return false;
+ return wm5110_is_adsp_memory(dev, reg);
}
}
+#define WM5110_MAX_REGISTER 0x4a9fff
+
const struct regmap_config wm5110_spi_regmap = {
.reg_bits = 32,
.pad_bits = 16,
.val_bits = 16,
- .max_register = ARIZONA_DSP1_STATUS_2,
+ .max_register = WM5110_MAX_REGISTER,
.readable_reg = wm5110_readable_register,
.volatile_reg = wm5110_volatile_register,
@@ -2364,7 +2773,7 @@ const struct regmap_config wm5110_i2c_regmap = {
.reg_bits = 32,
.val_bits = 16,
- .max_register = ARIZONA_DSP1_STATUS_2,
+ .max_register = WM5110_MAX_REGISTER,
.readable_reg = wm5110_readable_register,
.volatile_reg = wm5110_volatile_register,
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c
index 521340a708d..28366a90e1a 100644
--- a/drivers/mfd/wm831x-core.c
+++ b/drivers/mfd/wm831x-core.c
@@ -1011,7 +1011,7 @@ static struct resource wm831x_wdt_resources[] = {
},
};
-static struct mfd_cell wm8310_devs[] = {
+static const struct mfd_cell wm8310_devs[] = {
{
.name = "wm831x-backup",
},
@@ -1165,7 +1165,7 @@ static struct mfd_cell wm8310_devs[] = {
},
};
-static struct mfd_cell wm8311_devs[] = {
+static const struct mfd_cell wm8311_devs[] = {
{
.name = "wm831x-backup",
},
@@ -1295,7 +1295,7 @@ static struct mfd_cell wm8311_devs[] = {
},
};
-static struct mfd_cell wm8312_devs[] = {
+static const struct mfd_cell wm8312_devs[] = {
{
.name = "wm831x-backup",
},
@@ -1449,7 +1449,7 @@ static struct mfd_cell wm8312_devs[] = {
},
};
-static struct mfd_cell wm8320_devs[] = {
+static const struct mfd_cell wm8320_devs[] = {
{
.name = "wm831x-backup",
},
@@ -1578,7 +1578,7 @@ static struct mfd_cell wm8320_devs[] = {
},
};
-static struct mfd_cell touch_devs[] = {
+static const struct mfd_cell touch_devs[] = {
{
.name = "wm831x-touch",
.num_resources = ARRAY_SIZE(wm831x_touch_resources),
@@ -1586,7 +1586,7 @@ static struct mfd_cell touch_devs[] = {
},
};
-static struct mfd_cell rtc_devs[] = {
+static const struct mfd_cell rtc_devs[] = {
{
.name = "wm831x-rtc",
.num_resources = ARRAY_SIZE(wm831x_rtc_resources),
@@ -1594,7 +1594,7 @@ static struct mfd_cell rtc_devs[] = {
},
};
-static struct mfd_cell backlight_devs[] = {
+static const struct mfd_cell backlight_devs[] = {
{
.name = "wm831x-backlight",
},
@@ -1618,7 +1618,7 @@ EXPORT_SYMBOL_GPL(wm831x_regmap_config);
*/
int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
{
- struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
int rev, wm831x_num;
enum wm831x_parent parent;
int ret, i;
diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c
index 2b29caebc9c..a4cbefe5430 100644
--- a/drivers/mfd/wm831x-i2c.c
+++ b/drivers/mfd/wm831x-i2c.c
@@ -64,11 +64,13 @@ static int wm831x_i2c_suspend(struct device *dev)
return wm831x_device_suspend(wm831x);
}
-static void wm831x_i2c_shutdown(struct i2c_client *i2c)
+static int wm831x_i2c_poweroff(struct device *dev)
{
- struct wm831x *wm831x = i2c_get_clientdata(i2c);
+ struct wm831x *wm831x = dev_get_drvdata(dev);
wm831x_device_shutdown(wm831x);
+
+ return 0;
}
static const struct i2c_device_id wm831x_i2c_id[] = {
@@ -85,6 +87,7 @@ MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
static const struct dev_pm_ops wm831x_pm_ops = {
.suspend = wm831x_i2c_suspend,
+ .poweroff = wm831x_i2c_poweroff,
};
static struct i2c_driver wm831x_i2c_driver = {
@@ -95,7 +98,6 @@ static struct i2c_driver wm831x_i2c_driver = {
},
.probe = wm831x_i2c_probe,
.remove = wm831x_i2c_remove,
- .shutdown = wm831x_i2c_shutdown,
.id_table = wm831x_i2c_id,
};
diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c
index 804e56ec99e..64e512eadf1 100644
--- a/drivers/mfd/wm831x-irq.c
+++ b/drivers/mfd/wm831x-irq.c
@@ -571,7 +571,7 @@ static struct irq_domain_ops wm831x_irq_domain_ops = {
int wm831x_irq_init(struct wm831x *wm831x, int irq)
{
- struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
struct irq_domain *domain;
int i, ret, irq_base;
diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c
index 4bceee98f0a..b8a5e3b34ec 100644
--- a/drivers/mfd/wm831x-spi.c
+++ b/drivers/mfd/wm831x-spi.c
@@ -21,7 +21,7 @@
#include <linux/mfd/wm831x/core.h>
-static int __devinit wm831x_spi_probe(struct spi_device *spi)
+static int wm831x_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct wm831x *wm831x;
@@ -34,10 +34,9 @@ static int __devinit wm831x_spi_probe(struct spi_device *spi)
if (wm831x == NULL)
return -ENOMEM;
- spi->bits_per_word = 16;
spi->mode = SPI_MODE_0;
- dev_set_drvdata(&spi->dev, wm831x);
+ spi_set_drvdata(spi, wm831x);
wm831x->dev = &spi->dev;
wm831x->regmap = devm_regmap_init_spi(spi, &wm831x_regmap_config);
@@ -51,9 +50,9 @@ static int __devinit wm831x_spi_probe(struct spi_device *spi)
return wm831x_device_init(wm831x, type, spi->irq);
}
-static int __devexit wm831x_spi_remove(struct spi_device *spi)
+static int wm831x_spi_remove(struct spi_device *spi)
{
- struct wm831x *wm831x = dev_get_drvdata(&spi->dev);
+ struct wm831x *wm831x = spi_get_drvdata(spi);
wm831x_device_exit(wm831x);
@@ -67,16 +66,19 @@ static int wm831x_spi_suspend(struct device *dev)
return wm831x_device_suspend(wm831x);
}
-static void wm831x_spi_shutdown(struct spi_device *spi)
+static int wm831x_spi_poweroff(struct device *dev)
{
- struct wm831x *wm831x = dev_get_drvdata(&spi->dev);
+ struct wm831x *wm831x = dev_get_drvdata(dev);
wm831x_device_shutdown(wm831x);
+
+ return 0;
}
static const struct dev_pm_ops wm831x_spi_pm = {
.freeze = wm831x_spi_suspend,
.suspend = wm831x_spi_suspend,
+ .poweroff = wm831x_spi_poweroff,
};
static const struct spi_device_id wm831x_spi_ids[] = {
@@ -99,8 +101,7 @@ static struct spi_driver wm831x_spi_driver = {
},
.id_table = wm831x_spi_ids,
.probe = wm831x_spi_probe,
- .remove = __devexit_p(wm831x_spi_remove),
- .shutdown = wm831x_spi_shutdown,
+ .remove = wm831x_spi_remove,
};
static int __init wm831x_spi_init(void)
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index 7c1ae24605d..4ab527f5c53 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -14,7 +14,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/bug.h>
#include <linux/device.h>
diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c
index 2e57101c8d3..f919def05e2 100644
--- a/drivers/mfd/wm8350-i2c.c
+++ b/drivers/mfd/wm8350-i2c.c
@@ -27,6 +27,7 @@ static int wm8350_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8350 *wm8350;
+ struct wm8350_platform_data *pdata = dev_get_platdata(&i2c->dev);
int ret = 0;
wm8350 = devm_kzalloc(&i2c->dev, sizeof(struct wm8350), GFP_KERNEL);
@@ -44,7 +45,7 @@ static int wm8350_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8350);
wm8350->dev = &i2c->dev;
- return wm8350_device_init(wm8350, i2c->irq, i2c->dev.platform_data);
+ return wm8350_device_init(wm8350, i2c->irq, pdata);
}
static int wm8350_i2c_remove(struct i2c_client *i2c)
diff --git a/drivers/mfd/wm8350-irq.c b/drivers/mfd/wm8350-irq.c
index 624ff90501c..cd01f7962df 100644
--- a/drivers/mfd/wm8350-irq.c
+++ b/drivers/mfd/wm8350-irq.c
@@ -14,7 +14,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/interrupt.h>
diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c
index 639ca359242..c6fb5d16ca0 100644
--- a/drivers/mfd/wm8400-core.c
+++ b/drivers/mfd/wm8400-core.c
@@ -64,7 +64,7 @@ EXPORT_SYMBOL_GPL(wm8400_block_read);
static int wm8400_register_codec(struct wm8400 *wm8400)
{
- struct mfd_cell cell = {
+ const struct mfd_cell cell = {
.name = "wm8400-codec",
.platform_data = wm8400,
.pdata_size = sizeof(*wm8400),
@@ -161,31 +161,19 @@ static int wm8400_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8400 *wm8400;
- int ret;
wm8400 = devm_kzalloc(&i2c->dev, sizeof(struct wm8400), GFP_KERNEL);
- if (wm8400 == NULL) {
- ret = -ENOMEM;
- goto err;
- }
+ if (!wm8400)
+ return -ENOMEM;
wm8400->regmap = devm_regmap_init_i2c(i2c, &wm8400_regmap_config);
- if (IS_ERR(wm8400->regmap)) {
- ret = PTR_ERR(wm8400->regmap);
- goto err;
- }
+ if (IS_ERR(wm8400->regmap))
+ return PTR_ERR(wm8400->regmap);
wm8400->dev = &i2c->dev;
i2c_set_clientdata(i2c, wm8400);
- ret = wm8400_init(wm8400, i2c->dev.platform_data);
- if (ret != 0)
- goto err;
-
- return 0;
-
-err:
- return ret;
+ return wm8400_init(wm8400, dev_get_platdata(&i2c->dev));
}
static int wm8400_i2c_remove(struct i2c_client *i2c)
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index 8fefc961ec0..e6fab94e2c8 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -19,6 +19,9 @@
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/mfd/core.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -30,85 +33,7 @@
#include "wm8994.h"
-/**
- * wm8994_reg_read: Read a single WM8994 register.
- *
- * @wm8994: Device to read from.
- * @reg: Register to read.
- */
-int wm8994_reg_read(struct wm8994 *wm8994, unsigned short reg)
-{
- unsigned int val;
- int ret;
-
- ret = regmap_read(wm8994->regmap, reg, &val);
-
- if (ret < 0)
- return ret;
- else
- return val;
-}
-EXPORT_SYMBOL_GPL(wm8994_reg_read);
-
-/**
- * wm8994_bulk_read: Read multiple WM8994 registers
- *
- * @wm8994: Device to read from
- * @reg: First register
- * @count: Number of registers
- * @buf: Buffer to fill. The data will be returned big endian.
- */
-int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg,
- int count, u16 *buf)
-{
- return regmap_bulk_read(wm8994->regmap, reg, buf, count);
-}
-
-/**
- * wm8994_reg_write: Write a single WM8994 register.
- *
- * @wm8994: Device to write to.
- * @reg: Register to write to.
- * @val: Value to write.
- */
-int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg,
- unsigned short val)
-{
- return regmap_write(wm8994->regmap, reg, val);
-}
-EXPORT_SYMBOL_GPL(wm8994_reg_write);
-
-/**
- * wm8994_bulk_write: Write multiple WM8994 registers
- *
- * @wm8994: Device to write to
- * @reg: First register
- * @count: Number of registers
- * @buf: Buffer to write from. Data must be big-endian formatted.
- */
-int wm8994_bulk_write(struct wm8994 *wm8994, unsigned short reg,
- int count, const u16 *buf)
-{
- return regmap_raw_write(wm8994->regmap, reg, buf, count * sizeof(u16));
-}
-EXPORT_SYMBOL_GPL(wm8994_bulk_write);
-
-/**
- * wm8994_set_bits: Set the value of a bitfield in a WM8994 register
- *
- * @wm8994: Device to write to.
- * @reg: Register to write to.
- * @mask: Mask of bits to set.
- * @val: Value to set (unshifted)
- */
-int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg,
- unsigned short mask, unsigned short val)
-{
- return regmap_update_bits(wm8994->regmap, reg, mask, val);
-}
-EXPORT_SYMBOL_GPL(wm8994_set_bits);
-
-static struct mfd_cell wm8994_regulator_devs[] = {
+static const struct mfd_cell wm8994_regulator_devs[] = {
{
.name = "wm8994-ldo",
.id = 1,
@@ -137,7 +62,7 @@ static struct resource wm8994_gpio_resources[] = {
},
};
-static struct mfd_cell wm8994_devs[] = {
+static const struct mfd_cell wm8994_devs[] = {
{
.name = "wm8994-codec",
.num_resources = ARRAY_SIZE(wm8994_codec_resources),
@@ -191,42 +116,14 @@ static const char *wm8958_main_supplies[] = {
"SPKVDD2",
};
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_RUNTIME
static int wm8994_suspend(struct device *dev)
{
struct wm8994 *wm8994 = dev_get_drvdata(dev);
int ret;
/* Don't actually go through with the suspend if the CODEC is
- * still active (eg, for audio passthrough from CP. */
- ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_1);
- if (ret < 0) {
- dev_err(dev, "Failed to read power status: %d\n", ret);
- } else if (ret & WM8994_VMID_SEL_MASK) {
- dev_dbg(dev, "CODEC still active, ignoring suspend\n");
- return 0;
- }
-
- ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_4);
- if (ret < 0) {
- dev_err(dev, "Failed to read power status: %d\n", ret);
- } else if (ret & (WM8994_AIF2ADCL_ENA | WM8994_AIF2ADCR_ENA |
- WM8994_AIF1ADC2L_ENA | WM8994_AIF1ADC2R_ENA |
- WM8994_AIF1ADC1L_ENA | WM8994_AIF1ADC1R_ENA)) {
- dev_dbg(dev, "CODEC still active, ignoring suspend\n");
- return 0;
- }
-
- ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_5);
- if (ret < 0) {
- dev_err(dev, "Failed to read power status: %d\n", ret);
- } else if (ret & (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA |
- WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA |
- WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA)) {
- dev_dbg(dev, "CODEC still active, ignoring suspend\n");
- return 0;
- }
-
+ * still active for accessory detect. */
switch (wm8994->type) {
case WM8958:
case WM1811:
@@ -242,34 +139,6 @@ static int wm8994_suspend(struct device *dev)
break;
}
- switch (wm8994->type) {
- case WM1811:
- ret = wm8994_reg_read(wm8994, WM8994_ANTIPOP_2);
- if (ret < 0) {
- dev_err(dev, "Failed to read jackdet: %d\n", ret);
- } else if (ret & WM1811_JACKDET_MODE_MASK) {
- dev_dbg(dev, "CODEC still active, ignoring suspend\n");
- return 0;
- }
- break;
- default:
- break;
- }
-
- switch (wm8994->type) {
- case WM1811:
- ret = wm8994_reg_read(wm8994, WM8994_ANTIPOP_2);
- if (ret < 0) {
- dev_err(dev, "Failed to read jackdet: %d\n", ret);
- } else if (ret & WM1811_JACKDET_MODE_MASK) {
- dev_dbg(dev, "CODEC still active, ignoring suspend\n");
- return 0;
- }
- break;
- default:
- break;
- }
-
/* Disable LDO pulldowns while the device is suspended if we
* don't know that something will be driving them. */
if (!wm8994->ldo_ena_always_driven)
@@ -374,21 +243,21 @@ static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo)
}
#endif
-static const __devinitconst struct reg_default wm8994_revc_patch[] = {
+static const struct reg_default wm8994_revc_patch[] = {
{ 0x102, 0x3 },
{ 0x56, 0x3 },
{ 0x817, 0x0 },
{ 0x102, 0x0 },
};
-static const __devinitconst struct reg_default wm8958_reva_patch[] = {
+static const struct reg_default wm8958_reva_patch[] = {
{ 0x102, 0x3 },
{ 0xcb, 0x81 },
{ 0x817, 0x0 },
{ 0x102, 0x0 },
};
-static const __devinitconst struct reg_default wm1811_reva_patch[] = {
+static const struct reg_default wm1811_reva_patch[] = {
{ 0x102, 0x3 },
{ 0x56, 0xc07 },
{ 0x5d, 0x7e },
@@ -396,18 +265,82 @@ static const __devinitconst struct reg_default wm1811_reva_patch[] = {
{ 0x102, 0x0 },
};
+#ifdef CONFIG_OF
+static int wm8994_set_pdata_from_of(struct wm8994 *wm8994)
+{
+ struct device_node *np = wm8994->dev->of_node;
+ struct wm8994_pdata *pdata = &wm8994->pdata;
+ int i;
+
+ if (!np)
+ return 0;
+
+ if (of_property_read_u32_array(np, "wlf,gpio-cfg", pdata->gpio_defaults,
+ ARRAY_SIZE(pdata->gpio_defaults)) >= 0) {
+ for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) {
+ if (wm8994->pdata.gpio_defaults[i] == 0)
+ pdata->gpio_defaults[i]
+ = WM8994_CONFIGURE_GPIO;
+ }
+ }
+
+ of_property_read_u32_array(np, "wlf,micbias-cfg", pdata->micbias,
+ ARRAY_SIZE(pdata->micbias));
+
+ pdata->lineout1_diff = true;
+ pdata->lineout2_diff = true;
+ if (of_find_property(np, "wlf,lineout1-se", NULL))
+ pdata->lineout1_diff = false;
+ if (of_find_property(np, "wlf,lineout2-se", NULL))
+ pdata->lineout2_diff = false;
+
+ if (of_find_property(np, "wlf,lineout1-feedback", NULL))
+ pdata->lineout1fb = true;
+ if (of_find_property(np, "wlf,lineout2-feedback", NULL))
+ pdata->lineout2fb = true;
+
+ if (of_find_property(np, "wlf,ldoena-always-driven", NULL))
+ pdata->lineout2fb = true;
+
+ pdata->ldo[0].enable = of_get_named_gpio(np, "wlf,ldo1ena", 0);
+ if (pdata->ldo[0].enable < 0)
+ pdata->ldo[0].enable = 0;
+
+ pdata->ldo[1].enable = of_get_named_gpio(np, "wlf,ldo2ena", 0);
+ if (pdata->ldo[1].enable < 0)
+ pdata->ldo[1].enable = 0;
+
+ return 0;
+}
+#else
+static int wm8994_set_pdata_from_of(struct wm8994 *wm8994)
+{
+ return 0;
+}
+#endif
+
/*
* Instantiate the generic non-control parts of the device.
*/
-static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
+static int wm8994_device_init(struct wm8994 *wm8994, int irq)
{
- struct wm8994_pdata *pdata = wm8994->dev->platform_data;
+ struct wm8994_pdata *pdata;
struct regmap_config *regmap_config;
const struct reg_default *regmap_patch = NULL;
const char *devname;
- int ret, i, patch_regs;
+ int ret, i, patch_regs = 0;
int pulls = 0;
+ if (dev_get_platdata(wm8994->dev)) {
+ pdata = dev_get_platdata(wm8994->dev);
+ wm8994->pdata = *pdata;
+ }
+ pdata = &wm8994->pdata;
+
+ ret = wm8994_set_pdata_from_of(wm8994);
+ if (ret != 0)
+ return ret;
+
dev_set_drvdata(wm8994->dev, wm8994);
/* Add the on-chip regulators first for bootstrapping */
@@ -461,7 +394,7 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
goto err;
}
- ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
+ ret = devm_regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
wm8994->supplies);
if (ret != 0) {
dev_err(wm8994->dev, "Failed to get supplies: %d\n", ret);
@@ -472,7 +405,7 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
wm8994->supplies);
if (ret != 0) {
dev_err(wm8994->dev, "Failed to enable supplies: %d\n", ret);
- goto err_get;
+ goto err;
}
ret = wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET);
@@ -529,11 +462,10 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
break;
case 2:
case 3:
+ default:
regmap_patch = wm8994_revc_patch;
patch_regs = ARRAY_SIZE(wm8994_revc_patch);
break;
- default:
- break;
}
break;
@@ -552,17 +484,9 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
/* Revision C did not change the relevant layer */
if (wm8994->revision > 1)
wm8994->revision++;
- switch (wm8994->revision) {
- case 0:
- case 1:
- case 2:
- case 3:
- regmap_patch = wm1811_reva_patch;
- patch_regs = ARRAY_SIZE(wm1811_reva_patch);
- break;
- default:
- break;
- }
+
+ regmap_patch = wm1811_reva_patch;
+ patch_regs = ARRAY_SIZE(wm1811_reva_patch);
break;
default:
@@ -594,6 +518,17 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
return ret;
}
+ /* Explicitly put the device into reset in case regulators
+ * don't get disabled in order to ensure we know the device
+ * state.
+ */
+ ret = wm8994_reg_write(wm8994, WM8994_SOFTWARE_RESET,
+ wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET));
+ if (ret != 0) {
+ dev_err(wm8994->dev, "Failed to reset device: %d\n", ret);
+ return ret;
+ }
+
if (regmap_patch) {
ret = regmap_register_patch(wm8994->regmap, regmap_patch,
patch_regs);
@@ -604,24 +539,21 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
}
}
- if (pdata) {
- wm8994->irq_base = pdata->irq_base;
- wm8994->gpio_base = pdata->gpio_base;
+ wm8994->irq_base = pdata->irq_base;
+ wm8994->gpio_base = pdata->gpio_base;
- /* GPIO configuration is only applied if it's non-zero */
- for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) {
- if (pdata->gpio_defaults[i]) {
- wm8994_set_bits(wm8994, WM8994_GPIO_1 + i,
- 0xffff,
- pdata->gpio_defaults[i]);
- }
+ /* GPIO configuration is only applied if it's non-zero */
+ for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) {
+ if (pdata->gpio_defaults[i]) {
+ wm8994_set_bits(wm8994, WM8994_GPIO_1 + i,
+ 0xffff, pdata->gpio_defaults[i]);
}
+ }
- wm8994->ldo_ena_always_driven = pdata->ldo_ena_always_driven;
+ wm8994->ldo_ena_always_driven = pdata->ldo_ena_always_driven;
- if (pdata->spkmode_pu)
- pulls |= WM8994_SPKMODE_PU;
- }
+ if (pdata->spkmode_pu)
+ pulls |= WM8994_SPKMODE_PU;
/* Disable unneeded pulls */
wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
@@ -664,34 +596,32 @@ err_irq:
err_enable:
regulator_bulk_disable(wm8994->num_supplies,
wm8994->supplies);
-err_get:
- regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
err:
mfd_remove_devices(wm8994->dev);
return ret;
}
-static __devexit void wm8994_device_exit(struct wm8994 *wm8994)
+static void wm8994_device_exit(struct wm8994 *wm8994)
{
pm_runtime_disable(wm8994->dev);
mfd_remove_devices(wm8994->dev);
wm8994_irq_exit(wm8994);
regulator_bulk_disable(wm8994->num_supplies,
wm8994->supplies);
- regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
}
static const struct of_device_id wm8994_of_match[] = {
- { .compatible = "wlf,wm1811", },
- { .compatible = "wlf,wm8994", },
- { .compatible = "wlf,wm8958", },
+ { .compatible = "wlf,wm1811", .data = (void *)WM1811 },
+ { .compatible = "wlf,wm8994", .data = (void *)WM8994 },
+ { .compatible = "wlf,wm8958", .data = (void *)WM8958 },
{ }
};
MODULE_DEVICE_TABLE(of, wm8994_of_match);
-static __devinit int wm8994_i2c_probe(struct i2c_client *i2c,
+static int wm8994_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
+ const struct of_device_id *of_id;
struct wm8994 *wm8994;
int ret;
@@ -702,7 +632,14 @@ static __devinit int wm8994_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8994);
wm8994->dev = &i2c->dev;
wm8994->irq = i2c->irq;
- wm8994->type = id->driver_data;
+
+ if (i2c->dev.of_node) {
+ of_id = of_match_device(wm8994_of_match, &i2c->dev);
+ if (of_id)
+ wm8994->type = (enum wm8994_type)of_id->data;
+ } else {
+ wm8994->type = id->driver_data;
+ }
wm8994->regmap = devm_regmap_init_i2c(i2c, &wm8994_base_regmap_config);
if (IS_ERR(wm8994->regmap)) {
@@ -715,7 +652,7 @@ static __devinit int wm8994_i2c_probe(struct i2c_client *i2c,
return wm8994_device_init(wm8994, i2c->irq);
}
-static __devexit int wm8994_i2c_remove(struct i2c_client *i2c)
+static int wm8994_i2c_remove(struct i2c_client *i2c)
{
struct wm8994 *wm8994 = i2c_get_clientdata(i2c);
@@ -733,18 +670,19 @@ static const struct i2c_device_id wm8994_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, wm8994_i2c_id);
-static UNIVERSAL_DEV_PM_OPS(wm8994_pm_ops, wm8994_suspend, wm8994_resume,
- NULL);
+static const struct dev_pm_ops wm8994_pm_ops = {
+ SET_RUNTIME_PM_OPS(wm8994_suspend, wm8994_resume, NULL)
+};
static struct i2c_driver wm8994_i2c_driver = {
.driver = {
.name = "wm8994",
.owner = THIS_MODULE,
.pm = &wm8994_pm_ops,
- .of_match_table = wm8994_of_match,
+ .of_match_table = of_match_ptr(wm8994_of_match),
},
.probe = wm8994_i2c_probe,
- .remove = __devexit_p(wm8994_i2c_remove),
+ .remove = wm8994_i2c_remove,
.id_table = wm8994_i2c_id,
};
diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c
index a050e56a9bb..e74dedda5b5 100644
--- a/drivers/mfd/wm8994-irq.c
+++ b/drivers/mfd/wm8994-irq.c
@@ -14,10 +14,12 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/mfd/core.h>
#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
#include <linux/regmap.h>
#include <linux/mfd/wm8994/core.h>
@@ -138,11 +140,60 @@ static struct regmap_irq_chip wm8994_irq_chip = {
.runtime_pm = true,
};
+static void wm8994_edge_irq_enable(struct irq_data *data)
+{
+}
+
+static void wm8994_edge_irq_disable(struct irq_data *data)
+{
+}
+
+static struct irq_chip wm8994_edge_irq_chip = {
+ .name = "wm8994_edge",
+ .irq_disable = wm8994_edge_irq_disable,
+ .irq_enable = wm8994_edge_irq_enable,
+};
+
+static irqreturn_t wm8994_edge_irq(int irq, void *data)
+{
+ struct wm8994 *wm8994 = data;
+
+ while (gpio_get_value_cansleep(wm8994->pdata.irq_gpio))
+ handle_nested_irq(irq_create_mapping(wm8994->edge_irq, 0));
+
+ return IRQ_HANDLED;
+}
+
+static int wm8994_edge_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct wm8994 *wm8994 = h->host_data;
+
+ irq_set_chip_data(virq, wm8994);
+ irq_set_chip_and_handler(virq, &wm8994_edge_irq_chip, handle_edge_irq);
+ irq_set_nested_thread(virq, 1);
+
+ /* ARM needs us to explicitly flag the IRQ as valid
+ * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+ set_irq_flags(virq, IRQF_VALID);
+#else
+ irq_set_noprobe(virq);
+#endif
+
+ return 0;
+}
+
+static struct irq_domain_ops wm8994_edge_irq_ops = {
+ .map = wm8994_edge_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
int wm8994_irq_init(struct wm8994 *wm8994)
{
int ret;
unsigned long irqflags;
- struct wm8994_pdata *pdata = wm8994->dev->platform_data;
+ struct wm8994_pdata *pdata = dev_get_platdata(wm8994->dev);
if (!wm8994->irq) {
dev_warn(wm8994->dev,
@@ -156,10 +207,51 @@ int wm8994_irq_init(struct wm8994 *wm8994)
if (pdata->irq_flags)
irqflags = pdata->irq_flags;
- ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq,
- irqflags,
- wm8994->irq_base, &wm8994_irq_chip,
- &wm8994->irq_data);
+ /* use a GPIO for edge triggered controllers */
+ if (irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+ if (gpio_to_irq(pdata->irq_gpio) != wm8994->irq) {
+ dev_warn(wm8994->dev, "IRQ %d is not GPIO %d (%d)\n",
+ wm8994->irq, pdata->irq_gpio,
+ gpio_to_irq(pdata->irq_gpio));
+ wm8994->irq = gpio_to_irq(pdata->irq_gpio);
+ }
+
+ ret = devm_gpio_request_one(wm8994->dev, pdata->irq_gpio,
+ GPIOF_IN, "WM8994 IRQ");
+
+ if (ret != 0) {
+ dev_err(wm8994->dev, "Failed to get IRQ GPIO: %d\n",
+ ret);
+ return ret;
+ }
+
+ wm8994->edge_irq = irq_domain_add_linear(NULL, 1,
+ &wm8994_edge_irq_ops,
+ wm8994);
+
+ ret = regmap_add_irq_chip(wm8994->regmap,
+ irq_create_mapping(wm8994->edge_irq,
+ 0),
+ IRQF_ONESHOT,
+ wm8994->irq_base, &wm8994_irq_chip,
+ &wm8994->irq_data);
+ if (ret != 0) {
+ dev_err(wm8994->dev, "Failed to get IRQ: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = request_threaded_irq(wm8994->irq,
+ NULL, wm8994_edge_irq,
+ irqflags,
+ "WM8994 edge", wm8994);
+ } else {
+ ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq,
+ irqflags,
+ wm8994->irq_base, &wm8994_irq_chip,
+ &wm8994->irq_data);
+ }
+
if (ret != 0) {
dev_err(wm8994->dev, "Failed to register IRQ chip: %d\n", ret);
return ret;
diff --git a/drivers/mfd/wm8997-tables.c b/drivers/mfd/wm8997-tables.c
new file mode 100644
index 00000000000..c7a81da64ee
--- /dev/null
+++ b/drivers/mfd/wm8997-tables.c
@@ -0,0 +1,1527 @@
+/*
+ * wm8997-tables.c -- WM8997 data tables
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Charles Keepax <ckeepax@opensource.wolfsonmicro.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/module.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+
+static const struct reg_default wm8997_reva_patch[] = {
+ { 0x80, 0x0003 },
+ { 0x214, 0x0008 },
+ { 0x458, 0x0000 },
+ { 0x0081, 0xE022 },
+ { 0x294, 0x0000 },
+ { 0x80, 0x0000 },
+ { 0x171, 0x0000 },
+};
+
+/* We use a function so we can use ARRAY_SIZE() */
+int wm8997_patch(struct arizona *arizona)
+{
+ switch (arizona->rev) {
+ case 0:
+ return regmap_register_patch(arizona->regmap,
+ wm8997_reva_patch,
+ ARRAY_SIZE(wm8997_reva_patch));
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(wm8997_patch);
+
+static const struct regmap_irq wm8997_aod_irqs[ARIZONA_NUM_IRQ] = {
+ [ARIZONA_IRQ_GP5_FALL] = { .mask = ARIZONA_GP5_FALL_EINT1 },
+ [ARIZONA_IRQ_GP5_RISE] = { .mask = ARIZONA_GP5_RISE_EINT1 },
+ [ARIZONA_IRQ_JD_FALL] = { .mask = ARIZONA_JD1_FALL_EINT1 },
+ [ARIZONA_IRQ_JD_RISE] = { .mask = ARIZONA_JD1_RISE_EINT1 },
+};
+
+const struct regmap_irq_chip wm8997_aod = {
+ .name = "wm8997 AOD",
+ .status_base = ARIZONA_AOD_IRQ1,
+ .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1,
+ .ack_base = ARIZONA_AOD_IRQ1,
+ .num_regs = 1,
+ .irqs = wm8997_aod_irqs,
+ .num_irqs = ARRAY_SIZE(wm8997_aod_irqs),
+};
+EXPORT_SYMBOL_GPL(wm8997_aod);
+
+static const struct regmap_irq wm8997_irqs[ARIZONA_NUM_IRQ] = {
+ [ARIZONA_IRQ_GP4] = { .reg_offset = 0, .mask = ARIZONA_GP4_EINT1 },
+ [ARIZONA_IRQ_GP3] = { .reg_offset = 0, .mask = ARIZONA_GP3_EINT1 },
+ [ARIZONA_IRQ_GP2] = { .reg_offset = 0, .mask = ARIZONA_GP2_EINT1 },
+ [ARIZONA_IRQ_GP1] = { .reg_offset = 0, .mask = ARIZONA_GP1_EINT1 },
+
+ [ARIZONA_IRQ_SPK_SHUTDOWN_WARN] = {
+ .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_WARN_EINT1
+ },
+ [ARIZONA_IRQ_SPK_SHUTDOWN] = {
+ .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_EINT1
+ },
+ [ARIZONA_IRQ_HPDET] = {
+ .reg_offset = 2, .mask = ARIZONA_HPDET_EINT1
+ },
+ [ARIZONA_IRQ_MICDET] = {
+ .reg_offset = 2, .mask = ARIZONA_MICDET_EINT1
+ },
+ [ARIZONA_IRQ_WSEQ_DONE] = {
+ .reg_offset = 2, .mask = ARIZONA_WSEQ_DONE_EINT1
+ },
+ [ARIZONA_IRQ_DRC1_SIG_DET] = {
+ .reg_offset = 2, .mask = ARIZONA_DRC1_SIG_DET_EINT1
+ },
+ [ARIZONA_IRQ_UNDERCLOCKED] = {
+ .reg_offset = 2, .mask = ARIZONA_UNDERCLOCKED_EINT1
+ },
+ [ARIZONA_IRQ_OVERCLOCKED] = {
+ .reg_offset = 2, .mask = ARIZONA_OVERCLOCKED_EINT1
+ },
+ [ARIZONA_IRQ_FLL2_LOCK] = {
+ .reg_offset = 2, .mask = ARIZONA_FLL2_LOCK_EINT1
+ },
+ [ARIZONA_IRQ_FLL1_LOCK] = {
+ .reg_offset = 2, .mask = ARIZONA_FLL1_LOCK_EINT1
+ },
+ [ARIZONA_IRQ_CLKGEN_ERR] = {
+ .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_EINT1
+ },
+ [ARIZONA_IRQ_CLKGEN_ERR_ASYNC] = {
+ .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_ASYNC_EINT1
+ },
+
+ [ARIZONA_IRQ_AIF2_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_AIF2_ERR_EINT1
+ },
+ [ARIZONA_IRQ_AIF1_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_AIF1_ERR_EINT1
+ },
+ [ARIZONA_IRQ_CTRLIF_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_CTRLIF_ERR_EINT1
+ },
+ [ARIZONA_IRQ_MIXER_DROPPED_SAMPLES] = {
+ .reg_offset = 3, .mask = ARIZONA_MIXER_DROPPED_SAMPLE_EINT1
+ },
+ [ARIZONA_IRQ_ASYNC_CLK_ENA_LOW] = {
+ .reg_offset = 3, .mask = ARIZONA_ASYNC_CLK_ENA_LOW_EINT1
+ },
+ [ARIZONA_IRQ_SYSCLK_ENA_LOW] = {
+ .reg_offset = 3, .mask = ARIZONA_SYSCLK_ENA_LOW_EINT1
+ },
+ [ARIZONA_IRQ_ISRC1_CFG_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_ISRC1_CFG_ERR_EINT1
+ },
+ [ARIZONA_IRQ_ISRC2_CFG_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_ISRC2_CFG_ERR_EINT1
+ },
+
+ [ARIZONA_IRQ_BOOT_DONE] = {
+ .reg_offset = 4, .mask = ARIZONA_BOOT_DONE_EINT1
+ },
+ [ARIZONA_IRQ_DCS_DAC_DONE] = {
+ .reg_offset = 4, .mask = ARIZONA_DCS_DAC_DONE_EINT1
+ },
+ [ARIZONA_IRQ_DCS_HP_DONE] = {
+ .reg_offset = 4, .mask = ARIZONA_DCS_HP_DONE_EINT1
+ },
+ [ARIZONA_IRQ_FLL2_CLOCK_OK] = {
+ .reg_offset = 4, .mask = ARIZONA_FLL2_CLOCK_OK_EINT1
+ },
+ [ARIZONA_IRQ_FLL1_CLOCK_OK] = {
+ .reg_offset = 4, .mask = ARIZONA_FLL1_CLOCK_OK_EINT1
+ },
+};
+
+const struct regmap_irq_chip wm8997_irq = {
+ .name = "wm8997 IRQ",
+ .status_base = ARIZONA_INTERRUPT_STATUS_1,
+ .mask_base = ARIZONA_INTERRUPT_STATUS_1_MASK,
+ .ack_base = ARIZONA_INTERRUPT_STATUS_1,
+ .num_regs = 5,
+ .irqs = wm8997_irqs,
+ .num_irqs = ARRAY_SIZE(wm8997_irqs),
+};
+EXPORT_SYMBOL_GPL(wm8997_irq);
+
+static const struct reg_default wm8997_reg_default[] = {
+ { 0x00000009, 0x0001 }, /* R9 - Ctrl IF I2C1 CFG 1 */
+ { 0x00000016, 0x0000 }, /* R22 - Write Sequencer Ctrl 0 */
+ { 0x00000017, 0x0000 }, /* R23 - Write Sequencer Ctrl 1 */
+ { 0x00000018, 0x0000 }, /* R24 - Write Sequencer Ctrl 2 */
+ { 0x00000020, 0x0000 }, /* R32 - Tone Generator 1 */
+ { 0x00000021, 0x1000 }, /* R33 - Tone Generator 2 */
+ { 0x00000022, 0x0000 }, /* R34 - Tone Generator 3 */
+ { 0x00000023, 0x1000 }, /* R35 - Tone Generator 4 */
+ { 0x00000024, 0x0000 }, /* R36 - Tone Generator 5 */
+ { 0x00000030, 0x0000 }, /* R48 - PWM Drive 1 */
+ { 0x00000031, 0x0100 }, /* R49 - PWM Drive 2 */
+ { 0x00000032, 0x0100 }, /* R50 - PWM Drive 3 */
+ { 0x00000040, 0x0000 }, /* R64 - Wake control */
+ { 0x00000041, 0x0000 }, /* R65 - Sequence control */
+ { 0x00000061, 0x01FF }, /* R97 - Sample Rate Sequence Select 1 */
+ { 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */
+ { 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */
+ { 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */
+ { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 3 */
+ { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 4 */
+ { 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 5 */
+ { 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 6 */
+ { 0x00000070, 0x0000 }, /* R112 - Comfort Noise Generator */
+ { 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */
+ { 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */
+ { 0x00000092, 0x0000 }, /* R146 - Haptics phase 1 intensity */
+ { 0x00000093, 0x0000 }, /* R147 - Haptics phase 1 duration */
+ { 0x00000094, 0x0000 }, /* R148 - Haptics phase 2 intensity */
+ { 0x00000095, 0x0000 }, /* R149 - Haptics phase 2 duration */
+ { 0x00000096, 0x0000 }, /* R150 - Haptics phase 3 intensity */
+ { 0x00000097, 0x0000 }, /* R151 - Haptics phase 3 duration */
+ { 0x00000100, 0x0002 }, /* R256 - Clock 32k 1 */
+ { 0x00000101, 0x0304 }, /* R257 - System Clock 1 */
+ { 0x00000102, 0x0011 }, /* R258 - Sample rate 1 */
+ { 0x00000103, 0x0011 }, /* R259 - Sample rate 2 */
+ { 0x00000104, 0x0011 }, /* R260 - Sample rate 3 */
+ { 0x00000112, 0x0305 }, /* R274 - Async clock 1 */
+ { 0x00000113, 0x0011 }, /* R275 - Async sample rate 1 */
+ { 0x00000149, 0x0000 }, /* R329 - Output system clock */
+ { 0x0000014A, 0x0000 }, /* R330 - Output async clock */
+ { 0x00000152, 0x0000 }, /* R338 - Rate Estimator 1 */
+ { 0x00000153, 0x0000 }, /* R339 - Rate Estimator 2 */
+ { 0x00000154, 0x0000 }, /* R340 - Rate Estimator 3 */
+ { 0x00000155, 0x0000 }, /* R341 - Rate Estimator 4 */
+ { 0x00000156, 0x0000 }, /* R342 - Rate Estimator 5 */
+ { 0x00000161, 0x0000 }, /* R353 - Dynamic Frequency Scaling 1 */
+ { 0x00000171, 0x0000 }, /* R369 - FLL1 Control 1 */
+ { 0x00000172, 0x0008 }, /* R370 - FLL1 Control 2 */
+ { 0x00000173, 0x0018 }, /* R371 - FLL1 Control 3 */
+ { 0x00000174, 0x007D }, /* R372 - FLL1 Control 4 */
+ { 0x00000175, 0x0004 }, /* R373 - FLL1 Control 5 */
+ { 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */
+ { 0x00000177, 0x0181 }, /* R375 - FLL1 Loop Filter Test 1 */
+ { 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */
+ { 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */
+ { 0x00000183, 0x0000 }, /* R387 - FLL1 Synchroniser 3 */
+ { 0x00000184, 0x0000 }, /* R388 - FLL1 Synchroniser 4 */
+ { 0x00000185, 0x0000 }, /* R389 - FLL1 Synchroniser 5 */
+ { 0x00000186, 0x0000 }, /* R390 - FLL1 Synchroniser 6 */
+ { 0x00000189, 0x0000 }, /* R393 - FLL1 Spread Spectrum */
+ { 0x0000018A, 0x0004 }, /* R394 - FLL1 GPIO Clock */
+ { 0x00000191, 0x0000 }, /* R401 - FLL2 Control 1 */
+ { 0x00000192, 0x0008 }, /* R402 - FLL2 Control 2 */
+ { 0x00000193, 0x0018 }, /* R403 - FLL2 Control 3 */
+ { 0x00000194, 0x007D }, /* R404 - FLL2 Control 4 */
+ { 0x00000195, 0x0004 }, /* R405 - FLL2 Control 5 */
+ { 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */
+ { 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */
+ { 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */
+ { 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */
+ { 0x000001A3, 0x0000 }, /* R419 - FLL2 Synchroniser 3 */
+ { 0x000001A4, 0x0000 }, /* R420 - FLL2 Synchroniser 4 */
+ { 0x000001A5, 0x0000 }, /* R421 - FLL2 Synchroniser 5 */
+ { 0x000001A6, 0x0000 }, /* R422 - FLL2 Synchroniser 6 */
+ { 0x000001A9, 0x0000 }, /* R425 - FLL2 Spread Spectrum */
+ { 0x000001AA, 0x0004 }, /* R426 - FLL2 GPIO Clock */
+ { 0x00000200, 0x0006 }, /* R512 - Mic Charge Pump 1 */
+ { 0x00000210, 0x00D4 }, /* R528 - LDO1 Control 1 */
+ { 0x00000212, 0x0000 }, /* R530 - LDO1 Control 2 */
+ { 0x00000213, 0x0344 }, /* R531 - LDO2 Control 1 */
+ { 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */
+ { 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */
+ { 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */
+ { 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */
+ { 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */
+ { 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */
+ { 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */
+ { 0x000002A5, 0x0000 }, /* R677 - Mic Detect 3 */
+ { 0x000002C3, 0x0000 }, /* R707 - Mic noise mix control 1 */
+ { 0x000002CB, 0x0000 }, /* R715 - Isolation control */
+ { 0x000002D3, 0x0000 }, /* R723 - Jack detect analogue */
+ { 0x00000300, 0x0000 }, /* R768 - Input Enables */
+ { 0x00000308, 0x0000 }, /* R776 - Input Rate */
+ { 0x00000309, 0x0022 }, /* R777 - Input Volume Ramp */
+ { 0x00000310, 0x2080 }, /* R784 - IN1L Control */
+ { 0x00000311, 0x0180 }, /* R785 - ADC Digital Volume 1L */
+ { 0x00000312, 0x0000 }, /* R786 - DMIC1L Control */
+ { 0x00000314, 0x0080 }, /* R788 - IN1R Control */
+ { 0x00000315, 0x0180 }, /* R789 - ADC Digital Volume 1R */
+ { 0x00000316, 0x0000 }, /* R790 - DMIC1R Control */
+ { 0x00000318, 0x2080 }, /* R792 - IN2L Control */
+ { 0x00000319, 0x0180 }, /* R793 - ADC Digital Volume 2L */
+ { 0x0000031A, 0x0000 }, /* R794 - DMIC2L Control */
+ { 0x0000031C, 0x0080 }, /* R796 - IN2R Control */
+ { 0x0000031D, 0x0180 }, /* R797 - ADC Digital Volume 2R */
+ { 0x0000031E, 0x0000 }, /* R798 - DMIC2R Control */
+ { 0x00000400, 0x0000 }, /* R1024 - Output Enables 1 */
+ { 0x00000408, 0x0000 }, /* R1032 - Output Rate 1 */
+ { 0x00000409, 0x0022 }, /* R1033 - Output Volume Ramp */
+ { 0x00000410, 0x0080 }, /* R1040 - Output Path Config 1L */
+ { 0x00000411, 0x0180 }, /* R1041 - DAC Digital Volume 1L */
+ { 0x00000412, 0x0080 }, /* R1042 - DAC Volume Limit 1L */
+ { 0x00000413, 0x0001 }, /* R1043 - Noise Gate Select 1L */
+ { 0x00000414, 0x0080 }, /* R1044 - Output Path Config 1R */
+ { 0x00000415, 0x0180 }, /* R1045 - DAC Digital Volume 1R */
+ { 0x00000416, 0x0080 }, /* R1046 - DAC Volume Limit 1R */
+ { 0x00000417, 0x0002 }, /* R1047 - Noise Gate Select 1R */
+ { 0x00000420, 0x0080 }, /* R1056 - Output Path Config 3L */
+ { 0x00000421, 0x0180 }, /* R1057 - DAC Digital Volume 3L */
+ { 0x00000422, 0x0080 }, /* R1058 - DAC Volume Limit 3L */
+ { 0x00000423, 0x0010 }, /* R1059 - Noise Gate Select 3L */
+ { 0x00000428, 0x0000 }, /* R1064 - Output Path Config 4L */
+ { 0x00000429, 0x0180 }, /* R1065 - DAC Digital Volume 4L */
+ { 0x0000042A, 0x0080 }, /* R1066 - Out Volume 4L */
+ { 0x0000042B, 0x0040 }, /* R1067 - Noise Gate Select 4L */
+ { 0x00000430, 0x0000 }, /* R1072 - Output Path Config 5L */
+ { 0x00000431, 0x0180 }, /* R1073 - DAC Digital Volume 5L */
+ { 0x00000432, 0x0080 }, /* R1074 - DAC Volume Limit 5L */
+ { 0x00000433, 0x0100 }, /* R1075 - Noise Gate Select 5L */
+ { 0x00000435, 0x0180 }, /* R1077 - DAC Digital Volume 5R */
+ { 0x00000436, 0x0080 }, /* R1078 - DAC Volume Limit 5R */
+ { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */
+ { 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */
+ { 0x00000458, 0x0000 }, /* R1112 - Noise Gate Control */
+ { 0x00000490, 0x0069 }, /* R1168 - PDM SPK1 CTRL 1 */
+ { 0x00000491, 0x0000 }, /* R1169 - PDM SPK1 CTRL 2 */
+ { 0x00000500, 0x000C }, /* R1280 - AIF1 BCLK Ctrl */
+ { 0x00000501, 0x0008 }, /* R1281 - AIF1 Tx Pin Ctrl */
+ { 0x00000502, 0x0000 }, /* R1282 - AIF1 Rx Pin Ctrl */
+ { 0x00000503, 0x0000 }, /* R1283 - AIF1 Rate Ctrl */
+ { 0x00000504, 0x0000 }, /* R1284 - AIF1 Format */
+ { 0x00000505, 0x0040 }, /* R1285 - AIF1 Tx BCLK Rate */
+ { 0x00000506, 0x0040 }, /* R1286 - AIF1 Rx BCLK Rate */
+ { 0x00000507, 0x1818 }, /* R1287 - AIF1 Frame Ctrl 1 */
+ { 0x00000508, 0x1818 }, /* R1288 - AIF1 Frame Ctrl 2 */
+ { 0x00000509, 0x0000 }, /* R1289 - AIF1 Frame Ctrl 3 */
+ { 0x0000050A, 0x0001 }, /* R1290 - AIF1 Frame Ctrl 4 */
+ { 0x0000050B, 0x0002 }, /* R1291 - AIF1 Frame Ctrl 5 */
+ { 0x0000050C, 0x0003 }, /* R1292 - AIF1 Frame Ctrl 6 */
+ { 0x0000050D, 0x0004 }, /* R1293 - AIF1 Frame Ctrl 7 */
+ { 0x0000050E, 0x0005 }, /* R1294 - AIF1 Frame Ctrl 8 */
+ { 0x0000050F, 0x0006 }, /* R1295 - AIF1 Frame Ctrl 9 */
+ { 0x00000510, 0x0007 }, /* R1296 - AIF1 Frame Ctrl 10 */
+ { 0x00000511, 0x0000 }, /* R1297 - AIF1 Frame Ctrl 11 */
+ { 0x00000512, 0x0001 }, /* R1298 - AIF1 Frame Ctrl 12 */
+ { 0x00000513, 0x0002 }, /* R1299 - AIF1 Frame Ctrl 13 */
+ { 0x00000514, 0x0003 }, /* R1300 - AIF1 Frame Ctrl 14 */
+ { 0x00000515, 0x0004 }, /* R1301 - AIF1 Frame Ctrl 15 */
+ { 0x00000516, 0x0005 }, /* R1302 - AIF1 Frame Ctrl 16 */
+ { 0x00000517, 0x0006 }, /* R1303 - AIF1 Frame Ctrl 17 */
+ { 0x00000518, 0x0007 }, /* R1304 - AIF1 Frame Ctrl 18 */
+ { 0x00000519, 0x0000 }, /* R1305 - AIF1 Tx Enables */
+ { 0x0000051A, 0x0000 }, /* R1306 - AIF1 Rx Enables */
+ { 0x00000540, 0x000C }, /* R1344 - AIF2 BCLK Ctrl */
+ { 0x00000541, 0x0008 }, /* R1345 - AIF2 Tx Pin Ctrl */
+ { 0x00000542, 0x0000 }, /* R1346 - AIF2 Rx Pin Ctrl */
+ { 0x00000543, 0x0000 }, /* R1347 - AIF2 Rate Ctrl */
+ { 0x00000544, 0x0000 }, /* R1348 - AIF2 Format */
+ { 0x00000545, 0x0040 }, /* R1349 - AIF2 Tx BCLK Rate */
+ { 0x00000546, 0x0040 }, /* R1350 - AIF2 Rx BCLK Rate */
+ { 0x00000547, 0x1818 }, /* R1351 - AIF2 Frame Ctrl 1 */
+ { 0x00000548, 0x1818 }, /* R1352 - AIF2 Frame Ctrl 2 */
+ { 0x00000549, 0x0000 }, /* R1353 - AIF2 Frame Ctrl 3 */
+ { 0x0000054A, 0x0001 }, /* R1354 - AIF2 Frame Ctrl 4 */
+ { 0x00000551, 0x0000 }, /* R1361 - AIF2 Frame Ctrl 11 */
+ { 0x00000552, 0x0001 }, /* R1362 - AIF2 Frame Ctrl 12 */
+ { 0x00000559, 0x0000 }, /* R1369 - AIF2 Tx Enables */
+ { 0x0000055A, 0x0000 }, /* R1370 - AIF2 Rx Enables */
+ { 0x000005E3, 0x0004 }, /* R1507 - SLIMbus Framer Ref Gear */
+ { 0x000005E5, 0x0000 }, /* R1509 - SLIMbus Rates 1 */
+ { 0x000005E6, 0x0000 }, /* R1510 - SLIMbus Rates 2 */
+ { 0x000005E7, 0x0000 }, /* R1511 - SLIMbus Rates 3 */
+ { 0x000005E8, 0x0000 }, /* R1512 - SLIMbus Rates 4 */
+ { 0x000005E9, 0x0000 }, /* R1513 - SLIMbus Rates 5 */
+ { 0x000005EA, 0x0000 }, /* R1514 - SLIMbus Rates 6 */
+ { 0x000005EB, 0x0000 }, /* R1515 - SLIMbus Rates 7 */
+ { 0x000005EC, 0x0000 }, /* R1516 - SLIMbus Rates 8 */
+ { 0x000005F5, 0x0000 }, /* R1525 - SLIMbus RX Channel Enable */
+ { 0x000005F6, 0x0000 }, /* R1526 - SLIMbus TX Channel Enable */
+ { 0x00000640, 0x0000 }, /* R1600 - PWM1MIX Input 1 Source */
+ { 0x00000641, 0x0080 }, /* R1601 - PWM1MIX Input 1 Volume */
+ { 0x00000642, 0x0000 }, /* R1602 - PWM1MIX Input 2 Source */
+ { 0x00000643, 0x0080 }, /* R1603 - PWM1MIX Input 2 Volume */
+ { 0x00000644, 0x0000 }, /* R1604 - PWM1MIX Input 3 Source */
+ { 0x00000645, 0x0080 }, /* R1605 - PWM1MIX Input 3 Volume */
+ { 0x00000646, 0x0000 }, /* R1606 - PWM1MIX Input 4 Source */
+ { 0x00000647, 0x0080 }, /* R1607 - PWM1MIX Input 4 Volume */
+ { 0x00000648, 0x0000 }, /* R1608 - PWM2MIX Input 1 Source */
+ { 0x00000649, 0x0080 }, /* R1609 - PWM2MIX Input 1 Volume */
+ { 0x0000064A, 0x0000 }, /* R1610 - PWM2MIX Input 2 Source */
+ { 0x0000064B, 0x0080 }, /* R1611 - PWM2MIX Input 2 Volume */
+ { 0x0000064C, 0x0000 }, /* R1612 - PWM2MIX Input 3 Source */
+ { 0x0000064D, 0x0080 }, /* R1613 - PWM2MIX Input 3 Volume */
+ { 0x0000064E, 0x0000 }, /* R1614 - PWM2MIX Input 4 Source */
+ { 0x0000064F, 0x0080 }, /* R1615 - PWM2MIX Input 4 Volume */
+ { 0x00000660, 0x0000 }, /* R1632 - MICMIX Input 1 Source */
+ { 0x00000661, 0x0080 }, /* R1633 - MICMIX Input 1 Volume */
+ { 0x00000662, 0x0000 }, /* R1634 - MICMIX Input 2 Source */
+ { 0x00000663, 0x0080 }, /* R1635 - MICMIX Input 2 Volume */
+ { 0x00000664, 0x0000 }, /* R1636 - MICMIX Input 3 Source */
+ { 0x00000665, 0x0080 }, /* R1637 - MICMIX Input 3 Volume */
+ { 0x00000666, 0x0000 }, /* R1638 - MICMIX Input 4 Source */
+ { 0x00000667, 0x0080 }, /* R1639 - MICMIX Input 4 Volume */
+ { 0x00000668, 0x0000 }, /* R1640 - NOISEMIX Input 1 Source */
+ { 0x00000669, 0x0080 }, /* R1641 - NOISEMIX Input 1 Volume */
+ { 0x0000066A, 0x0000 }, /* R1642 - NOISEMIX Input 2 Source */
+ { 0x0000066B, 0x0080 }, /* R1643 - NOISEMIX Input 2 Volume */
+ { 0x0000066C, 0x0000 }, /* R1644 - NOISEMIX Input 3 Source */
+ { 0x0000066D, 0x0080 }, /* R1645 - NOISEMIX Input 3 Volume */
+ { 0x0000066E, 0x0000 }, /* R1646 - NOISEMIX Input 4 Source */
+ { 0x0000066F, 0x0080 }, /* R1647 - NOISEMIX Input 4 Volume */
+ { 0x00000680, 0x0000 }, /* R1664 - OUT1LMIX Input 1 Source */
+ { 0x00000681, 0x0080 }, /* R1665 - OUT1LMIX Input 1 Volume */
+ { 0x00000682, 0x0000 }, /* R1666 - OUT1LMIX Input 2 Source */
+ { 0x00000683, 0x0080 }, /* R1667 - OUT1LMIX Input 2 Volume */
+ { 0x00000684, 0x0000 }, /* R1668 - OUT1LMIX Input 3 Source */
+ { 0x00000685, 0x0080 }, /* R1669 - OUT1LMIX Input 3 Volume */
+ { 0x00000686, 0x0000 }, /* R1670 - OUT1LMIX Input 4 Source */
+ { 0x00000687, 0x0080 }, /* R1671 - OUT1LMIX Input 4 Volume */
+ { 0x00000688, 0x0000 }, /* R1672 - OUT1RMIX Input 1 Source */
+ { 0x00000689, 0x0080 }, /* R1673 - OUT1RMIX Input 1 Volume */
+ { 0x0000068A, 0x0000 }, /* R1674 - OUT1RMIX Input 2 Source */
+ { 0x0000068B, 0x0080 }, /* R1675 - OUT1RMIX Input 2 Volume */
+ { 0x0000068C, 0x0000 }, /* R1676 - OUT1RMIX Input 3 Source */
+ { 0x0000068D, 0x0080 }, /* R1677 - OUT1RMIX Input 3 Volume */
+ { 0x0000068E, 0x0000 }, /* R1678 - OUT1RMIX Input 4 Source */
+ { 0x0000068F, 0x0080 }, /* R1679 - OUT1RMIX Input 4 Volume */
+ { 0x000006A0, 0x0000 }, /* R1696 - OUT3LMIX Input 1 Source */
+ { 0x000006A1, 0x0080 }, /* R1697 - OUT3LMIX Input 1 Volume */
+ { 0x000006A2, 0x0000 }, /* R1698 - OUT3LMIX Input 2 Source */
+ { 0x000006A3, 0x0080 }, /* R1699 - OUT3LMIX Input 2 Volume */
+ { 0x000006A4, 0x0000 }, /* R1700 - OUT3LMIX Input 3 Source */
+ { 0x000006A5, 0x0080 }, /* R1701 - OUT3LMIX Input 3 Volume */
+ { 0x000006A6, 0x0000 }, /* R1702 - OUT3LMIX Input 4 Source */
+ { 0x000006A7, 0x0080 }, /* R1703 - OUT3LMIX Input 4 Volume */
+ { 0x000006B0, 0x0000 }, /* R1712 - OUT4LMIX Input 1 Source */
+ { 0x000006B1, 0x0080 }, /* R1713 - OUT4LMIX Input 1 Volume */
+ { 0x000006B2, 0x0000 }, /* R1714 - OUT4LMIX Input 2 Source */
+ { 0x000006B3, 0x0080 }, /* R1715 - OUT4LMIX Input 2 Volume */
+ { 0x000006B4, 0x0000 }, /* R1716 - OUT4LMIX Input 3 Source */
+ { 0x000006B5, 0x0080 }, /* R1717 - OUT4LMIX Input 3 Volume */
+ { 0x000006B6, 0x0000 }, /* R1718 - OUT4LMIX Input 4 Source */
+ { 0x000006B7, 0x0080 }, /* R1719 - OUT4LMIX Input 4 Volume */
+ { 0x000006C0, 0x0000 }, /* R1728 - OUT5LMIX Input 1 Source */
+ { 0x000006C1, 0x0080 }, /* R1729 - OUT5LMIX Input 1 Volume */
+ { 0x000006C2, 0x0000 }, /* R1730 - OUT5LMIX Input 2 Source */
+ { 0x000006C3, 0x0080 }, /* R1731 - OUT5LMIX Input 2 Volume */
+ { 0x000006C4, 0x0000 }, /* R1732 - OUT5LMIX Input 3 Source */
+ { 0x000006C5, 0x0080 }, /* R1733 - OUT5LMIX Input 3 Volume */
+ { 0x000006C6, 0x0000 }, /* R1734 - OUT5LMIX Input 4 Source */
+ { 0x000006C7, 0x0080 }, /* R1735 - OUT5LMIX Input 4 Volume */
+ { 0x000006C8, 0x0000 }, /* R1736 - OUT5RMIX Input 1 Source */
+ { 0x000006C9, 0x0080 }, /* R1737 - OUT5RMIX Input 1 Volume */
+ { 0x000006CA, 0x0000 }, /* R1738 - OUT5RMIX Input 2 Source */
+ { 0x000006CB, 0x0080 }, /* R1739 - OUT5RMIX Input 2 Volume */
+ { 0x000006CC, 0x0000 }, /* R1740 - OUT5RMIX Input 3 Source */
+ { 0x000006CD, 0x0080 }, /* R1741 - OUT5RMIX Input 3 Volume */
+ { 0x000006CE, 0x0000 }, /* R1742 - OUT5RMIX Input 4 Source */
+ { 0x000006CF, 0x0080 }, /* R1743 - OUT5RMIX Input 4 Volume */
+ { 0x00000700, 0x0000 }, /* R1792 - AIF1TX1MIX Input 1 Source */
+ { 0x00000701, 0x0080 }, /* R1793 - AIF1TX1MIX Input 1 Volume */
+ { 0x00000702, 0x0000 }, /* R1794 - AIF1TX1MIX Input 2 Source */
+ { 0x00000703, 0x0080 }, /* R1795 - AIF1TX1MIX Input 2 Volume */
+ { 0x00000704, 0x0000 }, /* R1796 - AIF1TX1MIX Input 3 Source */
+ { 0x00000705, 0x0080 }, /* R1797 - AIF1TX1MIX Input 3 Volume */
+ { 0x00000706, 0x0000 }, /* R1798 - AIF1TX1MIX Input 4 Source */
+ { 0x00000707, 0x0080 }, /* R1799 - AIF1TX1MIX Input 4 Volume */
+ { 0x00000708, 0x0000 }, /* R1800 - AIF1TX2MIX Input 1 Source */
+ { 0x00000709, 0x0080 }, /* R1801 - AIF1TX2MIX Input 1 Volume */
+ { 0x0000070A, 0x0000 }, /* R1802 - AIF1TX2MIX Input 2 Source */
+ { 0x0000070B, 0x0080 }, /* R1803 - AIF1TX2MIX Input 2 Volume */
+ { 0x0000070C, 0x0000 }, /* R1804 - AIF1TX2MIX Input 3 Source */
+ { 0x0000070D, 0x0080 }, /* R1805 - AIF1TX2MIX Input 3 Volume */
+ { 0x0000070E, 0x0000 }, /* R1806 - AIF1TX2MIX Input 4 Source */
+ { 0x0000070F, 0x0080 }, /* R1807 - AIF1TX2MIX Input 4 Volume */
+ { 0x00000710, 0x0000 }, /* R1808 - AIF1TX3MIX Input 1 Source */
+ { 0x00000711, 0x0080 }, /* R1809 - AIF1TX3MIX Input 1 Volume */
+ { 0x00000712, 0x0000 }, /* R1810 - AIF1TX3MIX Input 2 Source */
+ { 0x00000713, 0x0080 }, /* R1811 - AIF1TX3MIX Input 2 Volume */
+ { 0x00000714, 0x0000 }, /* R1812 - AIF1TX3MIX Input 3 Source */
+ { 0x00000715, 0x0080 }, /* R1813 - AIF1TX3MIX Input 3 Volume */
+ { 0x00000716, 0x0000 }, /* R1814 - AIF1TX3MIX Input 4 Source */
+ { 0x00000717, 0x0080 }, /* R1815 - AIF1TX3MIX Input 4 Volume */
+ { 0x00000718, 0x0000 }, /* R1816 - AIF1TX4MIX Input 1 Source */
+ { 0x00000719, 0x0080 }, /* R1817 - AIF1TX4MIX Input 1 Volume */
+ { 0x0000071A, 0x0000 }, /* R1818 - AIF1TX4MIX Input 2 Source */
+ { 0x0000071B, 0x0080 }, /* R1819 - AIF1TX4MIX Input 2 Volume */
+ { 0x0000071C, 0x0000 }, /* R1820 - AIF1TX4MIX Input 3 Source */
+ { 0x0000071D, 0x0080 }, /* R1821 - AIF1TX4MIX Input 3 Volume */
+ { 0x0000071E, 0x0000 }, /* R1822 - AIF1TX4MIX Input 4 Source */
+ { 0x0000071F, 0x0080 }, /* R1823 - AIF1TX4MIX Input 4 Volume */
+ { 0x00000720, 0x0000 }, /* R1824 - AIF1TX5MIX Input 1 Source */
+ { 0x00000721, 0x0080 }, /* R1825 - AIF1TX5MIX Input 1 Volume */
+ { 0x00000722, 0x0000 }, /* R1826 - AIF1TX5MIX Input 2 Source */
+ { 0x00000723, 0x0080 }, /* R1827 - AIF1TX5MIX Input 2 Volume */
+ { 0x00000724, 0x0000 }, /* R1828 - AIF1TX5MIX Input 3 Source */
+ { 0x00000725, 0x0080 }, /* R1829 - AIF1TX5MIX Input 3 Volume */
+ { 0x00000726, 0x0000 }, /* R1830 - AIF1TX5MIX Input 4 Source */
+ { 0x00000727, 0x0080 }, /* R1831 - AIF1TX5MIX Input 4 Volume */
+ { 0x00000728, 0x0000 }, /* R1832 - AIF1TX6MIX Input 1 Source */
+ { 0x00000729, 0x0080 }, /* R1833 - AIF1TX6MIX Input 1 Volume */
+ { 0x0000072A, 0x0000 }, /* R1834 - AIF1TX6MIX Input 2 Source */
+ { 0x0000072B, 0x0080 }, /* R1835 - AIF1TX6MIX Input 2 Volume */
+ { 0x0000072C, 0x0000 }, /* R1836 - AIF1TX6MIX Input 3 Source */
+ { 0x0000072D, 0x0080 }, /* R1837 - AIF1TX6MIX Input 3 Volume */
+ { 0x0000072E, 0x0000 }, /* R1838 - AIF1TX6MIX Input 4 Source */
+ { 0x0000072F, 0x0080 }, /* R1839 - AIF1TX6MIX Input 4 Volume */
+ { 0x00000730, 0x0000 }, /* R1840 - AIF1TX7MIX Input 1 Source */
+ { 0x00000731, 0x0080 }, /* R1841 - AIF1TX7MIX Input 1 Volume */
+ { 0x00000732, 0x0000 }, /* R1842 - AIF1TX7MIX Input 2 Source */
+ { 0x00000733, 0x0080 }, /* R1843 - AIF1TX7MIX Input 2 Volume */
+ { 0x00000734, 0x0000 }, /* R1844 - AIF1TX7MIX Input 3 Source */
+ { 0x00000735, 0x0080 }, /* R1845 - AIF1TX7MIX Input 3 Volume */
+ { 0x00000736, 0x0000 }, /* R1846 - AIF1TX7MIX Input 4 Source */
+ { 0x00000737, 0x0080 }, /* R1847 - AIF1TX7MIX Input 4 Volume */
+ { 0x00000738, 0x0000 }, /* R1848 - AIF1TX8MIX Input 1 Source */
+ { 0x00000739, 0x0080 }, /* R1849 - AIF1TX8MIX Input 1 Volume */
+ { 0x0000073A, 0x0000 }, /* R1850 - AIF1TX8MIX Input 2 Source */
+ { 0x0000073B, 0x0080 }, /* R1851 - AIF1TX8MIX Input 2 Volume */
+ { 0x0000073C, 0x0000 }, /* R1852 - AIF1TX8MIX Input 3 Source */
+ { 0x0000073D, 0x0080 }, /* R1853 - AIF1TX8MIX Input 3 Volume */
+ { 0x0000073E, 0x0000 }, /* R1854 - AIF1TX8MIX Input 4 Source */
+ { 0x0000073F, 0x0080 }, /* R1855 - AIF1TX8MIX Input 4 Volume */
+ { 0x00000740, 0x0000 }, /* R1856 - AIF2TX1MIX Input 1 Source */
+ { 0x00000741, 0x0080 }, /* R1857 - AIF2TX1MIX Input 1 Volume */
+ { 0x00000742, 0x0000 }, /* R1858 - AIF2TX1MIX Input 2 Source */
+ { 0x00000743, 0x0080 }, /* R1859 - AIF2TX1MIX Input 2 Volume */
+ { 0x00000744, 0x0000 }, /* R1860 - AIF2TX1MIX Input 3 Source */
+ { 0x00000745, 0x0080 }, /* R1861 - AIF2TX1MIX Input 3 Volume */
+ { 0x00000746, 0x0000 }, /* R1862 - AIF2TX1MIX Input 4 Source */
+ { 0x00000747, 0x0080 }, /* R1863 - AIF2TX1MIX Input 4 Volume */
+ { 0x00000748, 0x0000 }, /* R1864 - AIF2TX2MIX Input 1 Source */
+ { 0x00000749, 0x0080 }, /* R1865 - AIF2TX2MIX Input 1 Volume */
+ { 0x0000074A, 0x0000 }, /* R1866 - AIF2TX2MIX Input 2 Source */
+ { 0x0000074B, 0x0080 }, /* R1867 - AIF2TX2MIX Input 2 Volume */
+ { 0x0000074C, 0x0000 }, /* R1868 - AIF2TX2MIX Input 3 Source */
+ { 0x0000074D, 0x0080 }, /* R1869 - AIF2TX2MIX Input 3 Volume */
+ { 0x0000074E, 0x0000 }, /* R1870 - AIF2TX2MIX Input 4 Source */
+ { 0x0000074F, 0x0080 }, /* R1871 - AIF2TX2MIX Input 4 Volume */
+ { 0x000007C0, 0x0000 }, /* R1984 - SLIMTX1MIX Input 1 Source */
+ { 0x000007C1, 0x0080 }, /* R1985 - SLIMTX1MIX Input 1 Volume */
+ { 0x000007C2, 0x0000 }, /* R1986 - SLIMTX1MIX Input 2 Source */
+ { 0x000007C3, 0x0080 }, /* R1987 - SLIMTX1MIX Input 2 Volume */
+ { 0x000007C4, 0x0000 }, /* R1988 - SLIMTX1MIX Input 3 Source */
+ { 0x000007C5, 0x0080 }, /* R1989 - SLIMTX1MIX Input 3 Volume */
+ { 0x000007C6, 0x0000 }, /* R1990 - SLIMTX1MIX Input 4 Source */
+ { 0x000007C7, 0x0080 }, /* R1991 - SLIMTX1MIX Input 4 Volume */
+ { 0x000007C8, 0x0000 }, /* R1992 - SLIMTX2MIX Input 1 Source */
+ { 0x000007C9, 0x0080 }, /* R1993 - SLIMTX2MIX Input 1 Volume */
+ { 0x000007CA, 0x0000 }, /* R1994 - SLIMTX2MIX Input 2 Source */
+ { 0x000007CB, 0x0080 }, /* R1995 - SLIMTX2MIX Input 2 Volume */
+ { 0x000007CC, 0x0000 }, /* R1996 - SLIMTX2MIX Input 3 Source */
+ { 0x000007CD, 0x0080 }, /* R1997 - SLIMTX2MIX Input 3 Volume */
+ { 0x000007CE, 0x0000 }, /* R1998 - SLIMTX2MIX Input 4 Source */
+ { 0x000007CF, 0x0080 }, /* R1999 - SLIMTX2MIX Input 4 Volume */
+ { 0x000007D0, 0x0000 }, /* R2000 - SLIMTX3MIX Input 1 Source */
+ { 0x000007D1, 0x0080 }, /* R2001 - SLIMTX3MIX Input 1 Volume */
+ { 0x000007D2, 0x0000 }, /* R2002 - SLIMTX3MIX Input 2 Source */
+ { 0x000007D3, 0x0080 }, /* R2003 - SLIMTX3MIX Input 2 Volume */
+ { 0x000007D4, 0x0000 }, /* R2004 - SLIMTX3MIX Input 3 Source */
+ { 0x000007D5, 0x0080 }, /* R2005 - SLIMTX3MIX Input 3 Volume */
+ { 0x000007D6, 0x0000 }, /* R2006 - SLIMTX3MIX Input 4 Source */
+ { 0x000007D7, 0x0080 }, /* R2007 - SLIMTX3MIX Input 4 Volume */
+ { 0x000007D8, 0x0000 }, /* R2008 - SLIMTX4MIX Input 1 Source */
+ { 0x000007D9, 0x0080 }, /* R2009 - SLIMTX4MIX Input 1 Volume */
+ { 0x000007DA, 0x0000 }, /* R2010 - SLIMTX4MIX Input 2 Source */
+ { 0x000007DB, 0x0080 }, /* R2011 - SLIMTX4MIX Input 2 Volume */
+ { 0x000007DC, 0x0000 }, /* R2012 - SLIMTX4MIX Input 3 Source */
+ { 0x000007DD, 0x0080 }, /* R2013 - SLIMTX4MIX Input 3 Volume */
+ { 0x000007DE, 0x0000 }, /* R2014 - SLIMTX4MIX Input 4 Source */
+ { 0x000007DF, 0x0080 }, /* R2015 - SLIMTX4MIX Input 4 Volume */
+ { 0x000007E0, 0x0000 }, /* R2016 - SLIMTX5MIX Input 1 Source */
+ { 0x000007E1, 0x0080 }, /* R2017 - SLIMTX5MIX Input 1 Volume */
+ { 0x000007E2, 0x0000 }, /* R2018 - SLIMTX5MIX Input 2 Source */
+ { 0x000007E3, 0x0080 }, /* R2019 - SLIMTX5MIX Input 2 Volume */
+ { 0x000007E4, 0x0000 }, /* R2020 - SLIMTX5MIX Input 3 Source */
+ { 0x000007E5, 0x0080 }, /* R2021 - SLIMTX5MIX Input 3 Volume */
+ { 0x000007E6, 0x0000 }, /* R2022 - SLIMTX5MIX Input 4 Source */
+ { 0x000007E7, 0x0080 }, /* R2023 - SLIMTX5MIX Input 4 Volume */
+ { 0x000007E8, 0x0000 }, /* R2024 - SLIMTX6MIX Input 1 Source */
+ { 0x000007E9, 0x0080 }, /* R2025 - SLIMTX6MIX Input 1 Volume */
+ { 0x000007EA, 0x0000 }, /* R2026 - SLIMTX6MIX Input 2 Source */
+ { 0x000007EB, 0x0080 }, /* R2027 - SLIMTX6MIX Input 2 Volume */
+ { 0x000007EC, 0x0000 }, /* R2028 - SLIMTX6MIX Input 3 Source */
+ { 0x000007ED, 0x0080 }, /* R2029 - SLIMTX6MIX Input 3 Volume */
+ { 0x000007EE, 0x0000 }, /* R2030 - SLIMTX6MIX Input 4 Source */
+ { 0x000007EF, 0x0080 }, /* R2031 - SLIMTX6MIX Input 4 Volume */
+ { 0x000007F0, 0x0000 }, /* R2032 - SLIMTX7MIX Input 1 Source */
+ { 0x000007F1, 0x0080 }, /* R2033 - SLIMTX7MIX Input 1 Volume */
+ { 0x000007F2, 0x0000 }, /* R2034 - SLIMTX7MIX Input 2 Source */
+ { 0x000007F3, 0x0080 }, /* R2035 - SLIMTX7MIX Input 2 Volume */
+ { 0x000007F4, 0x0000 }, /* R2036 - SLIMTX7MIX Input 3 Source */
+ { 0x000007F5, 0x0080 }, /* R2037 - SLIMTX7MIX Input 3 Volume */
+ { 0x000007F6, 0x0000 }, /* R2038 - SLIMTX7MIX Input 4 Source */
+ { 0x000007F7, 0x0080 }, /* R2039 - SLIMTX7MIX Input 4 Volume */
+ { 0x000007F8, 0x0000 }, /* R2040 - SLIMTX8MIX Input 1 Source */
+ { 0x000007F9, 0x0080 }, /* R2041 - SLIMTX8MIX Input 1 Volume */
+ { 0x000007FA, 0x0000 }, /* R2042 - SLIMTX8MIX Input 2 Source */
+ { 0x000007FB, 0x0080 }, /* R2043 - SLIMTX8MIX Input 2 Volume */
+ { 0x000007FC, 0x0000 }, /* R2044 - SLIMTX8MIX Input 3 Source */
+ { 0x000007FD, 0x0080 }, /* R2045 - SLIMTX8MIX Input 3 Volume */
+ { 0x000007FE, 0x0000 }, /* R2046 - SLIMTX8MIX Input 4 Source */
+ { 0x000007FF, 0x0080 }, /* R2047 - SLIMTX8MIX Input 4 Volume */
+ { 0x00000880, 0x0000 }, /* R2176 - EQ1MIX Input 1 Source */
+ { 0x00000881, 0x0080 }, /* R2177 - EQ1MIX Input 1 Volume */
+ { 0x00000882, 0x0000 }, /* R2178 - EQ1MIX Input 2 Source */
+ { 0x00000883, 0x0080 }, /* R2179 - EQ1MIX Input 2 Volume */
+ { 0x00000884, 0x0000 }, /* R2180 - EQ1MIX Input 3 Source */
+ { 0x00000885, 0x0080 }, /* R2181 - EQ1MIX Input 3 Volume */
+ { 0x00000886, 0x0000 }, /* R2182 - EQ1MIX Input 4 Source */
+ { 0x00000887, 0x0080 }, /* R2183 - EQ1MIX Input 4 Volume */
+ { 0x00000888, 0x0000 }, /* R2184 - EQ2MIX Input 1 Source */
+ { 0x00000889, 0x0080 }, /* R2185 - EQ2MIX Input 1 Volume */
+ { 0x0000088A, 0x0000 }, /* R2186 - EQ2MIX Input 2 Source */
+ { 0x0000088B, 0x0080 }, /* R2187 - EQ2MIX Input 2 Volume */
+ { 0x0000088C, 0x0000 }, /* R2188 - EQ2MIX Input 3 Source */
+ { 0x0000088D, 0x0080 }, /* R2189 - EQ2MIX Input 3 Volume */
+ { 0x0000088E, 0x0000 }, /* R2190 - EQ2MIX Input 4 Source */
+ { 0x0000088F, 0x0080 }, /* R2191 - EQ2MIX Input 4 Volume */
+ { 0x00000890, 0x0000 }, /* R2192 - EQ3MIX Input 1 Source */
+ { 0x00000891, 0x0080 }, /* R2193 - EQ3MIX Input 1 Volume */
+ { 0x00000892, 0x0000 }, /* R2194 - EQ3MIX Input 2 Source */
+ { 0x00000893, 0x0080 }, /* R2195 - EQ3MIX Input 2 Volume */
+ { 0x00000894, 0x0000 }, /* R2196 - EQ3MIX Input 3 Source */
+ { 0x00000895, 0x0080 }, /* R2197 - EQ3MIX Input 3 Volume */
+ { 0x00000896, 0x0000 }, /* R2198 - EQ3MIX Input 4 Source */
+ { 0x00000897, 0x0080 }, /* R2199 - EQ3MIX Input 4 Volume */
+ { 0x00000898, 0x0000 }, /* R2200 - EQ4MIX Input 1 Source */
+ { 0x00000899, 0x0080 }, /* R2201 - EQ4MIX Input 1 Volume */
+ { 0x0000089A, 0x0000 }, /* R2202 - EQ4MIX Input 2 Source */
+ { 0x0000089B, 0x0080 }, /* R2203 - EQ4MIX Input 2 Volume */
+ { 0x0000089C, 0x0000 }, /* R2204 - EQ4MIX Input 3 Source */
+ { 0x0000089D, 0x0080 }, /* R2205 - EQ4MIX Input 3 Volume */
+ { 0x0000089E, 0x0000 }, /* R2206 - EQ4MIX Input 4 Source */
+ { 0x0000089F, 0x0080 }, /* R2207 - EQ4MIX Input 4 Volume */
+ { 0x000008C0, 0x0000 }, /* R2240 - DRC1LMIX Input 1 Source */
+ { 0x000008C1, 0x0080 }, /* R2241 - DRC1LMIX Input 1 Volume */
+ { 0x000008C2, 0x0000 }, /* R2242 - DRC1LMIX Input 2 Source */
+ { 0x000008C3, 0x0080 }, /* R2243 - DRC1LMIX Input 2 Volume */
+ { 0x000008C4, 0x0000 }, /* R2244 - DRC1LMIX Input 3 Source */
+ { 0x000008C5, 0x0080 }, /* R2245 - DRC1LMIX Input 3 Volume */
+ { 0x000008C6, 0x0000 }, /* R2246 - DRC1LMIX Input 4 Source */
+ { 0x000008C7, 0x0080 }, /* R2247 - DRC1LMIX Input 4 Volume */
+ { 0x000008C8, 0x0000 }, /* R2248 - DRC1RMIX Input 1 Source */
+ { 0x000008C9, 0x0080 }, /* R2249 - DRC1RMIX Input 1 Volume */
+ { 0x000008CA, 0x0000 }, /* R2250 - DRC1RMIX Input 2 Source */
+ { 0x000008CB, 0x0080 }, /* R2251 - DRC1RMIX Input 2 Volume */
+ { 0x000008CC, 0x0000 }, /* R2252 - DRC1RMIX Input 3 Source */
+ { 0x000008CD, 0x0080 }, /* R2253 - DRC1RMIX Input 3 Volume */
+ { 0x000008CE, 0x0000 }, /* R2254 - DRC1RMIX Input 4 Source */
+ { 0x000008CF, 0x0080 }, /* R2255 - DRC1RMIX Input 4 Volume */
+ { 0x00000900, 0x0000 }, /* R2304 - HPLP1MIX Input 1 Source */
+ { 0x00000901, 0x0080 }, /* R2305 - HPLP1MIX Input 1 Volume */
+ { 0x00000902, 0x0000 }, /* R2306 - HPLP1MIX Input 2 Source */
+ { 0x00000903, 0x0080 }, /* R2307 - HPLP1MIX Input 2 Volume */
+ { 0x00000904, 0x0000 }, /* R2308 - HPLP1MIX Input 3 Source */
+ { 0x00000905, 0x0080 }, /* R2309 - HPLP1MIX Input 3 Volume */
+ { 0x00000906, 0x0000 }, /* R2310 - HPLP1MIX Input 4 Source */
+ { 0x00000907, 0x0080 }, /* R2311 - HPLP1MIX Input 4 Volume */
+ { 0x00000908, 0x0000 }, /* R2312 - HPLP2MIX Input 1 Source */
+ { 0x00000909, 0x0080 }, /* R2313 - HPLP2MIX Input 1 Volume */
+ { 0x0000090A, 0x0000 }, /* R2314 - HPLP2MIX Input 2 Source */
+ { 0x0000090B, 0x0080 }, /* R2315 - HPLP2MIX Input 2 Volume */
+ { 0x0000090C, 0x0000 }, /* R2316 - HPLP2MIX Input 3 Source */
+ { 0x0000090D, 0x0080 }, /* R2317 - HPLP2MIX Input 3 Volume */
+ { 0x0000090E, 0x0000 }, /* R2318 - HPLP2MIX Input 4 Source */
+ { 0x0000090F, 0x0080 }, /* R2319 - HPLP2MIX Input 4 Volume */
+ { 0x00000910, 0x0000 }, /* R2320 - HPLP3MIX Input 1 Source */
+ { 0x00000911, 0x0080 }, /* R2321 - HPLP3MIX Input 1 Volume */
+ { 0x00000912, 0x0000 }, /* R2322 - HPLP3MIX Input 2 Source */
+ { 0x00000913, 0x0080 }, /* R2323 - HPLP3MIX Input 2 Volume */
+ { 0x00000914, 0x0000 }, /* R2324 - HPLP3MIX Input 3 Source */
+ { 0x00000915, 0x0080 }, /* R2325 - HPLP3MIX Input 3 Volume */
+ { 0x00000916, 0x0000 }, /* R2326 - HPLP3MIX Input 4 Source */
+ { 0x00000917, 0x0080 }, /* R2327 - HPLP3MIX Input 4 Volume */
+ { 0x00000918, 0x0000 }, /* R2328 - HPLP4MIX Input 1 Source */
+ { 0x00000919, 0x0080 }, /* R2329 - HPLP4MIX Input 1 Volume */
+ { 0x0000091A, 0x0000 }, /* R2330 - HPLP4MIX Input 2 Source */
+ { 0x0000091B, 0x0080 }, /* R2331 - HPLP4MIX Input 2 Volume */
+ { 0x0000091C, 0x0000 }, /* R2332 - HPLP4MIX Input 3 Source */
+ { 0x0000091D, 0x0080 }, /* R2333 - HPLP4MIX Input 3 Volume */
+ { 0x0000091E, 0x0000 }, /* R2334 - HPLP4MIX Input 4 Source */
+ { 0x0000091F, 0x0080 }, /* R2335 - HPLP4MIX Input 4 Volume */
+ { 0x00000B00, 0x0000 }, /* R2816 - ISRC1DEC1MIX Input 1 Source */
+ { 0x00000B08, 0x0000 }, /* R2824 - ISRC1DEC2MIX Input 1 Source */
+ { 0x00000B20, 0x0000 }, /* R2848 - ISRC1INT1MIX Input 1 Source */
+ { 0x00000B28, 0x0000 }, /* R2856 - ISRC1INT2MIX Input 1 Source */
+ { 0x00000B40, 0x0000 }, /* R2880 - ISRC2DEC1MIX Input 1 Source */
+ { 0x00000B48, 0x0000 }, /* R2888 - ISRC2DEC2MIX Input 1 Source */
+ { 0x00000B60, 0x0000 }, /* R2912 - ISRC2INT1MIX Input 1 Source */
+ { 0x00000B68, 0x0000 }, /* R2920 - ISRC2INT2MIX Input 1 Source */
+ { 0x00000C00, 0xA101 }, /* R3072 - GPIO1 CTRL */
+ { 0x00000C01, 0xA101 }, /* R3073 - GPIO2 CTRL */
+ { 0x00000C02, 0xA101 }, /* R3074 - GPIO3 CTRL */
+ { 0x00000C03, 0xA101 }, /* R3075 - GPIO4 CTRL */
+ { 0x00000C04, 0xA101 }, /* R3076 - GPIO5 CTRL */
+ { 0x00000C0F, 0x0400 }, /* R3087 - IRQ CTRL 1 */
+ { 0x00000C10, 0x1000 }, /* R3088 - GPIO Debounce Config */
+ { 0x00000C20, 0x8002 }, /* R3104 - Misc Pad Ctrl 1 */
+ { 0x00000C21, 0x0001 }, /* R3105 - Misc Pad Ctrl 2 */
+ { 0x00000C22, 0x0000 }, /* R3106 - Misc Pad Ctrl 3 */
+ { 0x00000C23, 0x0000 }, /* R3107 - Misc Pad Ctrl 4 */
+ { 0x00000C24, 0x0000 }, /* R3108 - Misc Pad Ctrl 5 */
+ { 0x00000D08, 0xFFFF }, /* R3336 - Interrupt Status 1 Mask */
+ { 0x00000D0A, 0xFFFF }, /* R3338 - Interrupt Status 3 Mask */
+ { 0x00000D0B, 0xFFFF }, /* R3339 - Interrupt Status 4 Mask */
+ { 0x00000D0C, 0xFEFF }, /* R3340 - Interrupt Status 5 Mask */
+ { 0x00000D0F, 0x0000 }, /* R3343 - Interrupt Control */
+ { 0x00000D18, 0xFFFF }, /* R3352 - IRQ2 Status 1 Mask */
+ { 0x00000D1A, 0xFFFF }, /* R3354 - IRQ2 Status 3 Mask */
+ { 0x00000D1B, 0xFFFF }, /* R3355 - IRQ2 Status 4 Mask */
+ { 0x00000D1C, 0xFFFF }, /* R3356 - IRQ2 Status 5 Mask */
+ { 0x00000D1F, 0x0000 }, /* R3359 - IRQ2 Control */
+ { 0x00000D53, 0xFFFF }, /* R3411 - AOD IRQ Mask IRQ1 */
+ { 0x00000D54, 0xFFFF }, /* R3412 - AOD IRQ Mask IRQ2 */
+ { 0x00000D56, 0x0000 }, /* R3414 - Jack detect debounce */
+ { 0x00000E00, 0x0000 }, /* R3584 - FX_Ctrl1 */
+ { 0x00000E01, 0x0000 }, /* R3585 - FX_Ctrl2 */
+ { 0x00000E10, 0x6318 }, /* R3600 - EQ1_1 */
+ { 0x00000E11, 0x6300 }, /* R3601 - EQ1_2 */
+ { 0x00000E12, 0x0FC8 }, /* R3602 - EQ1_3 */
+ { 0x00000E13, 0x03FE }, /* R3603 - EQ1_4 */
+ { 0x00000E14, 0x00E0 }, /* R3604 - EQ1_5 */
+ { 0x00000E15, 0x1EC4 }, /* R3605 - EQ1_6 */
+ { 0x00000E16, 0xF136 }, /* R3606 - EQ1_7 */
+ { 0x00000E17, 0x0409 }, /* R3607 - EQ1_8 */
+ { 0x00000E18, 0x04CC }, /* R3608 - EQ1_9 */
+ { 0x00000E19, 0x1C9B }, /* R3609 - EQ1_10 */
+ { 0x00000E1A, 0xF337 }, /* R3610 - EQ1_11 */
+ { 0x00000E1B, 0x040B }, /* R3611 - EQ1_12 */
+ { 0x00000E1C, 0x0CBB }, /* R3612 - EQ1_13 */
+ { 0x00000E1D, 0x16F8 }, /* R3613 - EQ1_14 */
+ { 0x00000E1E, 0xF7D9 }, /* R3614 - EQ1_15 */
+ { 0x00000E1F, 0x040A }, /* R3615 - EQ1_16 */
+ { 0x00000E20, 0x1F14 }, /* R3616 - EQ1_17 */
+ { 0x00000E21, 0x058C }, /* R3617 - EQ1_18 */
+ { 0x00000E22, 0x0563 }, /* R3618 - EQ1_19 */
+ { 0x00000E23, 0x4000 }, /* R3619 - EQ1_20 */
+ { 0x00000E24, 0x0B75 }, /* R3620 - EQ1_21 */
+ { 0x00000E26, 0x6318 }, /* R3622 - EQ2_1 */
+ { 0x00000E27, 0x6300 }, /* R3623 - EQ2_2 */
+ { 0x00000E28, 0x0FC8 }, /* R3624 - EQ2_3 */
+ { 0x00000E29, 0x03FE }, /* R3625 - EQ2_4 */
+ { 0x00000E2A, 0x00E0 }, /* R3626 - EQ2_5 */
+ { 0x00000E2B, 0x1EC4 }, /* R3627 - EQ2_6 */
+ { 0x00000E2C, 0xF136 }, /* R3628 - EQ2_7 */
+ { 0x00000E2D, 0x0409 }, /* R3629 - EQ2_8 */
+ { 0x00000E2E, 0x04CC }, /* R3630 - EQ2_9 */
+ { 0x00000E2F, 0x1C9B }, /* R3631 - EQ2_10 */
+ { 0x00000E30, 0xF337 }, /* R3632 - EQ2_11 */
+ { 0x00000E31, 0x040B }, /* R3633 - EQ2_12 */
+ { 0x00000E32, 0x0CBB }, /* R3634 - EQ2_13 */
+ { 0x00000E33, 0x16F8 }, /* R3635 - EQ2_14 */
+ { 0x00000E34, 0xF7D9 }, /* R3636 - EQ2_15 */
+ { 0x00000E35, 0x040A }, /* R3637 - EQ2_16 */
+ { 0x00000E36, 0x1F14 }, /* R3638 - EQ2_17 */
+ { 0x00000E37, 0x058C }, /* R3639 - EQ2_18 */
+ { 0x00000E38, 0x0563 }, /* R3640 - EQ2_19 */
+ { 0x00000E39, 0x4000 }, /* R3641 - EQ2_20 */
+ { 0x00000E3A, 0x0B75 }, /* R3642 - EQ2_21 */
+ { 0x00000E3C, 0x6318 }, /* R3644 - EQ3_1 */
+ { 0x00000E3D, 0x6300 }, /* R3645 - EQ3_2 */
+ { 0x00000E3E, 0x0FC8 }, /* R3646 - EQ3_3 */
+ { 0x00000E3F, 0x03FE }, /* R3647 - EQ3_4 */
+ { 0x00000E40, 0x00E0 }, /* R3648 - EQ3_5 */
+ { 0x00000E41, 0x1EC4 }, /* R3649 - EQ3_6 */
+ { 0x00000E42, 0xF136 }, /* R3650 - EQ3_7 */
+ { 0x00000E43, 0x0409 }, /* R3651 - EQ3_8 */
+ { 0x00000E44, 0x04CC }, /* R3652 - EQ3_9 */
+ { 0x00000E45, 0x1C9B }, /* R3653 - EQ3_10 */
+ { 0x00000E46, 0xF337 }, /* R3654 - EQ3_11 */
+ { 0x00000E47, 0x040B }, /* R3655 - EQ3_12 */
+ { 0x00000E48, 0x0CBB }, /* R3656 - EQ3_13 */
+ { 0x00000E49, 0x16F8 }, /* R3657 - EQ3_14 */
+ { 0x00000E4A, 0xF7D9 }, /* R3658 - EQ3_15 */
+ { 0x00000E4B, 0x040A }, /* R3659 - EQ3_16 */
+ { 0x00000E4C, 0x1F14 }, /* R3660 - EQ3_17 */
+ { 0x00000E4D, 0x058C }, /* R3661 - EQ3_18 */
+ { 0x00000E4E, 0x0563 }, /* R3662 - EQ3_19 */
+ { 0x00000E4F, 0x4000 }, /* R3663 - EQ3_20 */
+ { 0x00000E50, 0x0B75 }, /* R3664 - EQ3_21 */
+ { 0x00000E52, 0x6318 }, /* R3666 - EQ4_1 */
+ { 0x00000E53, 0x6300 }, /* R3667 - EQ4_2 */
+ { 0x00000E54, 0x0FC8 }, /* R3668 - EQ4_3 */
+ { 0x00000E55, 0x03FE }, /* R3669 - EQ4_4 */
+ { 0x00000E56, 0x00E0 }, /* R3670 - EQ4_5 */
+ { 0x00000E57, 0x1EC4 }, /* R3671 - EQ4_6 */
+ { 0x00000E58, 0xF136 }, /* R3672 - EQ4_7 */
+ { 0x00000E59, 0x0409 }, /* R3673 - EQ4_8 */
+ { 0x00000E5A, 0x04CC }, /* R3674 - EQ4_9 */
+ { 0x00000E5B, 0x1C9B }, /* R3675 - EQ4_10 */
+ { 0x00000E5C, 0xF337 }, /* R3676 - EQ4_11 */
+ { 0x00000E5D, 0x040B }, /* R3677 - EQ4_12 */
+ { 0x00000E5E, 0x0CBB }, /* R3678 - EQ4_13 */
+ { 0x00000E5F, 0x16F8 }, /* R3679 - EQ4_14 */
+ { 0x00000E60, 0xF7D9 }, /* R3680 - EQ4_15 */
+ { 0x00000E61, 0x040A }, /* R3681 - EQ4_16 */
+ { 0x00000E62, 0x1F14 }, /* R3682 - EQ4_17 */
+ { 0x00000E63, 0x058C }, /* R3683 - EQ4_18 */
+ { 0x00000E64, 0x0563 }, /* R3684 - EQ4_19 */
+ { 0x00000E65, 0x4000 }, /* R3685 - EQ4_20 */
+ { 0x00000E66, 0x0B75 }, /* R3686 - EQ4_21 */
+ { 0x00000E80, 0x0018 }, /* R3712 - DRC1 ctrl1 */
+ { 0x00000E81, 0x0933 }, /* R3713 - DRC1 ctrl2 */
+ { 0x00000E82, 0x0018 }, /* R3714 - DRC1 ctrl3 */
+ { 0x00000E83, 0x0000 }, /* R3715 - DRC1 ctrl4 */
+ { 0x00000E84, 0x0000 }, /* R3716 - DRC1 ctrl5 */
+ { 0x00000EC0, 0x0000 }, /* R3776 - HPLPF1_1 */
+ { 0x00000EC1, 0x0000 }, /* R3777 - HPLPF1_2 */
+ { 0x00000EC4, 0x0000 }, /* R3780 - HPLPF2_1 */
+ { 0x00000EC5, 0x0000 }, /* R3781 - HPLPF2_2 */
+ { 0x00000EC8, 0x0000 }, /* R3784 - HPLPF3_1 */
+ { 0x00000EC9, 0x0000 }, /* R3785 - HPLPF3_2 */
+ { 0x00000ECC, 0x0000 }, /* R3788 - HPLPF4_1 */
+ { 0x00000ECD, 0x0000 }, /* R3789 - HPLPF4_2 */
+ { 0x00000EF0, 0x0000 }, /* R3824 - ISRC 1 CTRL 1 */
+ { 0x00000EF1, 0x0000 }, /* R3825 - ISRC 1 CTRL 2 */
+ { 0x00000EF2, 0x0000 }, /* R3826 - ISRC 1 CTRL 3 */
+ { 0x00000EF3, 0x0000 }, /* R3827 - ISRC 2 CTRL 1 */
+ { 0x00000EF4, 0x0000 }, /* R3828 - ISRC 2 CTRL 2 */
+ { 0x00000EF5, 0x0000 }, /* R3829 - ISRC 2 CTRL 3 */
+ { 0x00001100, 0x0010 }, /* R4352 - DSP1 Control 1 */
+ { 0x00001101, 0x0000 }, /* R4353 - DSP1 Clocking 1 */
+};
+
+static bool wm8997_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ARIZONA_SOFTWARE_RESET:
+ case ARIZONA_DEVICE_REVISION:
+ case ARIZONA_CTRL_IF_I2C1_CFG_1:
+ case ARIZONA_WRITE_SEQUENCER_CTRL_0:
+ case ARIZONA_WRITE_SEQUENCER_CTRL_1:
+ case ARIZONA_WRITE_SEQUENCER_CTRL_2:
+ case ARIZONA_TONE_GENERATOR_1:
+ case ARIZONA_TONE_GENERATOR_2:
+ case ARIZONA_TONE_GENERATOR_3:
+ case ARIZONA_TONE_GENERATOR_4:
+ case ARIZONA_TONE_GENERATOR_5:
+ case ARIZONA_PWM_DRIVE_1:
+ case ARIZONA_PWM_DRIVE_2:
+ case ARIZONA_PWM_DRIVE_3:
+ case ARIZONA_WAKE_CONTROL:
+ case ARIZONA_SEQUENCE_CONTROL:
+ case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_1:
+ case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2:
+ case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3:
+ case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6:
+ case ARIZONA_COMFORT_NOISE_GENERATOR:
+ case ARIZONA_HAPTICS_CONTROL_1:
+ case ARIZONA_HAPTICS_CONTROL_2:
+ case ARIZONA_HAPTICS_PHASE_1_INTENSITY:
+ case ARIZONA_HAPTICS_PHASE_1_DURATION:
+ case ARIZONA_HAPTICS_PHASE_2_INTENSITY:
+ case ARIZONA_HAPTICS_PHASE_2_DURATION:
+ case ARIZONA_HAPTICS_PHASE_3_INTENSITY:
+ case ARIZONA_HAPTICS_PHASE_3_DURATION:
+ case ARIZONA_HAPTICS_STATUS:
+ case ARIZONA_CLOCK_32K_1:
+ case ARIZONA_SYSTEM_CLOCK_1:
+ case ARIZONA_SAMPLE_RATE_1:
+ case ARIZONA_SAMPLE_RATE_2:
+ case ARIZONA_SAMPLE_RATE_3:
+ case ARIZONA_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_SAMPLE_RATE_2_STATUS:
+ case ARIZONA_SAMPLE_RATE_3_STATUS:
+ case ARIZONA_ASYNC_CLOCK_1:
+ case ARIZONA_ASYNC_SAMPLE_RATE_1:
+ case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_OUTPUT_SYSTEM_CLOCK:
+ case ARIZONA_OUTPUT_ASYNC_CLOCK:
+ case ARIZONA_RATE_ESTIMATOR_1:
+ case ARIZONA_RATE_ESTIMATOR_2:
+ case ARIZONA_RATE_ESTIMATOR_3:
+ case ARIZONA_RATE_ESTIMATOR_4:
+ case ARIZONA_RATE_ESTIMATOR_5:
+ case ARIZONA_DYNAMIC_FREQUENCY_SCALING_1:
+ case ARIZONA_FLL1_CONTROL_1:
+ case ARIZONA_FLL1_CONTROL_2:
+ case ARIZONA_FLL1_CONTROL_3:
+ case ARIZONA_FLL1_CONTROL_4:
+ case ARIZONA_FLL1_CONTROL_5:
+ case ARIZONA_FLL1_CONTROL_6:
+ case ARIZONA_FLL1_LOOP_FILTER_TEST_1:
+ case ARIZONA_FLL1_NCO_TEST_0:
+ case ARIZONA_FLL1_SYNCHRONISER_1:
+ case ARIZONA_FLL1_SYNCHRONISER_2:
+ case ARIZONA_FLL1_SYNCHRONISER_3:
+ case ARIZONA_FLL1_SYNCHRONISER_4:
+ case ARIZONA_FLL1_SYNCHRONISER_5:
+ case ARIZONA_FLL1_SYNCHRONISER_6:
+ case ARIZONA_FLL1_SPREAD_SPECTRUM:
+ case ARIZONA_FLL1_GPIO_CLOCK:
+ case ARIZONA_FLL2_CONTROL_1:
+ case ARIZONA_FLL2_CONTROL_2:
+ case ARIZONA_FLL2_CONTROL_3:
+ case ARIZONA_FLL2_CONTROL_4:
+ case ARIZONA_FLL2_CONTROL_5:
+ case ARIZONA_FLL2_CONTROL_6:
+ case ARIZONA_FLL2_LOOP_FILTER_TEST_1:
+ case ARIZONA_FLL2_NCO_TEST_0:
+ case ARIZONA_FLL2_SYNCHRONISER_1:
+ case ARIZONA_FLL2_SYNCHRONISER_2:
+ case ARIZONA_FLL2_SYNCHRONISER_3:
+ case ARIZONA_FLL2_SYNCHRONISER_4:
+ case ARIZONA_FLL2_SYNCHRONISER_5:
+ case ARIZONA_FLL2_SYNCHRONISER_6:
+ case ARIZONA_FLL2_SPREAD_SPECTRUM:
+ case ARIZONA_FLL2_GPIO_CLOCK:
+ case ARIZONA_MIC_CHARGE_PUMP_1:
+ case ARIZONA_LDO1_CONTROL_1:
+ case ARIZONA_LDO1_CONTROL_2:
+ case ARIZONA_LDO2_CONTROL_1:
+ case ARIZONA_MIC_BIAS_CTRL_1:
+ case ARIZONA_MIC_BIAS_CTRL_2:
+ case ARIZONA_MIC_BIAS_CTRL_3:
+ case ARIZONA_ACCESSORY_DETECT_MODE_1:
+ case ARIZONA_HEADPHONE_DETECT_1:
+ case ARIZONA_HEADPHONE_DETECT_2:
+ case ARIZONA_MIC_DETECT_1:
+ case ARIZONA_MIC_DETECT_2:
+ case ARIZONA_MIC_DETECT_3:
+ case ARIZONA_MIC_NOISE_MIX_CONTROL_1:
+ case ARIZONA_ISOLATION_CONTROL:
+ case ARIZONA_JACK_DETECT_ANALOGUE:
+ case ARIZONA_INPUT_ENABLES:
+ case ARIZONA_INPUT_ENABLES_STATUS:
+ case ARIZONA_INPUT_RATE:
+ case ARIZONA_INPUT_VOLUME_RAMP:
+ case ARIZONA_IN1L_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_1L:
+ case ARIZONA_DMIC1L_CONTROL:
+ case ARIZONA_IN1R_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_1R:
+ case ARIZONA_DMIC1R_CONTROL:
+ case ARIZONA_IN2L_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_2L:
+ case ARIZONA_DMIC2L_CONTROL:
+ case ARIZONA_IN2R_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_2R:
+ case ARIZONA_DMIC2R_CONTROL:
+ case ARIZONA_OUTPUT_ENABLES_1:
+ case ARIZONA_OUTPUT_STATUS_1:
+ case ARIZONA_RAW_OUTPUT_STATUS_1:
+ case ARIZONA_OUTPUT_RATE_1:
+ case ARIZONA_OUTPUT_VOLUME_RAMP:
+ case ARIZONA_OUTPUT_PATH_CONFIG_1L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_1L:
+ case ARIZONA_DAC_VOLUME_LIMIT_1L:
+ case ARIZONA_NOISE_GATE_SELECT_1L:
+ case ARIZONA_OUTPUT_PATH_CONFIG_1R:
+ case ARIZONA_DAC_DIGITAL_VOLUME_1R:
+ case ARIZONA_DAC_VOLUME_LIMIT_1R:
+ case ARIZONA_NOISE_GATE_SELECT_1R:
+ case ARIZONA_OUTPUT_PATH_CONFIG_3L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_3L:
+ case ARIZONA_DAC_VOLUME_LIMIT_3L:
+ case ARIZONA_NOISE_GATE_SELECT_3L:
+ case ARIZONA_OUTPUT_PATH_CONFIG_4L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_4L:
+ case ARIZONA_OUT_VOLUME_4L:
+ case ARIZONA_NOISE_GATE_SELECT_4L:
+ case ARIZONA_OUTPUT_PATH_CONFIG_5L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_5L:
+ case ARIZONA_DAC_VOLUME_LIMIT_5L:
+ case ARIZONA_NOISE_GATE_SELECT_5L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_5R:
+ case ARIZONA_DAC_VOLUME_LIMIT_5R:
+ case ARIZONA_NOISE_GATE_SELECT_5R:
+ case ARIZONA_DAC_AEC_CONTROL_1:
+ case ARIZONA_NOISE_GATE_CONTROL:
+ case ARIZONA_PDM_SPK1_CTRL_1:
+ case ARIZONA_PDM_SPK1_CTRL_2:
+ case ARIZONA_AIF1_BCLK_CTRL:
+ case ARIZONA_AIF1_TX_PIN_CTRL:
+ case ARIZONA_AIF1_RX_PIN_CTRL:
+ case ARIZONA_AIF1_RATE_CTRL:
+ case ARIZONA_AIF1_FORMAT:
+ case ARIZONA_AIF1_TX_BCLK_RATE:
+ case ARIZONA_AIF1_RX_BCLK_RATE:
+ case ARIZONA_AIF1_FRAME_CTRL_1:
+ case ARIZONA_AIF1_FRAME_CTRL_2:
+ case ARIZONA_AIF1_FRAME_CTRL_3:
+ case ARIZONA_AIF1_FRAME_CTRL_4:
+ case ARIZONA_AIF1_FRAME_CTRL_5:
+ case ARIZONA_AIF1_FRAME_CTRL_6:
+ case ARIZONA_AIF1_FRAME_CTRL_7:
+ case ARIZONA_AIF1_FRAME_CTRL_8:
+ case ARIZONA_AIF1_FRAME_CTRL_9:
+ case ARIZONA_AIF1_FRAME_CTRL_10:
+ case ARIZONA_AIF1_FRAME_CTRL_11:
+ case ARIZONA_AIF1_FRAME_CTRL_12:
+ case ARIZONA_AIF1_FRAME_CTRL_13:
+ case ARIZONA_AIF1_FRAME_CTRL_14:
+ case ARIZONA_AIF1_FRAME_CTRL_15:
+ case ARIZONA_AIF1_FRAME_CTRL_16:
+ case ARIZONA_AIF1_FRAME_CTRL_17:
+ case ARIZONA_AIF1_FRAME_CTRL_18:
+ case ARIZONA_AIF1_TX_ENABLES:
+ case ARIZONA_AIF1_RX_ENABLES:
+ case ARIZONA_AIF2_BCLK_CTRL:
+ case ARIZONA_AIF2_TX_PIN_CTRL:
+ case ARIZONA_AIF2_RX_PIN_CTRL:
+ case ARIZONA_AIF2_RATE_CTRL:
+ case ARIZONA_AIF2_FORMAT:
+ case ARIZONA_AIF2_TX_BCLK_RATE:
+ case ARIZONA_AIF2_RX_BCLK_RATE:
+ case ARIZONA_AIF2_FRAME_CTRL_1:
+ case ARIZONA_AIF2_FRAME_CTRL_2:
+ case ARIZONA_AIF2_FRAME_CTRL_3:
+ case ARIZONA_AIF2_FRAME_CTRL_4:
+ case ARIZONA_AIF2_FRAME_CTRL_11:
+ case ARIZONA_AIF2_FRAME_CTRL_12:
+ case ARIZONA_AIF2_TX_ENABLES:
+ case ARIZONA_AIF2_RX_ENABLES:
+ case ARIZONA_SLIMBUS_FRAMER_REF_GEAR:
+ case ARIZONA_SLIMBUS_RATES_1:
+ case ARIZONA_SLIMBUS_RATES_2:
+ case ARIZONA_SLIMBUS_RATES_3:
+ case ARIZONA_SLIMBUS_RATES_4:
+ case ARIZONA_SLIMBUS_RATES_5:
+ case ARIZONA_SLIMBUS_RATES_6:
+ case ARIZONA_SLIMBUS_RATES_7:
+ case ARIZONA_SLIMBUS_RATES_8:
+ case ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE:
+ case ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE:
+ case ARIZONA_SLIMBUS_RX_PORT_STATUS:
+ case ARIZONA_SLIMBUS_TX_PORT_STATUS:
+ case ARIZONA_PWM1MIX_INPUT_1_SOURCE:
+ case ARIZONA_PWM1MIX_INPUT_1_VOLUME:
+ case ARIZONA_PWM1MIX_INPUT_2_SOURCE:
+ case ARIZONA_PWM1MIX_INPUT_2_VOLUME:
+ case ARIZONA_PWM1MIX_INPUT_3_SOURCE:
+ case ARIZONA_PWM1MIX_INPUT_3_VOLUME:
+ case ARIZONA_PWM1MIX_INPUT_4_SOURCE:
+ case ARIZONA_PWM1MIX_INPUT_4_VOLUME:
+ case ARIZONA_PWM2MIX_INPUT_1_SOURCE:
+ case ARIZONA_PWM2MIX_INPUT_1_VOLUME:
+ case ARIZONA_PWM2MIX_INPUT_2_SOURCE:
+ case ARIZONA_PWM2MIX_INPUT_2_VOLUME:
+ case ARIZONA_PWM2MIX_INPUT_3_SOURCE:
+ case ARIZONA_PWM2MIX_INPUT_3_VOLUME:
+ case ARIZONA_PWM2MIX_INPUT_4_SOURCE:
+ case ARIZONA_PWM2MIX_INPUT_4_VOLUME:
+ case ARIZONA_MICMIX_INPUT_1_SOURCE:
+ case ARIZONA_MICMIX_INPUT_1_VOLUME:
+ case ARIZONA_MICMIX_INPUT_2_SOURCE:
+ case ARIZONA_MICMIX_INPUT_2_VOLUME:
+ case ARIZONA_MICMIX_INPUT_3_SOURCE:
+ case ARIZONA_MICMIX_INPUT_3_VOLUME:
+ case ARIZONA_MICMIX_INPUT_4_SOURCE:
+ case ARIZONA_MICMIX_INPUT_4_VOLUME:
+ case ARIZONA_NOISEMIX_INPUT_1_SOURCE:
+ case ARIZONA_NOISEMIX_INPUT_1_VOLUME:
+ case ARIZONA_NOISEMIX_INPUT_2_SOURCE:
+ case ARIZONA_NOISEMIX_INPUT_2_VOLUME:
+ case ARIZONA_NOISEMIX_INPUT_3_SOURCE:
+ case ARIZONA_NOISEMIX_INPUT_3_VOLUME:
+ case ARIZONA_NOISEMIX_INPUT_4_SOURCE:
+ case ARIZONA_NOISEMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT1LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT1LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT1LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT1LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT1LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT1LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT1LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT1LMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT1RMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT1RMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT1RMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT1RMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT1RMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT1RMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT1RMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT1RMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT3LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT3LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT3LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT3LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT3LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT3LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT3LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT3LMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT4LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT4LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT4LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT4LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT4LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT4LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT4LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT4LMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT5LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT5LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT5LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT5LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT5LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT5LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT5LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT5LMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT5RMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT5RMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT5RMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT5RMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT5RMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT5RMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT5RMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT5RMIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX1MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX1MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX1MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX1MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX1MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX1MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX1MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX2MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX2MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX2MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX2MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX2MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX2MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX2MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX3MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX3MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX3MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX3MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX3MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX3MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX3MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX4MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX4MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX4MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX4MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX4MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX4MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX4MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX5MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX5MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX5MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX5MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX5MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX5MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX5MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX6MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX6MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX6MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX6MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX6MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX6MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX6MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX7MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX7MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX7MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX7MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX7MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX7MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX7MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX8MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX8MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX8MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX8MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX8MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX8MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX8MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF2TX1MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF2TX1MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF2TX1MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF2TX1MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF2TX1MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF2TX1MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF2TX1MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF2TX2MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF2TX2MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF2TX2MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF2TX2MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF2TX2MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF2TX2MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF2TX2MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX1MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX1MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX1MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX1MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX1MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX1MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX1MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX2MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX2MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX2MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX2MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX2MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX2MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX2MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX3MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX3MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX3MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX3MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX3MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX3MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX3MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX4MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX4MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX4MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX4MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX4MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX4MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX4MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX5MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX5MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX5MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX5MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX5MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX5MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX5MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX6MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX6MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX6MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX6MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX6MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX6MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX6MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX7MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX7MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX7MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX7MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX7MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX7MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX7MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX8MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX8MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX8MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX8MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX8MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX8MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX8MIX_INPUT_4_VOLUME:
+ case ARIZONA_EQ1MIX_INPUT_1_SOURCE:
+ case ARIZONA_EQ1MIX_INPUT_1_VOLUME:
+ case ARIZONA_EQ1MIX_INPUT_2_SOURCE:
+ case ARIZONA_EQ1MIX_INPUT_2_VOLUME:
+ case ARIZONA_EQ1MIX_INPUT_3_SOURCE:
+ case ARIZONA_EQ1MIX_INPUT_3_VOLUME:
+ case ARIZONA_EQ1MIX_INPUT_4_SOURCE:
+ case ARIZONA_EQ1MIX_INPUT_4_VOLUME:
+ case ARIZONA_EQ2MIX_INPUT_1_SOURCE:
+ case ARIZONA_EQ2MIX_INPUT_1_VOLUME:
+ case ARIZONA_EQ2MIX_INPUT_2_SOURCE:
+ case ARIZONA_EQ2MIX_INPUT_2_VOLUME:
+ case ARIZONA_EQ2MIX_INPUT_3_SOURCE:
+ case ARIZONA_EQ2MIX_INPUT_3_VOLUME:
+ case ARIZONA_EQ2MIX_INPUT_4_SOURCE:
+ case ARIZONA_EQ2MIX_INPUT_4_VOLUME:
+ case ARIZONA_EQ3MIX_INPUT_1_SOURCE:
+ case ARIZONA_EQ3MIX_INPUT_1_VOLUME:
+ case ARIZONA_EQ3MIX_INPUT_2_SOURCE:
+ case ARIZONA_EQ3MIX_INPUT_2_VOLUME:
+ case ARIZONA_EQ3MIX_INPUT_3_SOURCE:
+ case ARIZONA_EQ3MIX_INPUT_3_VOLUME:
+ case ARIZONA_EQ3MIX_INPUT_4_SOURCE:
+ case ARIZONA_EQ3MIX_INPUT_4_VOLUME:
+ case ARIZONA_EQ4MIX_INPUT_1_SOURCE:
+ case ARIZONA_EQ4MIX_INPUT_1_VOLUME:
+ case ARIZONA_EQ4MIX_INPUT_2_SOURCE:
+ case ARIZONA_EQ4MIX_INPUT_2_VOLUME:
+ case ARIZONA_EQ4MIX_INPUT_3_SOURCE:
+ case ARIZONA_EQ4MIX_INPUT_3_VOLUME:
+ case ARIZONA_EQ4MIX_INPUT_4_SOURCE:
+ case ARIZONA_EQ4MIX_INPUT_4_VOLUME:
+ case ARIZONA_DRC1LMIX_INPUT_1_SOURCE:
+ case ARIZONA_DRC1LMIX_INPUT_1_VOLUME:
+ case ARIZONA_DRC1LMIX_INPUT_2_SOURCE:
+ case ARIZONA_DRC1LMIX_INPUT_2_VOLUME:
+ case ARIZONA_DRC1LMIX_INPUT_3_SOURCE:
+ case ARIZONA_DRC1LMIX_INPUT_3_VOLUME:
+ case ARIZONA_DRC1LMIX_INPUT_4_SOURCE:
+ case ARIZONA_DRC1LMIX_INPUT_4_VOLUME:
+ case ARIZONA_DRC1RMIX_INPUT_1_SOURCE:
+ case ARIZONA_DRC1RMIX_INPUT_1_VOLUME:
+ case ARIZONA_DRC1RMIX_INPUT_2_SOURCE:
+ case ARIZONA_DRC1RMIX_INPUT_2_VOLUME:
+ case ARIZONA_DRC1RMIX_INPUT_3_SOURCE:
+ case ARIZONA_DRC1RMIX_INPUT_3_VOLUME:
+ case ARIZONA_DRC1RMIX_INPUT_4_SOURCE:
+ case ARIZONA_DRC1RMIX_INPUT_4_VOLUME:
+ case ARIZONA_HPLP1MIX_INPUT_1_SOURCE:
+ case ARIZONA_HPLP1MIX_INPUT_1_VOLUME:
+ case ARIZONA_HPLP1MIX_INPUT_2_SOURCE:
+ case ARIZONA_HPLP1MIX_INPUT_2_VOLUME:
+ case ARIZONA_HPLP1MIX_INPUT_3_SOURCE:
+ case ARIZONA_HPLP1MIX_INPUT_3_VOLUME:
+ case ARIZONA_HPLP1MIX_INPUT_4_SOURCE:
+ case ARIZONA_HPLP1MIX_INPUT_4_VOLUME:
+ case ARIZONA_HPLP2MIX_INPUT_1_SOURCE:
+ case ARIZONA_HPLP2MIX_INPUT_1_VOLUME:
+ case ARIZONA_HPLP2MIX_INPUT_2_SOURCE:
+ case ARIZONA_HPLP2MIX_INPUT_2_VOLUME:
+ case ARIZONA_HPLP2MIX_INPUT_3_SOURCE:
+ case ARIZONA_HPLP2MIX_INPUT_3_VOLUME:
+ case ARIZONA_HPLP2MIX_INPUT_4_SOURCE:
+ case ARIZONA_HPLP2MIX_INPUT_4_VOLUME:
+ case ARIZONA_HPLP3MIX_INPUT_1_SOURCE:
+ case ARIZONA_HPLP3MIX_INPUT_1_VOLUME:
+ case ARIZONA_HPLP3MIX_INPUT_2_SOURCE:
+ case ARIZONA_HPLP3MIX_INPUT_2_VOLUME:
+ case ARIZONA_HPLP3MIX_INPUT_3_SOURCE:
+ case ARIZONA_HPLP3MIX_INPUT_3_VOLUME:
+ case ARIZONA_HPLP3MIX_INPUT_4_SOURCE:
+ case ARIZONA_HPLP3MIX_INPUT_4_VOLUME:
+ case ARIZONA_HPLP4MIX_INPUT_1_SOURCE:
+ case ARIZONA_HPLP4MIX_INPUT_1_VOLUME:
+ case ARIZONA_HPLP4MIX_INPUT_2_SOURCE:
+ case ARIZONA_HPLP4MIX_INPUT_2_VOLUME:
+ case ARIZONA_HPLP4MIX_INPUT_3_SOURCE:
+ case ARIZONA_HPLP4MIX_INPUT_3_VOLUME:
+ case ARIZONA_HPLP4MIX_INPUT_4_SOURCE:
+ case ARIZONA_HPLP4MIX_INPUT_4_VOLUME:
+ case ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE:
+ case ARIZONA_GPIO1_CTRL:
+ case ARIZONA_GPIO2_CTRL:
+ case ARIZONA_GPIO3_CTRL:
+ case ARIZONA_GPIO4_CTRL:
+ case ARIZONA_GPIO5_CTRL:
+ case ARIZONA_IRQ_CTRL_1:
+ case ARIZONA_GPIO_DEBOUNCE_CONFIG:
+ case ARIZONA_MISC_PAD_CTRL_1:
+ case ARIZONA_MISC_PAD_CTRL_2:
+ case ARIZONA_MISC_PAD_CTRL_3:
+ case ARIZONA_MISC_PAD_CTRL_4:
+ case ARIZONA_MISC_PAD_CTRL_5:
+ case ARIZONA_INTERRUPT_STATUS_1:
+ case ARIZONA_INTERRUPT_STATUS_2:
+ case ARIZONA_INTERRUPT_STATUS_3:
+ case ARIZONA_INTERRUPT_STATUS_4:
+ case ARIZONA_INTERRUPT_STATUS_5:
+ case ARIZONA_INTERRUPT_STATUS_1_MASK:
+ case ARIZONA_INTERRUPT_STATUS_3_MASK:
+ case ARIZONA_INTERRUPT_STATUS_4_MASK:
+ case ARIZONA_INTERRUPT_STATUS_5_MASK:
+ case ARIZONA_INTERRUPT_CONTROL:
+ case ARIZONA_IRQ2_STATUS_1:
+ case ARIZONA_IRQ2_STATUS_3:
+ case ARIZONA_IRQ2_STATUS_4:
+ case ARIZONA_IRQ2_STATUS_5:
+ case ARIZONA_IRQ2_STATUS_1_MASK:
+ case ARIZONA_IRQ2_STATUS_3_MASK:
+ case ARIZONA_IRQ2_STATUS_4_MASK:
+ case ARIZONA_IRQ2_STATUS_5_MASK:
+ case ARIZONA_IRQ2_CONTROL:
+ case ARIZONA_INTERRUPT_RAW_STATUS_3:
+ case ARIZONA_INTERRUPT_RAW_STATUS_4:
+ case ARIZONA_INTERRUPT_RAW_STATUS_5:
+ case ARIZONA_INTERRUPT_RAW_STATUS_6:
+ case ARIZONA_INTERRUPT_RAW_STATUS_7:
+ case ARIZONA_INTERRUPT_RAW_STATUS_8:
+ case ARIZONA_IRQ_PIN_STATUS:
+ case ARIZONA_AOD_WKUP_AND_TRIG:
+ case ARIZONA_AOD_IRQ1:
+ case ARIZONA_AOD_IRQ2:
+ case ARIZONA_AOD_IRQ_MASK_IRQ1:
+ case ARIZONA_AOD_IRQ_MASK_IRQ2:
+ case ARIZONA_AOD_IRQ_RAW_STATUS:
+ case ARIZONA_JACK_DETECT_DEBOUNCE:
+ case ARIZONA_FX_CTRL1:
+ case ARIZONA_FX_CTRL2:
+ case ARIZONA_EQ1_1:
+ case ARIZONA_EQ1_2:
+ case ARIZONA_EQ1_3:
+ case ARIZONA_EQ1_4:
+ case ARIZONA_EQ1_5:
+ case ARIZONA_EQ1_6:
+ case ARIZONA_EQ1_7:
+ case ARIZONA_EQ1_8:
+ case ARIZONA_EQ1_9:
+ case ARIZONA_EQ1_10:
+ case ARIZONA_EQ1_11:
+ case ARIZONA_EQ1_12:
+ case ARIZONA_EQ1_13:
+ case ARIZONA_EQ1_14:
+ case ARIZONA_EQ1_15:
+ case ARIZONA_EQ1_16:
+ case ARIZONA_EQ1_17:
+ case ARIZONA_EQ1_18:
+ case ARIZONA_EQ1_19:
+ case ARIZONA_EQ1_20:
+ case ARIZONA_EQ1_21:
+ case ARIZONA_EQ2_1:
+ case ARIZONA_EQ2_2:
+ case ARIZONA_EQ2_3:
+ case ARIZONA_EQ2_4:
+ case ARIZONA_EQ2_5:
+ case ARIZONA_EQ2_6:
+ case ARIZONA_EQ2_7:
+ case ARIZONA_EQ2_8:
+ case ARIZONA_EQ2_9:
+ case ARIZONA_EQ2_10:
+ case ARIZONA_EQ2_11:
+ case ARIZONA_EQ2_12:
+ case ARIZONA_EQ2_13:
+ case ARIZONA_EQ2_14:
+ case ARIZONA_EQ2_15:
+ case ARIZONA_EQ2_16:
+ case ARIZONA_EQ2_17:
+ case ARIZONA_EQ2_18:
+ case ARIZONA_EQ2_19:
+ case ARIZONA_EQ2_20:
+ case ARIZONA_EQ2_21:
+ case ARIZONA_EQ3_1:
+ case ARIZONA_EQ3_2:
+ case ARIZONA_EQ3_3:
+ case ARIZONA_EQ3_4:
+ case ARIZONA_EQ3_5:
+ case ARIZONA_EQ3_6:
+ case ARIZONA_EQ3_7:
+ case ARIZONA_EQ3_8:
+ case ARIZONA_EQ3_9:
+ case ARIZONA_EQ3_10:
+ case ARIZONA_EQ3_11:
+ case ARIZONA_EQ3_12:
+ case ARIZONA_EQ3_13:
+ case ARIZONA_EQ3_14:
+ case ARIZONA_EQ3_15:
+ case ARIZONA_EQ3_16:
+ case ARIZONA_EQ3_17:
+ case ARIZONA_EQ3_18:
+ case ARIZONA_EQ3_19:
+ case ARIZONA_EQ3_20:
+ case ARIZONA_EQ3_21:
+ case ARIZONA_EQ4_1:
+ case ARIZONA_EQ4_2:
+ case ARIZONA_EQ4_3:
+ case ARIZONA_EQ4_4:
+ case ARIZONA_EQ4_5:
+ case ARIZONA_EQ4_6:
+ case ARIZONA_EQ4_7:
+ case ARIZONA_EQ4_8:
+ case ARIZONA_EQ4_9:
+ case ARIZONA_EQ4_10:
+ case ARIZONA_EQ4_11:
+ case ARIZONA_EQ4_12:
+ case ARIZONA_EQ4_13:
+ case ARIZONA_EQ4_14:
+ case ARIZONA_EQ4_15:
+ case ARIZONA_EQ4_16:
+ case ARIZONA_EQ4_17:
+ case ARIZONA_EQ4_18:
+ case ARIZONA_EQ4_19:
+ case ARIZONA_EQ4_20:
+ case ARIZONA_EQ4_21:
+ case ARIZONA_DRC1_CTRL1:
+ case ARIZONA_DRC1_CTRL2:
+ case ARIZONA_DRC1_CTRL3:
+ case ARIZONA_DRC1_CTRL4:
+ case ARIZONA_DRC1_CTRL5:
+ case ARIZONA_HPLPF1_1:
+ case ARIZONA_HPLPF1_2:
+ case ARIZONA_HPLPF2_1:
+ case ARIZONA_HPLPF2_2:
+ case ARIZONA_HPLPF3_1:
+ case ARIZONA_HPLPF3_2:
+ case ARIZONA_HPLPF4_1:
+ case ARIZONA_HPLPF4_2:
+ case ARIZONA_ISRC_1_CTRL_1:
+ case ARIZONA_ISRC_1_CTRL_2:
+ case ARIZONA_ISRC_1_CTRL_3:
+ case ARIZONA_ISRC_2_CTRL_1:
+ case ARIZONA_ISRC_2_CTRL_2:
+ case ARIZONA_ISRC_2_CTRL_3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool wm8997_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ARIZONA_SOFTWARE_RESET:
+ case ARIZONA_DEVICE_REVISION:
+ case ARIZONA_HAPTICS_STATUS:
+ case ARIZONA_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_SAMPLE_RATE_2_STATUS:
+ case ARIZONA_SAMPLE_RATE_3_STATUS:
+ case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_MIC_DETECT_3:
+ case ARIZONA_HEADPHONE_DETECT_2:
+ case ARIZONA_INPUT_ENABLES_STATUS:
+ case ARIZONA_OUTPUT_STATUS_1:
+ case ARIZONA_RAW_OUTPUT_STATUS_1:
+ case ARIZONA_SLIMBUS_RX_PORT_STATUS:
+ case ARIZONA_SLIMBUS_TX_PORT_STATUS:
+ case ARIZONA_INTERRUPT_STATUS_1:
+ case ARIZONA_INTERRUPT_STATUS_2:
+ case ARIZONA_INTERRUPT_STATUS_3:
+ case ARIZONA_INTERRUPT_STATUS_4:
+ case ARIZONA_INTERRUPT_STATUS_5:
+ case ARIZONA_IRQ2_STATUS_1:
+ case ARIZONA_IRQ2_STATUS_3:
+ case ARIZONA_IRQ2_STATUS_4:
+ case ARIZONA_IRQ2_STATUS_5:
+ case ARIZONA_INTERRUPT_RAW_STATUS_3:
+ case ARIZONA_INTERRUPT_RAW_STATUS_4:
+ case ARIZONA_INTERRUPT_RAW_STATUS_5:
+ case ARIZONA_INTERRUPT_RAW_STATUS_6:
+ case ARIZONA_INTERRUPT_RAW_STATUS_7:
+ case ARIZONA_INTERRUPT_RAW_STATUS_8:
+ case ARIZONA_IRQ_PIN_STATUS:
+ case ARIZONA_AOD_WKUP_AND_TRIG:
+ case ARIZONA_AOD_IRQ1:
+ case ARIZONA_AOD_IRQ2:
+ case ARIZONA_AOD_IRQ_RAW_STATUS:
+ case ARIZONA_FX_CTRL2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#define WM8997_MAX_REGISTER 0x31ff
+
+const struct regmap_config wm8997_i2c_regmap = {
+ .reg_bits = 32,
+ .val_bits = 16,
+
+ .max_register = WM8997_MAX_REGISTER,
+ .readable_reg = wm8997_readable_register,
+ .volatile_reg = wm8997_volatile_register,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm8997_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(wm8997_reg_default),
+};
+EXPORT_SYMBOL_GPL(wm8997_i2c_regmap);