diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-26 23:14:20 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-26 23:14:20 +0400 |
commit | 9f1912c48ce829d24789e3e5d499de0d44d3306a (patch) | |
tree | 056ca04727d478f74b20d8af5729e0776a942cca /drivers | |
parent | 4c171acc20794af16a27da25e11ec4e9cad5d9fa (diff) | |
parent | 099691081df40d8863cb2fb01ee64039633892dd (diff) | |
download | linux-9f1912c48ce829d24789e3e5d499de0d44d3306a.tar.xz |
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6
* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (57 commits)
regulator: Fix 88pm8607.c printk format warning
input: Add support for Qualcomm PMIC8XXX power key
input: Add Qualcomm pm8xxx keypad controller driver
mfd: Add omap-usbhs runtime PM support
mfd: Fix ASIC3 SD Host Controller Configuration size
mfd: Fix omap_usbhs_alloc_children error handling
mfd: Fix omap usbhs crash when rmmoding ehci or ohci
mfd: Add ASIC3 LED support
leds: Add ASIC3 LED support
mfd: Update twl4030-code maintainer e-mail address
mfd: Correct the name and bitmask for ab8500-gpadc BTempPullUp
mfd: Add manual ab8500-gpadc batt temp activation for AB8500 3.0
mfd: Provide ab8500-core enumerators for chip cuts
mfd: Check twl4030-power remove script error condition after i2cwrite
mfd: Fix twl6030 irq definitions
mfd: Add phoenix lite (twl6025) support to twl6030
mfd: Avoid to use constraint name in 88pm860x regulator driver
mfd: Remove checking on max8925 regulator[0]
mfd: Remove unused parameter from 88pm860x API
mfd: Avoid to allocate 88pm860x static platform data
...
Diffstat (limited to 'drivers')
73 files changed, 2973 insertions, 471 deletions
diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index d2c75feff7df..f69f90a61873 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -27,7 +27,6 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/mfd/core.h> #include <linux/slab.h> #include <linux/timb_dma.h> @@ -685,7 +684,7 @@ static irqreturn_t td_irq(int irq, void *devid) static int __devinit td_probe(struct platform_device *pdev) { - struct timb_dma_platform_data *pdata = mfd_get_data(pdev); + struct timb_dma_platform_data *pdata = pdev->dev.platform_data; struct timb_dma *td; struct resource *iomem; int irq; diff --git a/drivers/gpio/janz-ttl.c b/drivers/gpio/janz-ttl.c index 2514fb075f4a..813ac077e5d7 100644 --- a/drivers/gpio/janz-ttl.c +++ b/drivers/gpio/janz-ttl.c @@ -15,7 +15,6 @@ #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/platform_device.h> -#include <linux/mfd/core.h> #include <linux/io.h> #include <linux/gpio.h> #include <linux/slab.h> @@ -150,7 +149,7 @@ static int __devinit ttl_probe(struct platform_device *pdev) struct resource *res; int ret; - pdata = mfd_get_data(pdev); + pdata = pdev->dev.platform_data; if (!pdata) { dev_err(dev, "no platform data\n"); ret = -ENXIO; diff --git a/drivers/gpio/rdc321x-gpio.c b/drivers/gpio/rdc321x-gpio.c index a9bda881935a..2762698e0204 100644 --- a/drivers/gpio/rdc321x-gpio.c +++ b/drivers/gpio/rdc321x-gpio.c @@ -27,7 +27,6 @@ #include <linux/pci.h> #include <linux/gpio.h> #include <linux/mfd/rdc321x.h> -#include <linux/mfd/core.h> #include <linux/slab.h> struct rdc321x_gpio { @@ -136,7 +135,7 @@ static int __devinit rdc321x_gpio_probe(struct platform_device *pdev) struct rdc321x_gpio *rdc321x_gpio_dev; struct rdc321x_gpio_pdata *pdata; - pdata = mfd_get_data(pdev); + pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "no platform data supplied\n"); return -ENODEV; diff --git a/drivers/gpio/timbgpio.c b/drivers/gpio/timbgpio.c index edbe1eae531f..0265872e57d1 100644 --- a/drivers/gpio/timbgpio.c +++ b/drivers/gpio/timbgpio.c @@ -23,7 +23,6 @@ #include <linux/module.h> #include <linux/gpio.h> #include <linux/platform_device.h> -#include <linux/mfd/core.h> #include <linux/irq.h> #include <linux/io.h> #include <linux/timb_gpio.h> @@ -229,7 +228,7 @@ static int __devinit timbgpio_probe(struct platform_device *pdev) struct gpio_chip *gc; struct timbgpio *tgpio; struct resource *iomem; - struct timbgpio_platform_data *pdata = mfd_get_data(pdev); + struct timbgpio_platform_data *pdata = pdev->dev.platform_data; int irq = platform_get_irq(pdev, 0); if (!pdata || pdata->nr_pins > 32) { @@ -320,13 +319,14 @@ err_mem: static int __devexit timbgpio_remove(struct platform_device *pdev) { int err; + struct timbgpio_platform_data *pdata = pdev->dev.platform_data; struct timbgpio *tgpio = platform_get_drvdata(pdev); struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); int irq = platform_get_irq(pdev, 0); if (irq >= 0 && tgpio->irq_base > 0) { int i; - for (i = 0; i < tgpio->gpio.ngpio; i++) { + for (i = 0; i < pdata->nr_pins; i++) { irq_set_chip(tgpio->irq_base + i, NULL); irq_set_chip_data(tgpio->irq_base + i, NULL); } diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index fee1a2613861..1b46a9d9f907 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -49,7 +49,6 @@ #include <linux/init.h> #include <linux/errno.h> #include <linux/platform_device.h> -#include <linux/mfd/core.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/wait.h> @@ -306,7 +305,7 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev) return -EIO; } - pdata = mfd_get_data(pdev); + pdata = pdev->dev.platform_data; if (pdata) { i2c->regstep = pdata->regstep; i2c->clock_khz = pdata->clock_khz; diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index e9d5ff4d1496..4bb68f35caf2 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -34,7 +34,6 @@ #include <linux/errno.h> #include <linux/delay.h> #include <linux/platform_device.h> -#include <linux/mfd/core.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/wait.h> @@ -705,7 +704,7 @@ static int __devinit xiic_i2c_probe(struct platform_device *pdev) if (irq < 0) goto resource_missing; - pdata = mfd_get_data(pdev); + pdata = (struct xiic_i2c_platform_data *) pdev->dev.platform_data; if (!pdata) return -EINVAL; diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 69badb4e06aa..b4dee9d5a055 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -412,6 +412,17 @@ config KEYBOARD_PXA930_ROTARY To compile this driver as a module, choose M here: the module will be called pxa930_rotary. +config KEYBOARD_PMIC8XXX + tristate "Qualcomm PMIC8XXX keypad support" + depends on MFD_PM8XXX + help + Say Y here if you want to enable the driver for the PMIC8XXX + keypad provided as a reference design from Qualcomm. This is intended + to support upto 18x8 matrix based keypad design. + + To compile this driver as a module, choose M here: the module will + be called pmic8xxx-keypad. + config KEYBOARD_SAMSUNG tristate "Samsung keypad support" depends on SAMSUNG_DEV_KEYPAD diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index c49cf8e04cd7..ddde0fd476f7 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o +obj-$(CONFIG_KEYBOARD_PMIC8XXX) += pmic8xxx-keypad.o obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o obj-$(CONFIG_KEYBOARD_QT1070) += qt1070.o diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c new file mode 100644 index 000000000000..40b02ae96f86 --- /dev/null +++ b/drivers/input/keyboard/pmic8xxx-keypad.c @@ -0,0 +1,799 @@ +/* Copyright (c) 2009-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. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/mutex.h> + +#include <linux/mfd/pm8xxx/core.h> +#include <linux/mfd/pm8xxx/gpio.h> +#include <linux/input/pmic8xxx-keypad.h> + +#define PM8XXX_MAX_ROWS 18 +#define PM8XXX_MAX_COLS 8 +#define PM8XXX_ROW_SHIFT 3 +#define PM8XXX_MATRIX_MAX_SIZE (PM8XXX_MAX_ROWS * PM8XXX_MAX_COLS) + +#define PM8XXX_MIN_ROWS 5 +#define PM8XXX_MIN_COLS 5 + +#define MAX_SCAN_DELAY 128 +#define MIN_SCAN_DELAY 1 + +/* in nanoseconds */ +#define MAX_ROW_HOLD_DELAY 122000 +#define MIN_ROW_HOLD_DELAY 30500 + +#define MAX_DEBOUNCE_TIME 20 +#define MIN_DEBOUNCE_TIME 5 + +#define KEYP_CTRL 0x148 + +#define KEYP_CTRL_EVNTS BIT(0) +#define KEYP_CTRL_EVNTS_MASK 0x3 + +#define KEYP_CTRL_SCAN_COLS_SHIFT 5 +#define KEYP_CTRL_SCAN_COLS_MIN 5 +#define KEYP_CTRL_SCAN_COLS_BITS 0x3 + +#define KEYP_CTRL_SCAN_ROWS_SHIFT 2 +#define KEYP_CTRL_SCAN_ROWS_MIN 5 +#define KEYP_CTRL_SCAN_ROWS_BITS 0x7 + +#define KEYP_CTRL_KEYP_EN BIT(7) + +#define KEYP_SCAN 0x149 + +#define KEYP_SCAN_READ_STATE BIT(0) +#define KEYP_SCAN_DBOUNCE_SHIFT 1 +#define KEYP_SCAN_PAUSE_SHIFT 3 +#define KEYP_SCAN_ROW_HOLD_SHIFT 6 + +#define KEYP_TEST 0x14A + +#define KEYP_TEST_CLEAR_RECENT_SCAN BIT(6) +#define KEYP_TEST_CLEAR_OLD_SCAN BIT(5) +#define KEYP_TEST_READ_RESET BIT(4) +#define KEYP_TEST_DTEST_EN BIT(3) +#define KEYP_TEST_ABORT_READ BIT(0) + +#define KEYP_TEST_DBG_SELECT_SHIFT 1 + +/* bits of these registers represent + * '0' for key press + * '1' for key release + */ +#define KEYP_RECENT_DATA 0x14B +#define KEYP_OLD_DATA 0x14C + +#define KEYP_CLOCK_FREQ 32768 + +/** + * struct pmic8xxx_kp - internal keypad data structure + * @pdata - keypad platform data pointer + * @input - input device pointer for keypad + * @key_sense_irq - key press/release irq number + * @key_stuck_irq - key stuck notification irq number + * @keycodes - array to hold the key codes + * @dev - parent device pointer + * @keystate - present key press/release state + * @stuckstate - present state when key stuck irq + * @ctrl_reg - control register value + */ +struct pmic8xxx_kp { + const struct pm8xxx_keypad_platform_data *pdata; + struct input_dev *input; + int key_sense_irq; + int key_stuck_irq; + + unsigned short keycodes[PM8XXX_MATRIX_MAX_SIZE]; + + struct device *dev; + u16 keystate[PM8XXX_MAX_ROWS]; + u16 stuckstate[PM8XXX_MAX_ROWS]; + + u8 ctrl_reg; +}; + +static int pmic8xxx_kp_write_u8(struct pmic8xxx_kp *kp, + u8 data, u16 reg) +{ + int rc; + + rc = pm8xxx_writeb(kp->dev->parent, reg, data); + return rc; +} + +static int pmic8xxx_kp_read(struct pmic8xxx_kp *kp, + u8 *data, u16 reg, unsigned num_bytes) +{ + int rc; + + rc = pm8xxx_read_buf(kp->dev->parent, reg, data, num_bytes); + return rc; +} + +static int pmic8xxx_kp_read_u8(struct pmic8xxx_kp *kp, + u8 *data, u16 reg) +{ + int rc; + + rc = pmic8xxx_kp_read(kp, data, reg, 1); + return rc; +} + +static u8 pmic8xxx_col_state(struct pmic8xxx_kp *kp, u8 col) +{ + /* all keys pressed on that particular row? */ + if (col == 0x00) + return 1 << kp->pdata->num_cols; + else + return col & ((1 << kp->pdata->num_cols) - 1); +} + +/* + * Synchronous read protocol for RevB0 onwards: + * + * 1. Write '1' to ReadState bit in KEYP_SCAN register + * 2. Wait 2*32KHz clocks, so that HW can successfully enter read mode + * synchronously + * 3. Read rows in old array first if events are more than one + * 4. Read rows in recent array + * 5. Wait 4*32KHz clocks + * 6. Write '0' to ReadState bit of KEYP_SCAN register so that hw can + * synchronously exit read mode. + */ +static int pmic8xxx_chk_sync_read(struct pmic8xxx_kp *kp) +{ + int rc; + u8 scan_val; + + rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN); + if (rc < 0) { + dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc); + return rc; + } + + scan_val |= 0x1; + + rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN); + if (rc < 0) { + dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc); + return rc; + } + + /* 2 * 32KHz clocks */ + udelay((2 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1); + + return rc; +} + +static int pmic8xxx_kp_read_data(struct pmic8xxx_kp *kp, u16 *state, + u16 data_reg, int read_rows) +{ + int rc, row; + u8 new_data[PM8XXX_MAX_ROWS]; + + rc = pmic8xxx_kp_read(kp, new_data, data_reg, read_rows); + if (rc) + return rc; + + for (row = 0; row < kp->pdata->num_rows; row++) { + dev_dbg(kp->dev, "new_data[%d] = %d\n", row, + new_data[row]); + state[row] = pmic8xxx_col_state(kp, new_data[row]); + } + + return rc; +} + +static int pmic8xxx_kp_read_matrix(struct pmic8xxx_kp *kp, u16 *new_state, + u16 *old_state) +{ + int rc, read_rows; + u8 scan_val; + + if (kp->pdata->num_rows < PM8XXX_MIN_ROWS) + read_rows = PM8XXX_MIN_ROWS; + else + read_rows = kp->pdata->num_rows; + + pmic8xxx_chk_sync_read(kp); + + if (old_state) { + rc = pmic8xxx_kp_read_data(kp, old_state, KEYP_OLD_DATA, + read_rows); + if (rc < 0) { + dev_err(kp->dev, + "Error reading KEYP_OLD_DATA, rc=%d\n", rc); + return rc; + } + } + + rc = pmic8xxx_kp_read_data(kp, new_state, KEYP_RECENT_DATA, + read_rows); + if (rc < 0) { + dev_err(kp->dev, + "Error reading KEYP_RECENT_DATA, rc=%d\n", rc); + return rc; + } + + /* 4 * 32KHz clocks */ + udelay((4 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1); + + rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN); + if (rc < 0) { + dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc); + return rc; + } + + scan_val &= 0xFE; + rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN); + if (rc < 0) + dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc); + + return rc; +} + +static void __pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, u16 *new_state, + u16 *old_state) +{ + int row, col, code; + + for (row = 0; row < kp->pdata->num_rows; row++) { + int bits_changed = new_state[row] ^ old_state[row]; + + if (!bits_changed) + continue; + + for (col = 0; col < kp->pdata->num_cols; col++) { + if (!(bits_changed & (1 << col))) + continue; + + dev_dbg(kp->dev, "key [%d:%d] %s\n", row, col, + !(new_state[row] & (1 << col)) ? + "pressed" : "released"); + + code = MATRIX_SCAN_CODE(row, col, PM8XXX_ROW_SHIFT); + + input_event(kp->input, EV_MSC, MSC_SCAN, code); + input_report_key(kp->input, + kp->keycodes[code], + !(new_state[row] & (1 << col))); + + input_sync(kp->input); + } + } +} + +static bool pmic8xxx_detect_ghost_keys(struct pmic8xxx_kp *kp, u16 *new_state) +{ + int row, found_first = -1; + u16 check, row_state; + + check = 0; + for (row = 0; row < kp->pdata->num_rows; row++) { + row_state = (~new_state[row]) & + ((1 << kp->pdata->num_cols) - 1); + + if (hweight16(row_state) > 1) { + if (found_first == -1) + found_first = row; + if (check & row_state) { + dev_dbg(kp->dev, "detected ghost key on row[%d]" + " and row[%d]\n", found_first, row); + return true; + } + } + check |= row_state; + } + return false; +} + +static int pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, unsigned int events) +{ + u16 new_state[PM8XXX_MAX_ROWS]; + u16 old_state[PM8XXX_MAX_ROWS]; + int rc; + + switch (events) { + case 0x1: + rc = pmic8xxx_kp_read_matrix(kp, new_state, NULL); + if (rc < 0) + return rc; + + /* detecting ghost key is not an error */ + if (pmic8xxx_detect_ghost_keys(kp, new_state)) + return 0; + __pmic8xxx_kp_scan_matrix(kp, new_state, kp->keystate); + memcpy(kp->keystate, new_state, sizeof(new_state)); + break; + case 0x3: /* two events - eventcounter is gray-coded */ + rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state); + if (rc < 0) + return rc; + + __pmic8xxx_kp_scan_matrix(kp, old_state, kp->keystate); + __pmic8xxx_kp_scan_matrix(kp, new_state, old_state); + memcpy(kp->keystate, new_state, sizeof(new_state)); + break; + case 0x2: + dev_dbg(kp->dev, "Some key events were lost\n"); + rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state); + if (rc < 0) + return rc; + __pmic8xxx_kp_scan_matrix(kp, old_state, kp->keystate); + __pmic8xxx_kp_scan_matrix(kp, new_state, old_state); + memcpy(kp->keystate, new_state, sizeof(new_state)); + break; + default: + rc = -EINVAL; + } + return rc; +} + +/* + * NOTE: We are reading recent and old data registers blindly + * whenever key-stuck interrupt happens, because events counter doesn't + * get updated when this interrupt happens due to key stuck doesn't get + * considered as key state change. + * + * We are not using old data register contents after they are being read + * because it might report the key which was pressed before the key being stuck + * as stuck key because it's pressed status is stored in the old data + * register. + */ +static irqreturn_t pmic8xxx_kp_stuck_irq(int irq, void *data) +{ + u16 new_state[PM8XXX_MAX_ROWS]; + u16 old_state[PM8XXX_MAX_ROWS]; + int rc; + struct pmic8xxx_kp *kp = data; + + rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state); + if (rc < 0) { + dev_err(kp->dev, "failed to read keypad matrix\n"); + return IRQ_HANDLED; + } + + __pmic8xxx_kp_scan_matrix(kp, new_state, kp->stuckstate); + + return IRQ_HANDLED; +} + +static irqreturn_t pmic8xxx_kp_irq(int irq, void *data) +{ + struct pmic8xxx_kp *kp = data; + u8 ctrl_val, events; + int rc; + + rc = pmic8xxx_kp_read(kp, &ctrl_val, KEYP_CTRL, 1); + if (rc < 0) { + dev_err(kp->dev, "failed to read keyp_ctrl register\n"); + return IRQ_HANDLED; + } + + events = ctrl_val & KEYP_CTRL_EVNTS_MASK; + + rc = pmic8xxx_kp_scan_matrix(kp, events); + if (rc < 0) + dev_err(kp->dev, "failed to scan matrix\n"); + + return IRQ_HANDLED; +} + +static int __devinit pmic8xxx_kpd_init(struct pmic8xxx_kp *kp) +{ + int bits, rc, cycles; + u8 scan_val = 0, ctrl_val = 0; + static const u8 row_bits[] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, + }; + + /* Find column bits */ + if (kp->pdata->num_cols < KEYP_CTRL_SCAN_COLS_MIN) + bits = 0; + else + bits = kp->pdata->num_cols - KEYP_CTRL_SCAN_COLS_MIN; + ctrl_val = (bits & KEYP_CTRL_SCAN_COLS_BITS) << + KEYP_CTRL_SCAN_COLS_SHIFT; + + /* Find row bits */ + if (kp->pdata->num_rows < KEYP_CTRL_SCAN_ROWS_MIN) + bits = 0; + else + bits = row_bits[kp->pdata->num_rows - KEYP_CTRL_SCAN_ROWS_MIN]; + + ctrl_val |= (bits << KEYP_CTRL_SCAN_ROWS_SHIFT); + + rc = pmic8xxx_kp_write_u8(kp, ctrl_val, KEYP_CTRL); + if (rc < 0) { + dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc); + return rc; + } + + bits = (kp->pdata->debounce_ms / 5) - 1; + + scan_val |= (bits << KEYP_SCAN_DBOUNCE_SHIFT); + + bits = fls(kp->pdata->scan_delay_ms) - 1; + scan_val |= (bits << KEYP_SCAN_PAUSE_SHIFT); + + /* Row hold time is a multiple of 32KHz cycles. */ + cycles = (kp->pdata->row_hold_ns * KEYP_CLOCK_FREQ) / NSEC_PER_SEC; + + scan_val |= (cycles << KEYP_SCAN_ROW_HOLD_SHIFT); + + rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN); + if (rc) + dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc); + + return rc; + +} + +static int __devinit pmic8xxx_kp_config_gpio(int gpio_start, int num_gpios, + struct pmic8xxx_kp *kp, struct pm_gpio *gpio_config) +{ + int rc, i; + + if (gpio_start < 0 || num_gpios < 0) + return -EINVAL; + + for (i = 0; i < num_gpios; i++) { + rc = pm8xxx_gpio_config(gpio_start + i, gpio_config); + if (rc) { + dev_err(kp->dev, "%s: FAIL pm8xxx_gpio_config():" + "for PM GPIO [%d] rc=%d.\n", + __func__, gpio_start + i, rc); + return rc; + } + } + + return 0; +} + +static int pmic8xxx_kp_enable(struct pmic8xxx_kp *kp) +{ + int rc; + + kp->ctrl_reg |= KEYP_CTRL_KEYP_EN; + + rc = pmic8xxx_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL); + if (rc < 0) + dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc); + + return rc; +} + +static int pmic8xxx_kp_disable(struct pmic8xxx_kp *kp) +{ + int rc; + + kp->ctrl_reg &= ~KEYP_CTRL_KEYP_EN; + + rc = pmic8xxx_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL); + if (rc < 0) + return rc; + + return rc; +} + +static int pmic8xxx_kp_open(struct input_dev *dev) +{ + struct pmic8xxx_kp *kp = input_get_drvdata(dev); + + return pmic8xxx_kp_enable(kp); +} + +static void pmic8xxx_kp_close(struct input_dev *dev) +{ + struct pmic8xxx_kp *kp = input_get_drvdata(dev); + + pmic8xxx_kp_disable(kp); +} + +/* + * keypad controller should be initialized in the following sequence + * only, otherwise it might get into FSM stuck state. + * + * - Initialize keypad control parameters, like no. of rows, columns, + * timing values etc., + * - configure rows and column gpios pull up/down. + * - set irq edge type. + * - enable the keypad controller. + */ +static int __devinit pmic8xxx_kp_probe(struct platform_device *pdev) +{ + const struct pm8xxx_keypad_platform_data *pdata = mfd_get_data(pdev); + const struct matrix_keymap_data *keymap_data; + struct pmic8xxx_kp *kp; + int rc; + u8 ctrl_val; + + struct pm_gpio kypd_drv = { + .direction = PM_GPIO_DIR_OUT, + .output_buffer = PM_GPIO_OUT_BUF_OPEN_DRAIN, + .output_value = 0, + .pull = PM_GPIO_PULL_NO, + .vin_sel = PM_GPIO_VIN_S3, + .out_strength = PM_GPIO_STRENGTH_LOW, + .function = PM_GPIO_FUNC_1, + .inv_int_pol = 1, + }; + + struct pm_gpio kypd_sns = { + .direction = PM_GPIO_DIR_IN, + .pull = PM_GPIO_PULL_UP_31P5, + .vin_sel = PM_GPIO_VIN_S3, + .out_strength = PM_GPIO_STRENGTH_NO, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 1, + }; + + + if (!pdata || !pdata->num_cols || !pdata->num_rows || + pdata->num_cols > PM8XXX_MAX_COLS || + pdata->num_rows > PM8XXX_MAX_ROWS || + pdata->num_cols < PM8XXX_MIN_COLS) { + dev_err(&pdev->dev, "invalid platform data\n"); + return -EINVAL; + } + + if (!pdata->scan_delay_ms || + pdata->scan_delay_ms > MAX_SCAN_DELAY || + pdata->scan_delay_ms < MIN_SCAN_DELAY || + !is_power_of_2(pdata->scan_delay_ms)) { + dev_err(&pdev->dev, "invalid keypad scan time supplied\n"); + return -EINVAL; + } + + if (!pdata->row_hold_ns || + pdata->row_hold_ns > MAX_ROW_HOLD_DELAY || + pdata->row_hold_ns < MIN_ROW_HOLD_DELAY || + ((pdata->row_hold_ns % MIN_ROW_HOLD_DELAY) != 0)) { + dev_err(&pdev->dev, "invalid keypad row hold time supplied\n"); + return -EINVAL; + } + + if (!pdata->debounce_ms || + ((pdata->debounce_ms % 5) != 0) || + pdata->debounce_ms > MAX_DEBOUNCE_TIME || + pdata->debounce_ms < MIN_DEBOUNCE_TIME) { + dev_err(&pdev->dev, "invalid debounce time supplied\n"); + return -EINVAL; + } + + keymap_data = pdata->keymap_data; + if (!keymap_data) { + dev_err(&pdev->dev, "no keymap data supplied\n"); + return -EINVAL; + } + + kp = kzalloc(sizeof(*kp), GFP_KERNEL); + if (!kp) + return -ENOMEM; + + platform_set_drvdata(pdev, kp); + + kp->pdata = pdata; + kp->dev = &pdev->dev; + + kp->input = input_allocate_device(); + if (!kp->input) { + dev_err(&pdev->dev, "unable to allocate input device\n"); + rc = -ENOMEM; + goto err_alloc_device; + } + + kp->key_sense_irq = platform_get_irq(pdev, 0); + if (kp->key_sense_irq < 0) { + dev_err(&pdev->dev, "unable to get keypad sense irq\n"); + rc = -ENXIO; + goto err_get_irq; + } + + kp->key_stuck_irq = platform_get_irq(pdev, 1); + if (kp->key_stuck_irq < 0) { + dev_err(&pdev->dev, "unable to get keypad stuck irq\n"); + rc = -ENXIO; + goto err_get_irq; + } + + kp->input->name = pdata->input_name ? : "PMIC8XXX keypad"; + kp->input->phys = pdata->input_phys_device ? : "pmic8xxx_keypad/input0"; + + kp->input->dev.parent = &pdev->dev; + + kp->input->id.bustype = BUS_I2C; + kp->input->id.version = 0x0001; + kp->input->id.product = 0x0001; + kp->input->id.vendor = 0x0001; + + kp->input->evbit[0] = BIT_MASK(EV_KEY); + + if (pdata->rep) + __set_bit(EV_REP, kp->input->evbit); + + kp->input->keycode = kp->keycodes; + kp->input->keycodemax = PM8XXX_MATRIX_MAX_SIZE; + kp->input->keycodesize = sizeof(kp->keycodes); + kp->input->open = pmic8xxx_kp_open; + kp->input->close = pmic8xxx_kp_close; + + matrix_keypad_build_keymap(keymap_data, PM8XXX_ROW_SHIFT, + kp->input->keycode, kp->input->keybit); + + input_set_capability(kp->input, EV_MSC, MSC_SCAN); + input_set_drvdata(kp->input, kp); + + /* initialize keypad state */ + memset(kp->keystate, 0xff, sizeof(kp->keystate)); + memset(kp->stuckstate, 0xff, sizeof(kp->stuckstate)); + + rc = pmic8xxx_kpd_init(kp); + if (rc < 0) { + dev_err(&pdev->dev, "unable to initialize keypad controller\n"); + goto err_get_irq; + } + + rc = pmic8xxx_kp_config_gpio(pdata->cols_gpio_start, + pdata->num_cols, kp, &kypd_sns); + if (rc < 0) { + dev_err(&pdev->dev, "unable to configure keypad sense lines\n"); + goto err_gpio_config; + } + + rc = pmic8xxx_kp_config_gpio(pdata->rows_gpio_start, + pdata->num_rows, kp, &kypd_drv); + if (rc < 0) { + dev_err(&pdev->dev, "unable to configure keypad drive lines\n"); + goto err_gpio_config; + } + + rc = request_any_context_irq(kp->key_sense_irq, pmic8xxx_kp_irq, + IRQF_TRIGGER_RISING, "pmic-keypad", kp); + if (rc < 0) { + dev_err(&pdev->dev, "failed to request keypad sense irq\n"); + goto err_get_irq; + } + + rc = request_any_context_irq(kp->key_stuck_irq, pmic8xxx_kp_stuck_irq, + IRQF_TRIGGER_RISING, "pmic-keypad-stuck", kp); + if (rc < 0) { + dev_err(&pdev->dev, "failed to request keypad stuck irq\n"); + goto err_req_stuck_irq; + } + + rc = pmic8xxx_kp_read_u8(kp, &ctrl_val, KEYP_CTRL); + if (rc < 0) { + dev_err(&pdev->dev, "failed to read KEYP_CTRL register\n"); + goto err_pmic_reg_read; + } + + kp->ctrl_reg = ctrl_val; + + rc = input_register_device(kp->input); + if (rc < 0) { + dev_err(&pdev->dev, "unable to register keypad input device\n"); + goto err_pmic_reg_read; + } + + device_init_wakeup(&pdev->dev, pdata->wakeup); + + return 0; + +err_pmic_reg_read: + free_irq(kp->key_stuck_irq, NULL); +err_req_stuck_irq: + free_irq(kp->key_sense_irq, NULL); +err_gpio_config: +err_get_irq: + input_free_device(kp->input); +err_alloc_device: + platform_set_drvdata(pdev, NULL); + kfree(kp); + return rc; +} + +static int __devexit pmic8xxx_kp_remove(struct platform_device *pdev) +{ + struct pmic8xxx_kp *kp = platform_get_drvdata(pdev); + + device_init_wakeup(&pdev->dev, 0); + free_irq(kp->key_stuck_irq, NULL); + free_irq(kp->key_sense_irq, NULL); + input_unregister_device(kp->input); + kfree(kp); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int pmic8xxx_kp_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pmic8xxx_kp *kp = platform_get_drvdata(pdev); + struct input_dev *input_dev = kp->input; + + if (device_may_wakeup(dev)) { + enable_irq_wake(kp->key_sense_irq); + } else { + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + pmic8xxx_kp_disable(kp); + + mutex_unlock(&input_dev->mutex); + } + + return 0; +} + +static int pmic8xxx_kp_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pmic8xxx_kp *kp = platform_get_drvdata(pdev); + struct input_dev *input_dev = kp->input; + + if (device_may_wakeup(dev)) { + disable_irq_wake(kp->key_sense_irq); + } else { + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + pmic8xxx_kp_enable(kp); + + mutex_unlock(&input_dev->mutex); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pm8xxx_kp_pm_ops, + pmic8xxx_kp_suspend, pmic8xxx_kp_resume); + +static struct platform_driver pmic8xxx_kp_driver = { + .probe = pmic8xxx_kp_probe, + .remove = __devexit_p(pmic8xxx_kp_remove), + .driver = { + .name = PM8XXX_KEYPAD_DEV_NAME, + .owner = THIS_MODULE, + .pm = &pm8xxx_kp_pm_ops, + }, +}; + +static int __init pmic8xxx_kp_init(void) +{ + return platform_driver_register(&pmic8xxx_kp_driver); +} +module_init(pmic8xxx_kp_init); + +static void __exit pmic8xxx_kp_exit(void) +{ + platform_driver_unregister(&pmic8xxx_kp_driver); +} +module_exit(pmic8xxx_kp_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PMIC8XXX keypad driver"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("platform:pmic8xxx_keypad"); +MODULE_AUTHOR("Trilok Soni <tsoni@codeaurora.org>"); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index f9cf0881b0e3..45dc6aa62ba4 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -330,6 +330,17 @@ config INPUT_PWM_BEEPER To compile this driver as a module, choose M here: the module will be called pwm-beeper. +config INPUT_PMIC8XXX_PWRKEY + tristate "PMIC8XXX power key support" + depends on MFD_PM8XXX + help + Say Y here if you want support for the PMIC8XXX power key. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called pmic8xxx-pwrkey. + config INPUT_GPIO_ROTARY_ENCODER tristate "Rotary encoders connected to GPIO pins" depends on GPIOLIB && GENERIC_GPIO diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index e3f7984e6274..38efb2cb182b 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o +obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c new file mode 100644 index 000000000000..97e07e786e41 --- /dev/null +++ b/drivers/input/misc/pmic8xxx-pwrkey.c @@ -0,0 +1,231 @@ +/* Copyright (c) 2010-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. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/log2.h> + +#include <linux/mfd/pm8xxx/core.h> +#include <linux/input/pmic8xxx-pwrkey.h> + +#define PON_CNTL_1 0x1C +#define PON_CNTL_PULL_UP BIT(7) +#define PON_CNTL_TRIG_DELAY_MASK (0x7) + +/** + * struct pmic8xxx_pwrkey - pmic8xxx pwrkey information + * @key_press_irq: key press irq number + */ +struct pmic8xxx_pwrkey { + struct input_dev *pwr; + int key_press_irq; +}; + +static irqreturn_t pwrkey_press_irq(int irq, void *_pwrkey) +{ + struct pmic8xxx_pwrkey *pwrkey = _pwrkey; + + input_report_key(pwrkey->pwr, KEY_POWER, 1); + input_sync(pwrkey->pwr); + + return IRQ_HANDLED; +} + +static irqreturn_t pwrkey_release_irq(int irq, void *_pwrkey) +{ + struct pmic8xxx_pwrkey *pwrkey = _pwrkey; + + input_report_key(pwrkey->pwr, KEY_POWER, 0); + input_sync(pwrkey->pwr); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM_SLEEP +static int pmic8xxx_pwrkey_suspend(struct device *dev) +{ + struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(pwrkey->key_press_irq); + + return 0; +} + +static int pmic8xxx_pwrkey_resume(struct device *dev) +{ + struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(pwrkey->key_press_irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops, + pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume); + +static int __devinit pmic8xxx_pwrkey_probe(struct platform_device *pdev) +{ + struct input_dev *pwr; + int key_release_irq = platform_get_irq(pdev, 0); + int key_press_irq = platform_get_irq(pdev, 1); + int err; + unsigned int delay; + u8 pon_cntl; + struct pmic8xxx_pwrkey *pwrkey; + const struct pm8xxx_pwrkey_platform_data *pdata = mfd_get_data(pdev); + + if (!pdata) { + dev_err(&pdev->dev, "power key platform data not supplied\n"); + return -EINVAL; + } + + if (pdata->kpd_trigger_delay_us > 62500) { + dev_err(&pdev->dev, "invalid power key trigger delay\n"); + return -EINVAL; + } + + pwrkey = kzalloc(sizeof(*pwrkey), GFP_KERNEL); + if (!pwrkey) + return -ENOMEM; + + pwr = input_allocate_device(); + if (!pwr) { + dev_dbg(&pdev->dev, "Can't allocate power button\n"); + err = -ENOMEM; + goto free_pwrkey; + } + + input_set_capability(pwr, EV_KEY, KEY_POWER); + + pwr->name = "pmic8xxx_pwrkey"; + pwr->phys = "pmic8xxx_pwrkey/input0"; + pwr->dev.parent = &pdev->dev; + + delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC; + delay = 1 + ilog2(delay); + + err = pm8xxx_readb(pdev->dev.parent, PON_CNTL_1, &pon_cntl); + if (err < 0) { + dev_err(&pdev->dev, "failed reading PON_CNTL_1 err=%d\n", err); + goto free_input_dev; + } + + pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK; + pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK); + if (pdata->pull_up) + pon_cntl |= PON_CNTL_PULL_UP; + else + pon_cntl &= ~PON_CNTL_PULL_UP; + + err = pm8xxx_writeb(pdev->dev.parent, PON_CNTL_1, pon_cntl); + if (err < 0) { + dev_err(&pdev->dev, "failed writing PON_CNTL_1 err=%d\n", err); + goto free_input_dev; + } + + err = input_register_device(pwr); + if (err) { + dev_dbg(&pdev->dev, "Can't register power key: %d\n", err); + goto free_input_dev; + } + + pwrkey->key_press_irq = key_press_irq; + pwrkey->pwr = pwr; + + platform_set_drvdata(pdev, pwrkey); + + err = request_irq(key_press_irq, pwrkey_press_irq, + IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_press", pwrkey); + if (err < 0) { + dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n", + key_press_irq, err); + goto unreg_input_dev; + } + + err = request_irq(key_release_irq, pwrkey_release_irq, + IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_release", pwrkey); + if (err < 0) { + dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n", + key_release_irq, err); + + goto free_press_irq; + } + + device_init_wakeup(&pdev->dev, pdata->wakeup); + + return 0; + +free_press_irq: + free_irq(key_press_irq, NULL); +unreg_input_dev: + platform_set_drvdata(pdev, NULL); + input_unregister_device(pwr); + pwr = NULL; +free_input_dev: + input_free_device(pwr); +free_pwrkey: + kfree(pwrkey); + return err; +} + +static int __devexit pmic8xxx_pwrkey_remove(struct platform_device *pdev) +{ + struct pmic8xxx_pwrkey *pwrkey = platform_get_drvdata(pdev); + int key_release_irq = platform_get_irq(pdev, 0); + int key_press_irq = platform_get_irq(pdev, 1); + + device_init_wakeup(&pdev->dev, 0); + + free_irq(key_press_irq, pwrkey); + free_irq(key_release_irq, pwrkey); + input_unregister_device(pwrkey->pwr); + platform_set_drvdata(pdev, NULL); + kfree(pwrkey); + + return 0; +} + +static struct platform_driver pmic8xxx_pwrkey_driver = { + .probe = pmic8xxx_pwrkey_probe, + .remove = __devexit_p(pmic8xxx_pwrkey_remove), + .driver = { + .name = PM8XXX_PWRKEY_DEV_NAME, + .owner = THIS_MODULE, + .pm = &pm8xxx_pwr_key_pm_ops, + }, +}; + +static int __init pmic8xxx_pwrkey_init(void) +{ + return platform_driver_register(&pmic8xxx_pwrkey_driver); +} +module_init(pmic8xxx_pwrkey_init); + +static void __exit pmic8xxx_pwrkey_exit(void) +{ + platform_driver_unregister(&pmic8xxx_pwrkey_driver); +} +module_exit(pmic8xxx_pwrkey_exit); + +MODULE_ALIAS("platform:pmic8xxx_pwrkey"); +MODULE_DESCRIPTION("PMIC8XXX Power Key driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Trilok Soni <tsoni@codeaurora.org>"); diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index 6a11694e3fc7..014dd4ad0d4f 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -29,7 +29,6 @@ #include <linux/workqueue.h> #include <linux/i2c/twl.h> #include <linux/mfd/twl4030-codec.h> -#include <linux/mfd/core.h> #include <linux/input.h> #include <linux/slab.h> @@ -197,7 +196,7 @@ static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, static int __devinit twl4030_vibra_probe(struct platform_device *pdev) { - struct twl4030_codec_vibra_data *pdata = mfd_get_data(pdev); + struct twl4030_codec_vibra_data *pdata = pdev->dev.platform_data; struct vibra_info *info; int ret; diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 1d027b475b22..23f0d5e99f35 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -389,6 +389,16 @@ config LEDS_NETXBIG and 5Big Network v2 boards. The LEDs are wired to a CPLD and are controlled through a GPIO extension bus. +config LEDS_ASIC3 + bool "LED support for the HTC ASIC3" + depends on MFD_ASIC3 + default y + help + This option enables support for the LEDs on the HTC ASIC3. The HTC + ASIC3 LED GPIOs are inputs, not outputs, thus the leds-gpio driver + cannot be used. This driver supports hardware blinking with an on+off + period from 62ms to 125s. Say Y to enable LEDs on the HP iPAQ hx4700. + config LEDS_TRIGGERS bool "LED Trigger support" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index bccb96c9bb45..bbfd2e367dc0 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_LEDS_DELL_NETBOOKS) += dell-led.o obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o obj-$(CONFIG_LEDS_NS2) += leds-ns2.o obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o +obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o # LED SPI Drivers obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index 416def84d045..0d4c16678ace 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -17,7 +17,6 @@ #include <linux/leds.h> #include <linux/slab.h> #include <linux/workqueue.h> -#include <linux/mfd/core.h> #include <linux/mfd/88pm860x.h> #define LED_PWM_SHIFT (3) @@ -171,7 +170,6 @@ static int pm860x_led_probe(struct platform_device *pdev) struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); struct pm860x_led_pdata *pdata; struct pm860x_led *data; - struct mfd_cell *cell; struct resource *res; int ret; @@ -181,10 +179,7 @@ static int pm860x_led_probe(struct platform_device *pdev) return -EINVAL; } - cell = pdev->dev.platform_data; - if (cell == NULL) - return -ENODEV; - pdata = cell->mfd_data; + pdata = pdev->dev.platform_data; if (pdata == NULL) { dev_err(&pdev->dev, "No platform data!\n"); return -EINVAL; diff --git a/drivers/leds/leds-asic3.c b/drivers/leds/leds-asic3.c new file mode 100644 index 000000000000..22f847c890c9 --- /dev/null +++ b/drivers/leds/leds-asic3.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2011 Paul Parsons <lost.distance@yahoo.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/leds.h> +#include <linux/slab.h> + +#include <linux/mfd/asic3.h> +#include <linux/mfd/core.h> + +/* + * The HTC ASIC3 LED GPIOs are inputs, not outputs. + * Hence we turn the LEDs on/off via the TimeBase register. + */ + +/* + * When TimeBase is 4 the clock resolution is about 32Hz. + * This driver supports hardware blinking with an on+off + * period from 62ms (2 clocks) to 125s (4000 clocks). + */ +#define MS_TO_CLK(ms) DIV_ROUND_CLOSEST(((ms)*1024), 32000) +#define CLK_TO_MS(clk) (((clk)*32000)/1024) +#define MAX_CLK 4000 /* Fits into 12-bit Time registers */ +#define MAX_MS CLK_TO_MS(MAX_CLK) + +static const unsigned int led_n_base[ASIC3_NUM_LEDS] = { + [0] = ASIC3_LED_0_Base, + [1] = ASIC3_LED_1_Base, + [2] = ASIC3_LED_2_Base, +}; + +static void brightness_set(struct led_classdev *cdev, + enum led_brightness value) +{ + struct platform_device *pdev = to_platform_device(cdev->dev->parent); + const struct mfd_cell *cell = mfd_get_cell(pdev); + struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); + u32 timebase; + unsigned int base; + + timebase = (value == LED_OFF) ? 0 : (LED_EN|0x4); + + base = led_n_base[cell->id]; + asic3_write_register(asic, (base + ASIC3_LED_PeriodTime), 32); + asic3_write_register(asic, (base + ASIC3_LED_DutyTime), 32); + asic3_write_register(asic, (base + ASIC3_LED_AutoStopCount), 0); + asic3_write_register(asic, (base + ASIC3_LED_TimeBase), timebase); +} + +static int blink_set(struct led_classdev *cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct platform_device *pdev = to_platform_device(cdev->dev->parent); + const struct mfd_cell *cell = mfd_get_cell(pdev); + struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); + u32 on; + u32 off; + unsigned int base; + + if (*delay_on > MAX_MS || *delay_off > MAX_MS) + return -EINVAL; + + if (*delay_on == 0 && *delay_off == 0) { + /* If both are zero then a sensible default should be chosen */ + on = MS_TO_CLK(500); + off = MS_TO_CLK(500); + } else { + on = MS_TO_CLK(*delay_on); + off = MS_TO_CLK(*delay_off); + if ((on + off) > MAX_CLK) + return -EINVAL; + } + + base = led_n_base[cell->id]; + asic3_write_register(asic, (base + ASIC3_LED_PeriodTime), (on + off)); + asic3_write_register(asic, (base + ASIC3_LED_DutyTime), on); + asic3_write_register(asic, (base + ASIC3_LED_AutoStopCount), 0); + asic3_write_register(asic, (base + ASIC3_LED_TimeBase), (LED_EN|0x4)); + + *delay_on = CLK_TO_MS(on); + *delay_off = CLK_TO_MS(off); + + return 0; +} + +static int __devinit asic3_led_probe(struct platform_device *pdev) +{ + struct asic3_led *led = pdev->dev.platform_data; + int ret; + + ret = mfd_cell_enable(pdev); + if (ret < 0) + goto ret0; + + led->cdev = kzalloc(sizeof(struct led_classdev), GFP_KERNEL); + if (!led->cdev) { + ret = -ENOMEM; + goto ret1; + } + + led->cdev->name = led->name; + led->cdev->default_trigger = led->default_trigger; + led->cdev->brightness_set = brightness_set; + led->cdev->blink_set = blink_set; + + ret = led_classdev_register(&pdev->dev, led->cdev); + if (ret < 0) + goto ret2; + + return 0; + +ret2: + kfree(led->cdev); +ret1: + (void) mfd_cell_disable(pdev); +ret0: + return ret; +} + +static int __devexit asic3_led_remove(struct platform_device *pdev) +{ + struct asic3_led *led = pdev->dev.platform_data; + + led_classdev_unregister(led->cdev); + + kfree(led->cdev); + + return mfd_cell_disable(pdev); +} + +static struct platform_driver asic3_led_driver = { + .probe = asic3_led_probe, + .remove = __devexit_p(asic3_led_remove), + .driver = { + .name = "leds-asic3", + .owner = THIS_MODULE, + }, +}; + +MODULE_ALIAS("platform:leds-asic3"); + +static int __init asic3_led_init(void) +{ + return platform_driver_register(&asic3_led_driver); +} + +static void __exit asic3_led_exit(void) +{ + platform_driver_unregister(&asic3_led_driver); +} + +module_init(asic3_led_init); +module_exit(asic3_led_exit); + +MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>"); +MODULE_DESCRIPTION("HTC ASIC3 LED driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index 126ca7955f6e..f369e56d6547 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -22,7 +22,6 @@ #include <linux/leds.h> #include <linux/workqueue.h> #include <linux/mfd/mc13783.h> -#include <linux/mfd/core.h> #include <linux/slab.h> struct mc13783_led { @@ -184,7 +183,7 @@ static int __devinit mc13783_led_setup(struct mc13783_led *led, int max_current) static int __devinit mc13783_leds_prepare(struct platform_device *pdev) { - struct mc13783_leds_platform_data *pdata = mfd_get_data(pdev); + struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent); int ret = 0; int reg = 0; @@ -265,7 +264,7 @@ out: static int __devinit mc13783_led_probe(struct platform_device *pdev) { - struct mc13783_leds_platform_data *pdata = mfd_get_data(pdev); + struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); struct mc13783_led_platform_data *led_cur; struct mc13783_led *led, *led_dat; int ret, i; @@ -352,7 +351,7 @@ err_free: static int __devexit mc13783_led_remove(struct platform_device *pdev) { - struct mc13783_leds_platform_data *pdata = mfd_get_data(pdev); + struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); struct mc13783_led *led = platform_get_drvdata(pdev); struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent); int i; diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c index 1e3a8dd820a4..a185610b376b 100644 --- a/drivers/media/radio/radio-timb.c +++ b/drivers/media/radio/radio-timb.c @@ -21,7 +21,6 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-device.h> #include <linux/platform_device.h> -#include <linux/mfd/core.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/i2c.h> @@ -149,7 +148,7 @@ static const struct v4l2_file_operations timbradio_fops = { static int __devinit timbradio_probe(struct platform_device *pdev) { - struct timb_radio_platform_data *pdata = mfd_get_data(pdev); + struct timb_radio_platform_data *pdata = pdev->dev.platform_data; struct timbradio *tr; int err; diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index e2550dc2944f..46cacf845049 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -1990,7 +1990,7 @@ static int wl1273_fm_radio_remove(struct platform_device *pdev) static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev) { - struct wl1273_core **core = mfd_get_data(pdev); + struct wl1273_core **core = pdev->dev.platform_data; struct wl1273_device *radio; struct v4l2_ctrl *ctrl; int r = 0; diff --git a/drivers/media/video/timblogiw.c b/drivers/media/video/timblogiw.c index 84d4c7c83435..fc611ebeb82c 100644 --- a/drivers/media/video/timblogiw.c +++ b/drivers/media/video/timblogiw.c @@ -24,7 +24,6 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/dmaengine.h> -#include <linux/mfd/core.h> #include <linux/scatterlist.h> #include <linux/interrupt.h> #include <linux/list.h> @@ -791,7 +790,7 @@ static int __devinit timblogiw_probe(struct platform_device *pdev) { int err; struct timblogiw *lw = NULL; - struct timb_video_platform_data *pdata = mfd_get_data(pdev); + struct timb_video_platform_data *pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "No platform data\n"); diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 011cb6ce861b..17dfe9bb6d27 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -21,13 +21,13 @@ #define INT_STATUS_NUM 3 -static struct resource bk_resources[] __initdata = { +static struct resource bk_resources[] __devinitdata = { {PM8606_BACKLIGHT1, PM8606_BACKLIGHT1, "backlight-0", IORESOURCE_IO,}, {PM8606_BACKLIGHT2, PM8606_BACKLIGHT2, "backlight-1", IORESOURCE_IO,}, {PM8606_BACKLIGHT3, PM8606_BACKLIGHT3, "backlight-2", IORESOURCE_IO,}, }; -static struct resource led_resources[] __initdata = { +static struct resource led_resources[] __devinitdata = { {PM8606_LED1_RED, PM8606_LED1_RED, "led0-red", IORESOURCE_IO,}, {PM8606_LED1_GREEN, PM8606_LED1_GREEN, "led0-green", IORESOURCE_IO,}, {PM8606_LED1_BLUE, PM8606_LED1_BLUE, "led0-blue", IORESOURCE_IO,}, @@ -36,7 +36,7 @@ static struct resource led_resources[] __initdata = { {PM8606_LED2_BLUE, PM8606_LED2_BLUE, "led1-blue", IORESOURCE_IO,}, }; -static struct resource regulator_resources[] __initdata = { +static struct resource regulator_resources[] __devinitdata = { {PM8607_ID_BUCK1, PM8607_ID_BUCK1, "buck-1", IORESOURCE_IO,}, {PM8607_ID_BUCK2, PM8607_ID_BUCK2, "buck-2", IORESOURCE_IO,}, {PM8607_ID_BUCK3, PM8607_ID_BUCK3, "buck-3", IORESOURCE_IO,}, @@ -57,15 +57,15 @@ static struct resource regulator_resources[] __initdata = { {PM8607_ID_LDO15, PM8607_ID_LDO15, "ldo-15", IORESOURCE_IO,}, }; -static struct resource touch_resources[] __initdata = { +static struct resource touch_resources[] __devinitdata = { {PM8607_IRQ_PEN, PM8607_IRQ_PEN, "touch", IORESOURCE_IRQ,}, }; -static struct resource onkey_resources[] __initdata = { +static struct resource onkey_resources[] __devinitdata = { {PM8607_IRQ_ONKEY, PM8607_IRQ_ONKEY, "onkey", IORESOURCE_IRQ,}, }; -static struct resource codec_resources[] __initdata = { +static struct resource codec_resources[] __devinitdata = { /* Headset microphone insertion or removal */ {PM8607_IRQ_MICIN, PM8607_IRQ_MICIN, "micin", IORESOURCE_IRQ,}, /* Hook-switch press or release */ @@ -76,12 +76,12 @@ static struct resource codec_resources[] __initdata = { {PM8607_IRQ_AUDIO_SHORT, PM8607_IRQ_AUDIO_SHORT, "audio-short", IORESOURCE_IRQ,}, }; -static struct resource battery_resources[] __initdata = { +static struct resource battery_resources[] __devinitdata = { {PM8607_IRQ_CC, PM8607_IRQ_CC, "columb counter", IORESOURCE_IRQ,}, {PM8607_IRQ_BAT, PM8607_IRQ_BAT, "battery", IORESOURCE_IRQ,}, }; -static struct resource charger_resources[] __initdata = { +static struct resource charger_resources[] __devinitdata = { {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_FAULT, PM8607_IRQ_CHG_FAULT, "charging timeout", IORESOURCE_IRQ,}, @@ -90,13 +90,17 @@ static struct resource charger_resources[] __initdata = { {PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage", IORESOURCE_IRQ,}, }; -static struct mfd_cell bk_devs[] __initdata = { +static struct resource rtc_resources[] __devinitdata = { + {PM8607_IRQ_RTC, PM8607_IRQ_RTC, "rtc", IORESOURCE_IRQ,}, +}; + +static struct mfd_cell bk_devs[] = { {"88pm860x-backlight", 0,}, {"88pm860x-backlight", 1,}, {"88pm860x-backlight", 2,}, }; -static struct mfd_cell led_devs[] __initdata = { +static struct mfd_cell led_devs[] = { {"88pm860x-led", 0,}, {"88pm860x-led", 1,}, {"88pm860x-led", 2,}, @@ -105,7 +109,7 @@ static struct mfd_cell led_devs[] __initdata = { {"88pm860x-led", 5,}, }; -static struct mfd_cell regulator_devs[] __initdata = { +static struct mfd_cell regulator_devs[] = { {"88pm860x-regulator", 0,}, {"88pm860x-regulator", 1,}, {"88pm860x-regulator", 2,}, @@ -126,15 +130,15 @@ static struct mfd_cell regulator_devs[] __initdata = { {"88pm860x-regulator", 17,}, }; -static struct mfd_cell touch_devs[] __initdata = { +static struct mfd_cell touch_devs[] = { {"88pm860x-touch", -1,}, }; -static struct mfd_cell onkey_devs[] __initdata = { +static struct mfd_cell onkey_devs[] = { {"88pm860x-onkey", -1,}, }; -static struct mfd_cell codec_devs[] __initdata = { +static struct mfd_cell codec_devs[] = { {"88pm860x-codec", -1,}, }; @@ -143,11 +147,10 @@ static struct mfd_cell power_devs[] = { {"88pm860x-charger", -1,}, }; -static struct pm860x_backlight_pdata bk_pdata[ARRAY_SIZE(bk_devs)]; -static struct pm860x_led_pdata led_pdata[ARRAY_SIZE(led_devs)]; -static struct regulator_init_data regulator_pdata[ARRAY_SIZE(regulator_devs)]; -static struct pm860x_touch_pdata touch_pdata; -static struct pm860x_power_pdata power_pdata; +static struct mfd_cell rtc_devs[] = { + {"88pm860x-rtc", -1,}, +}; + struct pm860x_irq_data { int reg; @@ -501,7 +504,6 @@ static void device_irq_exit(struct pm860x_chip *chip) } static void __devinit device_bk_init(struct pm860x_chip *chip, - struct i2c_client *i2c, struct pm860x_platform_data *pdata) { int ret; @@ -514,13 +516,12 @@ static void __devinit device_bk_init(struct pm860x_chip *chip, pdata->num_backlights = ARRAY_SIZE(bk_devs); for (i = 0; i < pdata->num_backlights; i++) { - memcpy(&bk_pdata[i], &pdata->backlight[i], - sizeof(struct pm860x_backlight_pdata)); - bk_devs[i].mfd_data = &bk_pdata[i]; + bk_devs[i].platform_data = &pdata->backlight[i]; + bk_devs[i].pdata_size = sizeof(struct pm860x_backlight_pdata); for (j = 0; j < ARRAY_SIZE(bk_devs); j++) { id = bk_resources[j].start; - if (bk_pdata[i].flags != id) + if (pdata->backlight[i].flags != id) continue; bk_devs[i].num_resources = 1; @@ -538,7 +539,6 @@ static void __devinit device_bk_init(struct pm860x_chip *chip, } static void __devinit device_led_init(struct pm860x_chip *chip, - struct i2c_client *i2c, struct pm860x_platform_data *pdata) { int ret; @@ -551,13 +551,12 @@ static void __devinit device_led_init(struct pm860x_chip *chip, pdata->num_leds = ARRAY_SIZE(led_devs); for (i = 0; i < pdata->num_leds; i++) { - memcpy(&led_pdata[i], &pdata->led[i], - sizeof(struct pm860x_led_pdata)); - led_devs[i].mfd_data = &led_pdata[i]; + led_devs[i].platform_data = &pdata->led[i]; + led_devs[i].pdata_size = sizeof(struct pm860x_led_pdata); for (j = 0; j < ARRAY_SIZE(led_devs); j++) { id = led_resources[j].start; - if (led_pdata[i].flags != id) + if (pdata->led[i].flags != id) continue; led_devs[i].num_resources = 1; @@ -575,12 +574,11 @@ static void __devinit device_led_init(struct pm860x_chip *chip, } static void __devinit device_regulator_init(struct pm860x_chip *chip, - struct i2c_client *i2c, struct pm860x_platform_data *pdata) { struct regulator_init_data *initdata; int ret; - int i, j; + int i, seq; if ((pdata == NULL) || (pdata->regulator == NULL)) return; @@ -588,41 +586,21 @@ static void __devinit device_regulator_init(struct pm860x_chip *chip, if (pdata->num_regulators > ARRAY_SIZE(regulator_devs)) pdata->num_regulators = ARRAY_SIZE(regulator_devs); - for (i = 0, j = -1; i < pdata->num_regulators; i++) { + for (i = 0, seq = -1; i < pdata->num_regulators; i++) { initdata = &pdata->regulator[i]; - if (strstr(initdata->constraints.name, "BUCK")) { - sscanf(initdata->constraints.name, "BUCK%d", &j); - /* BUCK1 ~ BUCK3 */ - if ((j < 1) || (j > 3)) { - dev_err(chip->dev, "Failed to add constraint " - "(%s)\n", initdata->constraints.name); - goto out; - } - j = (j - 1) + PM8607_ID_BUCK1; - } - if (strstr(initdata->constraints.name, "LDO")) { - sscanf(initdata->constraints.name, "LDO%d", &j); - /* LDO1 ~ LDO15 */ - if ((j < 1) || (j > 15)) { - dev_err(chip->dev, "Failed to add constraint " - "(%s)\n", initdata->constraints.name); - goto out; - } - j = (j - 1) + PM8607_ID_LDO1; - } - if (j == -1) { - dev_err(chip->dev, "Failed to add constraint (%s)\n", - initdata->constraints.name); + seq = *(unsigned int *)initdata->driver_data; + if ((seq < 0) || (seq > PM8607_ID_RG_MAX)) { + dev_err(chip->dev, "Wrong ID(%d) on regulator(%s)\n", + seq, initdata->constraints.name); goto out; } - memcpy(®ulator_pdata[i], &pdata->regulator[i], - sizeof(struct regulator_init_data)); - regulator_devs[i].mfd_data = ®ulator_pdata[i]; + regulator_devs[i].platform_data = &pdata->regulator[i]; + regulator_devs[i].pdata_size = sizeof(struct regulator_init_data); regulator_devs[i].num_resources = 1; - regulator_devs[i].resources = ®ulator_resources[j]; + regulator_devs[i].resources = ®ulator_resources[seq]; ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[i], 1, - ®ulator_resources[j], 0); + ®ulator_resources[seq], 0); if (ret < 0) { dev_err(chip->dev, "Failed to add regulator subdev\n"); goto out; @@ -632,17 +610,35 @@ out: return; } +static void __devinit device_rtc_init(struct pm860x_chip *chip, + struct pm860x_platform_data *pdata) +{ + int ret; + + if ((pdata == NULL)) + return; + + rtc_devs[0].platform_data = pdata->rtc; + rtc_devs[0].pdata_size = sizeof(struct pm860x_rtc_pdata); + rtc_devs[0].num_resources = ARRAY_SIZE(rtc_resources); + rtc_devs[0].resources = &rtc_resources[0]; + ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0], + ARRAY_SIZE(rtc_devs), &rtc_resources[0], + chip->irq_base); + if (ret < 0) + dev_err(chip->dev, "Failed to add rtc subdev\n"); +} + static void __devinit device_touch_init(struct pm860x_chip *chip, - struct i2c_client *i2c, struct pm860x_platform_data *pdata) { int ret; - if ((pdata == NULL) || (pdata->touch == NULL)) + if (pdata == NULL) return; - memcpy(&touch_pdata, pdata->touch, sizeof(struct pm860x_touch_pdata)); - touch_devs[0].mfd_data = &touch_pdata; + touch_devs[0].platform_data = pdata->touch; + touch_devs[0].pdata_size = sizeof(struct pm860x_touch_pdata); touch_devs[0].num_resources = ARRAY_SIZE(touch_resources); touch_devs[0].resources = &touch_resources[0]; ret = mfd_add_devices(chip->dev, 0, &touch_devs[0], @@ -653,16 +649,15 @@ static void __devinit device_touch_init(struct pm860x_chip *chip, } static void __devinit device_power_init(struct pm860x_chip *chip, - struct i2c_client *i2c, struct pm860x_platform_data *pdata) { int ret; - if ((pdata == NULL) || (pdata->power == NULL)) + if (pdata == NULL) return; - memcpy(&power_pdata, pdata->power, sizeof(struct pm860x_power_pdata)); - power_devs[0].mfd_data = &power_pdata; + power_devs[0].platform_data = pdata->power; + power_devs[0].pdata_size = sizeof(struct pm860x_power_pdata); power_devs[0].num_resources = ARRAY_SIZE(battery_resources); power_devs[0].resources = &battery_resources[0], ret = mfd_add_devices(chip->dev, 0, &power_devs[0], 1, @@ -670,7 +665,8 @@ static void __devinit device_power_init(struct pm860x_chip *chip, if (ret < 0) dev_err(chip->dev, "Failed to add battery subdev\n"); - power_devs[1].mfd_data = &power_pdata; + power_devs[1].platform_data = pdata->power; + power_devs[1].pdata_size = sizeof(struct pm860x_power_pdata); power_devs[1].num_resources = ARRAY_SIZE(charger_resources); power_devs[1].resources = &charger_resources[0], ret = mfd_add_devices(chip->dev, 0, &power_devs[1], 1, @@ -680,7 +676,6 @@ static void __devinit device_power_init(struct pm860x_chip *chip, } static void __devinit device_onkey_init(struct pm860x_chip *chip, - struct i2c_client *i2c, struct pm860x_platform_data *pdata) { int ret; @@ -695,7 +690,6 @@ static void __devinit device_onkey_init(struct pm860x_chip *chip, } static void __devinit device_codec_init(struct pm860x_chip *chip, - struct i2c_client *i2c, struct pm860x_platform_data *pdata) { int ret; @@ -763,11 +757,12 @@ static void __devinit device_8607_init(struct pm860x_chip *chip, if (ret < 0) goto out; - device_regulator_init(chip, i2c, pdata); - device_onkey_init(chip, i2c, pdata); - device_touch_init(chip, i2c, pdata); - device_power_init(chip, i2c, pdata); - device_codec_init(chip, i2c, pdata); + device_regulator_init(chip, pdata); + device_rtc_init(chip, pdata); + device_onkey_init(chip, pdata); + device_touch_init(chip, pdata); + device_power_init(chip, pdata); + device_codec_init(chip, pdata); out: return; } @@ -779,8 +774,8 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip, switch (chip->id) { case CHIP_PM8606: - device_bk_init(chip, chip->client, pdata); - device_led_init(chip, chip->client, pdata); + device_bk_init(chip, pdata); + device_led_init(chip, pdata); break; case CHIP_PM8607: device_8607_init(chip, chip->client, pdata); @@ -790,8 +785,8 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip, if (chip->companion) { switch (chip->id) { case CHIP_PM8607: - device_bk_init(chip, chip->companion, pdata); - device_led_init(chip, chip->companion, pdata); + device_bk_init(chip, pdata); + device_led_init(chip, pdata); break; case CHIP_PM8606: device_8607_init(chip, chip->companion, pdata); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 481770ab2716..8344fc0ab858 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -157,6 +157,20 @@ config TPS6507X This driver can also be built as a module. If so, the module will be called tps6507x. +config MFD_TPS6586X + bool "TPS6586x Power Management chips" + depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS + select MFD_CORE + 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 MENELAUS bool "Texas Instruments TWL92330/Menelaus PM chip" depends on I2C=y && ARCH_OMAP2 @@ -455,6 +469,20 @@ config MFD_PCF50633 facilities, and registers devices for the various functions so that function-specific drivers can bind to them. +config PCF50633_ADC + tristate "Support for 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" + 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 @@ -470,20 +498,6 @@ config MFD_MC13XXX additional drivers must be enabled in order to use the functionality of the device. -config PCF50633_ADC - tristate "Support for 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" - depends on MFD_PCF50633 - help - Say yes here if you want to include support GPIO for pins on - the PCF50633 chip. - config ABX500_CORE bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions" default y if ARCH_U300 || ARCH_U8500 @@ -649,20 +663,6 @@ config MFD_JZ4740_ADC 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_TPS6586X - bool "TPS6586x Power Management chips" - depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS - select MFD_CORE - 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_VX855 tristate "Support for VIA VX855/VX875 integrated south bridge" depends on PCI @@ -691,6 +691,34 @@ config MFD_OMAP_USB_HOST This MFD driver does the required setup functionalities for OMAP USB Host drivers. +config MFD_PM8XXX + tristate + +config MFD_PM8921_CORE + tristate "Qualcomm PM8921 PMIC chip" + depends on MSM_SSBI + select MFD_CORE + select MFD_PM8XXX + help + If you say yes to this option, support will be included for the + built-in PM8921 PMIC chip. + + 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_PM8XXX_IRQ + bool "Support for Qualcomm PM8xxx IRQ features" + depends on MFD_PM8XXX + default y if MFD_PM8XXX + 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. + endif # MFD_SUPPORT menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 24aa44448daf..1acb8f29a96c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -91,3 +91,5 @@ obj-$(CONFIG_MFD_VX855) += vx855.o obj-$(CONFIG_MFD_WL1273_CORE) += wl1273-core.o obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o +obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o +obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c index a751927047ac..a20e1c41bed2 100644 --- a/drivers/mfd/ab3100-core.c +++ b/drivers/mfd/ab3100-core.c @@ -949,8 +949,10 @@ static int __devinit ab3100_probe(struct i2c_client *client, goto exit_no_ops; /* Set up and register the platform devices. */ - for (i = 0; i < ARRAY_SIZE(ab3100_devs); i++) - ab3100_devs[i].mfd_data = ab3100_plf_data; + for (i = 0; i < ARRAY_SIZE(ab3100_devs); i++) { + ab3100_devs[i].platform_data = ab3100_plf_data; + ab3100_devs[i].pdata_size = sizeof(struct ab3100_platform_data); + } err = mfd_add_devices(&client->dev, 0, ab3100_devs, ARRAY_SIZE(ab3100_devs), NULL, 0); diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c index ff86acf3e6bd..3d7dce671b93 100644 --- a/drivers/mfd/ab3550-core.c +++ b/drivers/mfd/ab3550-core.c @@ -1320,8 +1320,10 @@ static int __init ab3550_probe(struct i2c_client *client, goto exit_no_ops; /* Set up and register the platform devices. */ - for (i = 0; i < AB3550_NUM_DEVICES; i++) - ab3550_devs[i].mfd_data = ab3550_plf_data->dev_data[i]; + for (i = 0; i < AB3550_NUM_DEVICES; i++) { + ab3550_devs[i].platform_data = ab3550_plf_data->dev_data[i]; + ab3550_devs[i].pdata_size = ab3550_plf_data->dev_data_sz[i]; + } err = mfd_add_devices(&client->dev, 0, ab3550_devs, ARRAY_SIZE(ab3550_devs), NULL, diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 67d01c938284..fc0c1af1566e 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -254,8 +254,9 @@ static void ab8500_irq_sync_unlock(struct irq_data *data) if (new == old) continue; - /* Interrupt register 12 does'nt exist prior to version 0x20 */ - if (ab8500_irq_regoffset[i] == 11 && ab8500->chip_id < 0x20) + /* Interrupt register 12 doesn't exist prior to version 2.0 */ + if (ab8500_irq_regoffset[i] == 11 && + ab8500->chip_id < AB8500_CUT2P0) continue; ab8500->oldmask[i] = new; @@ -307,8 +308,8 @@ static irqreturn_t ab8500_irq(int irq, void *dev) int status; u8 value; - /* Interrupt register 12 does'nt exist prior to version 0x20 */ - if (regoffset == 11 && ab8500->chip_id < 0x20) + /* Interrupt register 12 doesn't exist prior to version 2.0 */ + if (regoffset == 11 && ab8500->chip_id < AB8500_CUT2P0) continue; status = get_register_interruptible(ab8500, AB8500_INTERRUPT, @@ -724,17 +725,15 @@ int __devinit ab8500_init(struct ab8500 *ab8500) if (ret < 0) return ret; - /* - * 0x0 - Early Drop - * 0x10 - Cut 1.0 - * 0x11 - Cut 1.1 - * 0x20 - Cut 2.0 - * 0x30 - Cut 3.0 - */ - if (value == 0x0 || value == 0x10 || value == 0x11 || value == 0x20 || - value == 0x30) { + switch (value) { + case AB8500_CUTEARLY: + case AB8500_CUT1P0: + case AB8500_CUT1P1: + case AB8500_CUT2P0: + case AB8500_CUT3P0: dev_info(ab8500->dev, "detected chip, revision: %#x\n", value); - } else { + break; + default: dev_err(ab8500->dev, "unknown chip, revision: %#x\n", value); return -EINVAL; } @@ -763,8 +762,9 @@ int __devinit ab8500_init(struct ab8500 *ab8500) /* Clear and mask all interrupts */ for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { - /* Interrupt register 12 does'nt exist prior to version 0x20 */ - if (ab8500_irq_regoffset[i] == 11 && ab8500->chip_id < 0x20) + /* Interrupt register 12 doesn't exist prior to version 2.0 */ + if (ab8500_irq_regoffset[i] == 11 && + ab8500->chip_id < AB8500_CUT2P0) continue; get_register_interruptible(ab8500, AB8500_INTERRUPT, diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index 6421ad1160de..f16afb234ff9 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -57,6 +57,7 @@ #define SW_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 @@ -101,6 +102,7 @@ struct adc_cal_data { /** * 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 @@ -112,6 +114,7 @@ struct adc_cal_data { * @cal_data array of ADC calibration data structs */ struct ab8500_gpadc { + u8 chip_id; struct device *dev; struct list_head node; struct completion ab8500_gpadc_complete; @@ -274,6 +277,7 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) dev_err(gpadc->dev, "gpadc_conversion: enable gpadc failed\n"); goto out; } + /* Select the input source and set average samples to 16 */ ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, AB8500_GPADC_CTRL2_REG, (input | SW_AVG_16)); @@ -282,9 +286,11 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) "gpadc_conversion: set avg samples failed\n"); goto out; } + /* * Enable ADC, buffering, select rising edge and enable ADC path - * charging current sense if it needed + * charging current sense if it needed, ABB 3.0 needs some special + * treatment too. */ switch (input) { case MAIN_CHARGER_C: @@ -294,6 +300,23 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) EN_BUF | EN_ICHAR, 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 availible + */ + msleep(1); + break; + } + /* Intentional fallthrough */ default: ret = abx500_mask_and_set_register_interruptible(gpadc->dev, AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF); @@ -304,6 +327,7 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) "gpadc_conversion: select falling edge 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) { @@ -552,6 +576,14 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev) 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; + } + gpadc->chip_id = (u8) ret; + /* VTVout LDO used to power up ab8500-GPADC */ gpadc->regu = regulator_get(&pdev->dev, "vddadc"); if (IS_ERR(gpadc->regu)) { diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index 0b4d5b23bec9..c27fd1fc3b86 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -88,19 +88,19 @@ struct asic3 { static int asic3_gpio_get(struct gpio_chip *chip, unsigned offset); -static inline void asic3_write_register(struct asic3 *asic, - unsigned int reg, u32 value) +void asic3_write_register(struct asic3 *asic, unsigned int reg, u32 value) { iowrite16(value, asic->mapping + (reg >> asic->bus_shift)); } +EXPORT_SYMBOL_GPL(asic3_write_register); -static inline u32 asic3_read_register(struct asic3 *asic, - unsigned int reg) +u32 asic3_read_register(struct asic3 *asic, unsigned int reg) { return ioread16(asic->mapping + (reg >> asic->bus_shift)); } +EXPORT_SYMBOL_GPL(asic3_read_register); static void asic3_set_register(struct asic3 *asic, u32 reg, u32 bits, bool set) { @@ -676,7 +676,8 @@ static struct mfd_cell asic3_cell_ds1wm = { .name = "ds1wm", .enable = ds1wm_enable, .disable = ds1wm_disable, - .mfd_data = &ds1wm_pdata, + .platform_data = &ds1wm_pdata, + .pdata_size = sizeof(ds1wm_pdata), .num_resources = ARRAY_SIZE(ds1wm_resources), .resources = ds1wm_resources, }; @@ -777,12 +778,61 @@ static struct mfd_cell asic3_cell_mmc = { .name = "tmio-mmc", .enable = asic3_mmc_enable, .disable = asic3_mmc_disable, - .mfd_data = &asic3_mmc_data, + .platform_data = &asic3_mmc_data, + .pdata_size = sizeof(asic3_mmc_data), .num_resources = ARRAY_SIZE(asic3_mmc_resources), .resources = asic3_mmc_resources, }; +static const int clock_ledn[ASIC3_NUM_LEDS] = { + [0] = ASIC3_CLOCK_LED0, + [1] = ASIC3_CLOCK_LED1, + [2] = ASIC3_CLOCK_LED2, +}; + +static int asic3_leds_enable(struct platform_device *pdev) +{ + const struct mfd_cell *cell = mfd_get_cell(pdev); + struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); + + asic3_clk_enable(asic, &asic->clocks[clock_ledn[cell->id]]); + + return 0; +} + +static int asic3_leds_disable(struct platform_device *pdev) +{ + const struct mfd_cell *cell = mfd_get_cell(pdev); + struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); + + asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]); + + return 0; +} + +static struct mfd_cell asic3_cell_leds[ASIC3_NUM_LEDS] = { + [0] = { + .name = "leds-asic3", + .id = 0, + .enable = asic3_leds_enable, + .disable = asic3_leds_disable, + }, + [1] = { + .name = "leds-asic3", + .id = 1, + .enable = asic3_leds_enable, + .disable = asic3_leds_disable, + }, + [2] = { + .name = "leds-asic3", + .id = 2, + .enable = asic3_leds_enable, + .disable = asic3_leds_disable, + }, +}; + static int __init asic3_mfd_probe(struct platform_device *pdev, + struct asic3_platform_data *pdata, struct resource *mem) { struct asic3 *asic = platform_get_drvdata(pdev); @@ -806,7 +856,8 @@ static int __init asic3_mfd_probe(struct platform_device *pdev, /* MMC */ asic->tmio_cnf = ioremap((ASIC3_SD_CONFIG_BASE >> asic->bus_shift) + - mem_sdio->start, 0x400 >> asic->bus_shift); + mem_sdio->start, + ASIC3_SD_CONFIG_SIZE >> asic->bus_shift); if (!asic->tmio_cnf) { ret = -ENOMEM; dev_dbg(asic->dev, "Couldn't ioremap SD_CONFIG\n"); @@ -820,9 +871,23 @@ static int __init asic3_mfd_probe(struct platform_device *pdev, if (ret < 0) goto out; - if (mem_sdio && (irq >= 0)) + if (mem_sdio && (irq >= 0)) { ret = mfd_add_devices(&pdev->dev, pdev->id, &asic3_cell_mmc, 1, mem_sdio, irq); + if (ret < 0) + goto out; + } + + if (pdata->leds) { + int i; + + for (i = 0; i < ASIC3_NUM_LEDS; ++i) { + asic3_cell_leds[i].platform_data = &pdata->leds[i]; + asic3_cell_leds[i].pdata_size = sizeof(pdata->leds[i]); + } + ret = mfd_add_devices(&pdev->dev, 0, + asic3_cell_leds, ASIC3_NUM_LEDS, NULL, 0); + } out: return ret; @@ -903,7 +968,7 @@ static int __init asic3_probe(struct platform_device *pdev) */ memcpy(asic->clocks, asic3_clk_init, sizeof(asic3_clk_init)); - asic3_mfd_probe(pdev, mem); + asic3_mfd_probe(pdev, pdata, mem); dev_info(asic->dev, "ASIC3 Core driver\n"); diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c index 414783b04849..4e2af2cb2d26 100644 --- a/drivers/mfd/davinci_voicecodec.c +++ b/drivers/mfd/davinci_voicecodec.c @@ -119,12 +119,14 @@ static int __init davinci_vc_probe(struct platform_device *pdev) /* Voice codec interface client */ cell = &davinci_vc->cells[DAVINCI_VC_VCIF_CELL]; cell->name = "davinci-vcif"; - cell->mfd_data = davinci_vc; + cell->platform_data = davinci_vc; + cell->pdata_size = sizeof(*davinci_vc); /* Voice codec CQ93VC client */ cell = &davinci_vc->cells[DAVINCI_VC_CQ93VC_CELL]; cell->name = "cq93vc-codec"; - cell->mfd_data = davinci_vc; + cell->platform_data = davinci_vc; + cell->pdata_size = sizeof(*davinci_vc); ret = mfd_add_devices(&pdev->dev, pdev->id, davinci_vc->cells, DAVINCI_VC_CELLS, NULL, 0); diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c index fb9770b39a32..2808bd125d13 100644 --- a/drivers/mfd/htc-pasic3.c +++ b/drivers/mfd/htc-pasic3.c @@ -117,7 +117,8 @@ static struct mfd_cell ds1wm_cell __initdata = { .name = "ds1wm", .enable = ds1wm_enable, .disable = ds1wm_disable, - .mfd_data = &ds1wm_pdata, + .platform_data = &ds1wm_pdata, + .pdata_size = sizeof(ds1wm_pdata), .num_resources = 2, .resources = ds1wm_resources, }; @@ -172,6 +173,8 @@ static int __init pasic3_probe(struct platform_device *pdev) } if (pdata && pdata->led_pdata) { + led_cell.platform_data = pdata->led_pdata; + led_cell.pdata_size = sizeof(struct pasic3_leds_machinfo); ret = mfd_add_devices(&pdev->dev, pdev->id, &led_cell, 1, r, 0); if (ret < 0) dev_warn(dev, "failed to register LED device\n"); diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c index fc4191137e90..5c2a06acb77f 100644 --- a/drivers/mfd/janz-cmodio.c +++ b/drivers/mfd/janz-cmodio.c @@ -86,7 +86,8 @@ static int __devinit cmodio_setup_subdevice(struct cmodio_device *priv, /* Add platform data */ pdata->modno = modno; - cell->mfd_data = pdata; + cell->platform_data = pdata; + cell->pdata_size = sizeof(*pdata); /* MODULbus registers -- PCI BAR3 is big-endian MODULbus access */ res->flags = IORESOURCE_MEM; diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index 58cc5fdde016..e1e59c92f758 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -627,7 +627,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip, goto out_dev; } - if (pdata && pdata->regulator[0]) { + if (pdata) { ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[0], ARRAY_SIZE(regulator_devs), ®ulator_resources[0], 0); diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 668634e89e81..7e4d44bf92ab 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -683,13 +683,14 @@ out: EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion); static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx, - const char *format, void *pdata) + const char *format, void *pdata, size_t pdata_size) { char buf[30]; const char *name = mc13xxx_get_chipname(mc13xxx); struct mfd_cell cell = { - .mfd_data = pdata, + .platform_data = pdata, + .pdata_size = pdata_size, }; /* there is no asnprintf in the kernel :-( */ @@ -705,7 +706,7 @@ static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx, static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format) { - return mc13xxx_add_subdevice_pdata(mc13xxx, format, NULL); + return mc13xxx_add_subdevice_pdata(mc13xxx, format, NULL, 0); } static int mc13xxx_probe(struct spi_device *spi) @@ -764,7 +765,7 @@ err_revision: if (pdata->flags & MC13XXX_USE_REGULATOR) { mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator", - &pdata->regulators); + &pdata->regulators, sizeof(pdata->regulators)); } if (pdata->flags & MC13XXX_USE_RTC) @@ -774,7 +775,8 @@ err_revision: mc13xxx_add_subdevice(mc13xxx, "%s-ts"); if (pdata->flags & MC13XXX_USE_LED) - mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led", pdata->leds); + mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led", + pdata->leds, sizeof(*pdata->leds)); return 0; } diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index f4c8c844b913..0902523af62d 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -88,6 +88,13 @@ static int mfd_add_device(struct device *parent, int id, pdev->dev.parent = parent; + if (cell->pdata_size) { + ret = platform_device_add_data(pdev, + cell->platform_data, cell->pdata_size); + if (ret) + goto fail_res; + } + ret = mfd_platform_add_cell(pdev, cell); if (ret) goto fail_res; diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 3ab9ffa00aad..855219526ccb 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -26,6 +26,7 @@ #include <linux/spinlock.h> #include <linux/gpio.h> #include <plat/usb.h> +#include <linux/pm_runtime.h> #define USBHS_DRIVER_NAME "usbhs-omap" #define OMAP_EHCI_DEVICE "ehci-omap" @@ -146,9 +147,6 @@ struct usbhs_hcd_omap { - struct clk *usbhost_ick; - struct clk *usbhost_hs_fck; - struct clk *usbhost_fs_fck; struct clk *xclk60mhsp1_ck; struct clk *xclk60mhsp2_ck; struct clk *utmi_p1_fck; @@ -158,8 +156,6 @@ struct usbhs_hcd_omap { struct clk *usbhost_p2_fck; struct clk *usbtll_p2_fck; struct clk *init_60m_fclk; - struct clk *usbtll_fck; - struct clk *usbtll_ick; void __iomem *uhh_base; void __iomem *tll_base; @@ -281,6 +277,7 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev) if (!ehci) { dev_err(dev, "omap_usbhs_alloc_child failed\n"); + ret = -ENOMEM; goto err_end; } @@ -304,13 +301,14 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev) sizeof(*ohci_data), dev); if (!ohci) { dev_err(dev, "omap_usbhs_alloc_child failed\n"); + ret = -ENOMEM; goto err_ehci; } return 0; err_ehci: - platform_device_put(ehci); + platform_device_unregister(ehci); err_end: return ret; @@ -351,46 +349,13 @@ static int __devinit usbhs_omap_probe(struct platform_device *pdev) omap->platdata.ehci_data = pdata->ehci_data; omap->platdata.ohci_data = pdata->ohci_data; - omap->usbhost_ick = clk_get(dev, "usbhost_ick"); - if (IS_ERR(omap->usbhost_ick)) { - ret = PTR_ERR(omap->usbhost_ick); - dev_err(dev, "usbhost_ick failed error:%d\n", ret); - goto err_end; - } - - omap->usbhost_hs_fck = clk_get(dev, "hs_fck"); - if (IS_ERR(omap->usbhost_hs_fck)) { - ret = PTR_ERR(omap->usbhost_hs_fck); - dev_err(dev, "usbhost_hs_fck failed error:%d\n", ret); - goto err_usbhost_ick; - } - - omap->usbhost_fs_fck = clk_get(dev, "fs_fck"); - if (IS_ERR(omap->usbhost_fs_fck)) { - ret = PTR_ERR(omap->usbhost_fs_fck); - dev_err(dev, "usbhost_fs_fck failed error:%d\n", ret); - goto err_usbhost_hs_fck; - } - - omap->usbtll_fck = clk_get(dev, "usbtll_fck"); - if (IS_ERR(omap->usbtll_fck)) { - ret = PTR_ERR(omap->usbtll_fck); - dev_err(dev, "usbtll_fck failed error:%d\n", ret); - goto err_usbhost_fs_fck; - } - - omap->usbtll_ick = clk_get(dev, "usbtll_ick"); - if (IS_ERR(omap->usbtll_ick)) { - ret = PTR_ERR(omap->usbtll_ick); - dev_err(dev, "usbtll_ick failed error:%d\n", ret); - goto err_usbtll_fck; - } + pm_runtime_enable(&pdev->dev); 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_usbtll_ick; + goto err_end; } omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck"); @@ -520,22 +485,8 @@ err_xclk60mhsp1_ck: err_utmi_p1_fck: clk_put(omap->utmi_p1_fck); -err_usbtll_ick: - clk_put(omap->usbtll_ick); - -err_usbtll_fck: - clk_put(omap->usbtll_fck); - -err_usbhost_fs_fck: - clk_put(omap->usbhost_fs_fck); - -err_usbhost_hs_fck: - clk_put(omap->usbhost_hs_fck); - -err_usbhost_ick: - clk_put(omap->usbhost_ick); - err_end: + pm_runtime_disable(&pdev->dev); kfree(omap); end_probe: @@ -569,11 +520,7 @@ static int __devexit usbhs_omap_remove(struct platform_device *pdev) clk_put(omap->utmi_p2_fck); clk_put(omap->xclk60mhsp1_ck); clk_put(omap->utmi_p1_fck); - clk_put(omap->usbtll_ick); - clk_put(omap->usbtll_fck); - clk_put(omap->usbhost_fs_fck); - clk_put(omap->usbhost_hs_fck); - clk_put(omap->usbhost_ick); + pm_runtime_disable(&pdev->dev); kfree(omap); return 0; @@ -693,7 +640,6 @@ static int usbhs_enable(struct device *dev) struct usbhs_omap_platform_data *pdata = &omap->platdata; unsigned long flags = 0; int ret = 0; - unsigned long timeout; unsigned reg; dev_dbg(dev, "starting TI HSUSB Controller\n"); @@ -706,11 +652,7 @@ static int usbhs_enable(struct device *dev) if (omap->count > 0) goto end_count; - clk_enable(omap->usbhost_ick); - clk_enable(omap->usbhost_hs_fck); - clk_enable(omap->usbhost_fs_fck); - clk_enable(omap->usbtll_fck); - clk_enable(omap->usbtll_ick); + pm_runtime_get_sync(dev); if (pdata->ehci_data->phy_reset) { if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) { @@ -734,50 +676,6 @@ static int usbhs_enable(struct device *dev) omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION); dev_dbg(dev, "OMAP UHH_REVISION 0x%x\n", omap->usbhs_rev); - /* perform TLL soft reset, and wait until reset is complete */ - usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG, - OMAP_USBTLL_SYSCONFIG_SOFTRESET); - - /* Wait for TLL reset to complete */ - timeout = jiffies + msecs_to_jiffies(1000); - while (!(usbhs_read(omap->tll_base, OMAP_USBTLL_SYSSTATUS) - & OMAP_USBTLL_SYSSTATUS_RESETDONE)) { - cpu_relax(); - - if (time_after(jiffies, timeout)) { - dev_dbg(dev, "operation timed out\n"); - ret = -EINVAL; - goto err_tll; - } - } - - dev_dbg(dev, "TLL RESET DONE\n"); - - /* (1<<3) = no idle mode only for initial debugging */ - usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG, - OMAP_USBTLL_SYSCONFIG_ENAWAKEUP | - OMAP_USBTLL_SYSCONFIG_SIDLEMODE | - OMAP_USBTLL_SYSCONFIG_AUTOIDLE); - - /* Put UHH in NoIdle/NoStandby mode */ - reg = usbhs_read(omap->uhh_base, OMAP_UHH_SYSCONFIG); - if (is_omap_usbhs_rev1(omap)) { - reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP - | OMAP_UHH_SYSCONFIG_SIDLEMODE - | OMAP_UHH_SYSCONFIG_CACTIVITY - | OMAP_UHH_SYSCONFIG_MIDLEMODE); - reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE; - - - } else if (is_omap_usbhs_rev2(omap)) { - reg &= ~OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR; - reg |= OMAP4_UHH_SYSCONFIG_NOIDLE; - reg &= ~OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR; - reg |= OMAP4_UHH_SYSCONFIG_NOSTDBY; - } - - usbhs_write(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg); - reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG); /* setup ULPI bypass and burst configurations */ reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN @@ -917,6 +815,8 @@ end_count: return 0; err_tll: + pm_runtime_put_sync(dev); + spin_unlock_irqrestore(&omap->lock, flags); 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]); @@ -924,13 +824,6 @@ err_tll: if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) gpio_free(pdata->ehci_data->reset_gpio_port[1]); } - - clk_disable(omap->usbtll_ick); - clk_disable(omap->usbtll_fck); - clk_disable(omap->usbhost_fs_fck); - clk_disable(omap->usbhost_hs_fck); - clk_disable(omap->usbhost_ick); - spin_unlock_irqrestore(&omap->lock, flags); return ret; } @@ -994,6 +887,20 @@ static void usbhs_disable(struct device *dev) dev_dbg(dev, "operation timed out\n"); } + if (is_omap_usbhs_rev2(omap)) { + if (is_ehci_tll_mode(pdata->port_mode[0])) + clk_enable(omap->usbtll_p1_fck); + if (is_ehci_tll_mode(pdata->port_mode[1])) + clk_enable(omap->usbtll_p2_fck); + clk_disable(omap->utmi_p2_fck); + clk_disable(omap->utmi_p1_fck); + } + + pm_runtime_put_sync(dev); + + /* The gpio_free migh sleep; so unlock the spinlock */ + spin_unlock_irqrestore(&omap->lock, flags); + 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]); @@ -1001,14 +908,7 @@ static void usbhs_disable(struct device *dev) if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) gpio_free(pdata->ehci_data->reset_gpio_port[1]); } - - clk_disable(omap->utmi_p2_fck); - clk_disable(omap->utmi_p1_fck); - clk_disable(omap->usbtll_ick); - clk_disable(omap->usbtll_fck); - clk_disable(omap->usbhost_fs_fck); - clk_disable(omap->usbhost_hs_fck); - clk_disable(omap->usbhost_ick); + return; end_disble: spin_unlock_irqrestore(&omap->lock, flags); diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c new file mode 100644 index 000000000000..e873b15753d8 --- /dev/null +++ b/drivers/mfd/pm8921-core.c @@ -0,0 +1,212 @@ +/* + * 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/kernel.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/msm_ssbi.h> +#include <linux/mfd/core.h> +#include <linux/mfd/pm8xxx/pm8921.h> +#include <linux/mfd/pm8xxx/core.h> + +#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; +}; + +static int pm8921_readb(const struct device *dev, u16 addr, u8 *val) +{ + const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); + const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; + + return msm_ssbi_read(pmic->dev->parent, addr, val, 1); +} + +static int pm8921_writeb(const struct device *dev, u16 addr, u8 val) +{ + const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); + const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; + + return msm_ssbi_write(pmic->dev->parent, addr, &val, 1); +} + +static int pm8921_read_buf(const struct device *dev, u16 addr, u8 *buf, + int cnt) +{ + const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); + const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; + + return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt); +} + +static int pm8921_write_buf(const struct device *dev, u16 addr, u8 *buf, + int cnt) +{ + const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); + const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; + + return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt); +} + +static int pm8921_read_irq_stat(const struct device *dev, int irq) +{ + const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); + const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; + + return pm8xxx_get_irq_stat(pmic->irq_chip, irq); +} + +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 int __devinit pm8921_add_subdevices(const struct pm8921_platform_data + *pdata, + struct pm8921 *pmic, + u32 rev) +{ + 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; + } + return ret; +} + +static int __devinit pm8921_probe(struct platform_device *pdev) +{ + const struct pm8921_platform_data *pdata = pdev->dev.platform_data; + struct pm8921 *pmic; + int rc; + u8 val; + u32 rev; + + if (!pdata) { + pr_err("missing platform data\n"); + return -EINVAL; + } + + pmic = kzalloc(sizeof(struct pm8921), GFP_KERNEL); + if (!pmic) { + pr_err("Cannot alloc pm8921 struct\n"); + return -ENOMEM; + } + + /* Read PMIC chip revision */ + rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val)); + if (rc) { + pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc); + goto err_read_rev; + } + 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)); + if (rc) { + pr_err("Failed to read hw rev 2 reg %d:rc=%d\n", + REG_HWREV_2, rc); + goto err_read_rev; + } + 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); + + rc = pm8921_add_subdevices(pdata, pmic, rev); + if (rc) { + pr_err("Cannot add subdevices rc=%d\n", rc); + goto err; + } + + /* gpio might not work if no irq device is found */ + WARN_ON(pmic->irq_chip == NULL); + + 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) +{ + 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); + + return 0; +} + +static struct platform_driver pm8921_driver = { + .probe = pm8921_probe, + .remove = __devexit_p(pm8921_remove), + .driver = { + .name = "pm8921-core", + .owner = THIS_MODULE, + }, +}; + +static int __init pm8921_init(void) +{ + return platform_driver_register(&pm8921_driver); +} +subsys_initcall(pm8921_init); + +static void __exit pm8921_exit(void) +{ + platform_driver_unregister(&pm8921_driver); +} +module_exit(pm8921_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PMIC 8921 core driver"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("platform:pm8921-core"); diff --git a/drivers/mfd/pm8xxx-irq.c b/drivers/mfd/pm8xxx-irq.c new file mode 100644 index 000000000000..d452dd013081 --- /dev/null +++ b/drivers/mfd/pm8xxx-irq.c @@ -0,0 +1,371 @@ +/* + * 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/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c index 10dbe6374a89..809bd4a61089 100644 --- a/drivers/mfd/rdc321x-southbridge.c +++ b/drivers/mfd/rdc321x-southbridge.c @@ -61,12 +61,14 @@ static struct mfd_cell rdc321x_sb_cells[] = { .name = "rdc321x-wdt", .resources = rdc321x_wdt_resource, .num_resources = ARRAY_SIZE(rdc321x_wdt_resource), - .mfd_data = &rdc321x_wdt_pdata, + .platform_data = &rdc321x_wdt_pdata, + .pdata_size = sizeof(rdc321x_wdt_pdata), }, { .name = "rdc321x-gpio", .resources = rdc321x_gpio_resources, .num_resources = ARRAY_SIZE(rdc321x_gpio_resources), - .mfd_data = &rdc321x_gpio_pdata, + .platform_data = &rdc321x_gpio_pdata, + .pdata_size = sizeof(rdc321x_gpio_pdata), }, }; diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c index 42830e692964..91ad21ef7721 100644 --- a/drivers/mfd/t7l66xb.c +++ b/drivers/mfd/t7l66xb.c @@ -170,7 +170,8 @@ static struct mfd_cell t7l66xb_cells[] = { .name = "tmio-mmc", .enable = t7l66xb_mmc_enable, .disable = t7l66xb_mmc_disable, - .mfd_data = &t7166xb_mmc_data, + .platform_data = &t7166xb_mmc_data, + .pdata_size = sizeof(t7166xb_mmc_data), .num_resources = ARRAY_SIZE(t7l66xb_mmc_resources), .resources = t7l66xb_mmc_resources, }, @@ -382,7 +383,8 @@ static int t7l66xb_probe(struct platform_device *dev) t7l66xb_attach_irq(dev); - t7l66xb_cells[T7L66XB_CELL_NAND].mfd_data = pdata->nand_data; + t7l66xb_cells[T7L66XB_CELL_NAND].platform_data = pdata->nand_data; + t7l66xb_cells[T7L66XB_CELL_NAND].pdata_size = sizeof(*pdata->nand_data); ret = mfd_add_devices(&dev->dev, dev->id, t7l66xb_cells, ARRAY_SIZE(t7l66xb_cells), diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c index b006f7cee952..ad715bf49cac 100644 --- a/drivers/mfd/tc6387xb.c +++ b/drivers/mfd/tc6387xb.c @@ -131,7 +131,8 @@ static struct mfd_cell tc6387xb_cells[] = { .name = "tmio-mmc", .enable = tc6387xb_mmc_enable, .disable = tc6387xb_mmc_disable, - .mfd_data = &tc6387xb_mmc_data, + .platform_data = &tc6387xb_mmc_data, + .pdata_size = sizeof(tc6387xb_mmc_data), .num_resources = ARRAY_SIZE(tc6387xb_mmc_resources), .resources = tc6387xb_mmc_resources, }, diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index fc53ce287601..9612264f0e6d 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -393,7 +393,8 @@ static struct mfd_cell __devinitdata tc6393xb_cells[] = { .name = "tmio-mmc", .enable = tc6393xb_mmc_enable, .resume = tc6393xb_mmc_resume, - .mfd_data = &tc6393xb_mmc_data, + .platform_data = &tc6393xb_mmc_data, + .pdata_size = sizeof(tc6393xb_mmc_data), .num_resources = ARRAY_SIZE(tc6393xb_mmc_resources), .resources = tc6393xb_mmc_resources, }, @@ -692,8 +693,11 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) goto err_setup; } - tc6393xb_cells[TC6393XB_CELL_NAND].mfd_data = tcpd->nand_data; - tc6393xb_cells[TC6393XB_CELL_FB].mfd_data = tcpd->fb_data; + tc6393xb_cells[TC6393XB_CELL_NAND].platform_data = tcpd->nand_data; + tc6393xb_cells[TC6393XB_CELL_NAND].pdata_size = + sizeof(*tcpd->nand_data); + tc6393xb_cells[TC6393XB_CELL_FB].platform_data = tcpd->fb_data; + tc6393xb_cells[TC6393XB_CELL_FB].pdata_size = sizeof(*tcpd->fb_data); ret = mfd_add_devices(&dev->dev, dev->id, tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells), diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index 94c6c8afad12..69272e4e3459 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -384,7 +384,8 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, - .mfd_data = &timb_dma_platform_data, + .platform_data = &timb_dma_platform_data, + .pdata_size = sizeof(timb_dma_platform_data), }, { .name = "timb-uart", @@ -395,37 +396,43 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = { .name = "xiic-i2c", .num_resources = ARRAY_SIZE(timberdale_xiic_resources), .resources = timberdale_xiic_resources, - .mfd_data = &timberdale_xiic_platform_data, + .platform_data = &timberdale_xiic_platform_data, + .pdata_size = sizeof(timberdale_xiic_platform_data), }, { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .mfd_data = &timberdale_gpio_platform_data, + .platform_data = &timberdale_gpio_platform_data, + .pdata_size = sizeof(timberdale_gpio_platform_data), }, { .name = "timb-video", .num_resources = ARRAY_SIZE(timberdale_video_resources), .resources = timberdale_video_resources, - .mfd_data = &timberdale_video_platform_data, + .platform_data = &timberdale_video_platform_data, + .pdata_size = sizeof(timberdale_video_platform_data), }, { .name = "timb-radio", .num_resources = ARRAY_SIZE(timberdale_radio_resources), .resources = timberdale_radio_resources, - .mfd_data = &timberdale_radio_platform_data, + .platform_data = &timberdale_radio_platform_data, + .pdata_size = sizeof(timberdale_radio_platform_data), }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), .resources = timberdale_spi_resources, - .mfd_data = &timberdale_xspi_platform_data, + .platform_data = &timberdale_xspi_platform_data, + .pdata_size = sizeof(timberdale_xspi_platform_data), }, { .name = "ks8842", .num_resources = ARRAY_SIZE(timberdale_eth_resources), .resources = timberdale_eth_resources, - .mfd_data = &timberdale_ks8842_platform_data, + .platform_data = &timberdale_ks8842_platform_data, + .pdata_size = sizeof(timberdale_ks8842_platform_data), }, }; @@ -434,7 +441,8 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, - .mfd_data = &timb_dma_platform_data, + .platform_data = &timb_dma_platform_data, + .pdata_size = sizeof(timb_dma_platform_data), }, { .name = "timb-uart", @@ -450,13 +458,15 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { .name = "xiic-i2c", .num_resources = ARRAY_SIZE(timberdale_xiic_resources), .resources = timberdale_xiic_resources, - .mfd_data = &timberdale_xiic_platform_data, + .platform_data = &timberdale_xiic_platform_data, + .pdata_size = sizeof(timberdale_xiic_platform_data), }, { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .mfd_data = &timberdale_gpio_platform_data, + .platform_data = &timberdale_gpio_platform_data, + .pdata_size = sizeof(timberdale_gpio_platform_data), }, { .name = "timb-mlogicore", @@ -467,25 +477,29 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { .name = "timb-video", .num_resources = ARRAY_SIZE(timberdale_video_resources), .resources = timberdale_video_resources, - .mfd_data = &timberdale_video_platform_data, + .platform_data = &timberdale_video_platform_data, + .pdata_size = sizeof(timberdale_video_platform_data), }, { .name = "timb-radio", .num_resources = ARRAY_SIZE(timberdale_radio_resources), .resources = timberdale_radio_resources, - .mfd_data = &timberdale_radio_platform_data, + .platform_data = &timberdale_radio_platform_data, + .pdata_size = sizeof(timberdale_radio_platform_data), }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), .resources = timberdale_spi_resources, - .mfd_data = &timberdale_xspi_platform_data, + .platform_data = &timberdale_xspi_platform_data, + .pdata_size = sizeof(timberdale_xspi_platform_data), }, { .name = "ks8842", .num_resources = ARRAY_SIZE(timberdale_eth_resources), .resources = timberdale_eth_resources, - .mfd_data = &timberdale_ks8842_platform_data, + .platform_data = &timberdale_ks8842_platform_data, + .pdata_size = sizeof(timberdale_ks8842_platform_data), }, }; @@ -494,7 +508,8 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, - .mfd_data = &timb_dma_platform_data, + .platform_data = &timb_dma_platform_data, + .pdata_size = sizeof(timb_dma_platform_data), }, { .name = "timb-uart", @@ -505,31 +520,36 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = { .name = "xiic-i2c", .num_resources = ARRAY_SIZE(timberdale_xiic_resources), .resources = timberdale_xiic_resources, - .mfd_data = &timberdale_xiic_platform_data, + .platform_data = &timberdale_xiic_platform_data, + .pdata_size = sizeof(timberdale_xiic_platform_data), }, { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .mfd_data = &timberdale_gpio_platform_data, + .platform_data = &timberdale_gpio_platform_data, + .pdata_size = sizeof(timberdale_gpio_platform_data), }, { .name = "timb-video", .num_resources = ARRAY_SIZE(timberdale_video_resources), .resources = timberdale_video_resources, - .mfd_data = &timberdale_video_platform_data, + .platform_data = &timberdale_video_platform_data, + .pdata_size = sizeof(timberdale_video_platform_data), }, { .name = "timb-radio", .num_resources = ARRAY_SIZE(timberdale_radio_resources), .resources = timberdale_radio_resources, - .mfd_data = &timberdale_radio_platform_data, + .platform_data = &timberdale_radio_platform_data, + .pdata_size = sizeof(timberdale_radio_platform_data), }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), .resources = timberdale_spi_resources, - .mfd_data = &timberdale_xspi_platform_data, + .platform_data = &timberdale_xspi_platform_data, + .pdata_size = sizeof(timberdale_xspi_platform_data), }, }; @@ -538,7 +558,8 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, - .mfd_data = &timb_dma_platform_data, + .platform_data = &timb_dma_platform_data, + .pdata_size = sizeof(timb_dma_platform_data), }, { .name = "timb-uart", @@ -549,37 +570,43 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = { .name = "ocores-i2c", .num_resources = ARRAY_SIZE(timberdale_ocores_resources), .resources = timberdale_ocores_resources, - .mfd_data = &timberdale_ocores_platform_data, + .platform_data = &timberdale_ocores_platform_data, + .pdata_size = sizeof(timberdale_ocores_platform_data), }, { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .mfd_data = &timberdale_gpio_platform_data, + .platform_data = &timberdale_gpio_platform_data, + .pdata_size = sizeof(timberdale_gpio_platform_data), }, { .name = "timb-video", .num_resources = ARRAY_SIZE(timberdale_video_resources), .resources = timberdale_video_resources, - .mfd_data = &timberdale_video_platform_data, + .platform_data = &timberdale_video_platform_data, + .pdata_size = sizeof(timberdale_video_platform_data), }, { .name = "timb-radio", .num_resources = ARRAY_SIZE(timberdale_radio_resources), .resources = timberdale_radio_resources, - .mfd_data = &timberdale_radio_platform_data, + .platform_data = &timberdale_radio_platform_data, + .pdata_size = sizeof(timberdale_radio_platform_data), }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), .resources = timberdale_spi_resources, - .mfd_data = &timberdale_xspi_platform_data, + .platform_data = &timberdale_xspi_platform_data, + .pdata_size = sizeof(timberdale_xspi_platform_data), }, { .name = "ks8842", .num_resources = ARRAY_SIZE(timberdale_eth_resources), .resources = timberdale_eth_resources, - .mfd_data = &timberdale_ks8842_platform_data, + .platform_data = &timberdale_ks8842_platform_data, + .pdata_size = sizeof(timberdale_ks8842_platform_data), }, }; diff --git a/drivers/mfd/tps6105x.c b/drivers/mfd/tps6105x.c index 46d8205646b6..a293b978e27c 100644 --- a/drivers/mfd/tps6105x.c +++ b/drivers/mfd/tps6105x.c @@ -183,7 +183,8 @@ static int __devinit tps6105x_probe(struct i2c_client *client, /* Set up and register the platform devices. */ for (i = 0; i < ARRAY_SIZE(tps6105x_cells); i++) { /* One state holder for all drivers, this is simple */ - tps6105x_cells[i].mfd_data = tps6105x; + tps6105x_cells[i].platform_data = tps6105x; + tps6105x_cells[i].pdata_size = sizeof(*tps6105x); } ret = mfd_add_devices(&client->dev, 0, tps6105x_cells, diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index b600808690c1..bba26d96c240 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -270,8 +270,8 @@ static void tps6586x_gpio_set(struct gpio_chip *chip, unsigned offset, { struct tps6586x *tps6586x = container_of(chip, struct tps6586x, gpio); - __tps6586x_write(tps6586x->client, TPS6586X_GPIOSET2, - value << offset); + tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET2, + value << offset, 1 << offset); } static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset, diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 960b5bed7f52..b8f2a4e7f6e7 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -198,6 +198,7 @@ #define TWL6030_BASEADD_GASGAUGE 0x00C0 #define TWL6030_BASEADD_PIH 0x00D0 #define TWL6030_BASEADD_CHARGER 0x00E0 +#define TWL6025_BASEADD_CHARGER 0x00DA /* subchip/slave 2 0x4A - DFT */ #define TWL6030_BASEADD_DIEID 0x00C0 @@ -229,6 +230,9 @@ /* 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) { @@ -328,6 +332,7 @@ static struct twl_mapping twl6030_map[] = { { SUB_CHIP_ID0, TWL6030_BASEADD_RTC }, { SUB_CHIP_ID0, TWL6030_BASEADD_MEM }, + { SUB_CHIP_ID1, TWL6025_BASEADD_CHARGER }, }; /*----------------------------------------------------------------------*/ @@ -487,6 +492,58 @@ EXPORT_SYMBOL(twl_i2c_read_u8); /*----------------------------------------------------------------------*/ +/** + * twl_read_idcode_register - API to read the IDCODE register. + * + * Unlocks the IDCODE register and read the 32 bit value. + */ +static int twl_read_idcode_register(void) +{ + int err; + + err = twl_i2c_write_u8(TWL4030_MODULE_INTBR, TWL_EEPROM_R_UNLOCK, + REG_UNLOCK_TEST_REG); + if (err) { + pr_err("TWL4030 Unable to unlock IDCODE registers -%d\n", err); + goto fail; + } + + err = twl_i2c_read(TWL4030_MODULE_INTBR, (u8 *)(&twl_idcode), + REG_IDCODE_7_0, 4); + if (err) { + pr_err("TWL4030: unable to read IDCODE -%d\n", err); + goto fail; + } + + err = twl_i2c_write_u8(TWL4030_MODULE_INTBR, 0x0, REG_UNLOCK_TEST_REG); + if (err) + pr_err("TWL4030 Unable to relock IDCODE registers -%d\n", err); +fail: + return err; +} + +/** + * twl_get_type - API to get TWL Si type. + * + * Api to get the TWL Si type from IDCODE value. + */ +int twl_get_type(void) +{ + return TWL_SIL_TYPE(twl_idcode); +} +EXPORT_SYMBOL_GPL(twl_get_type); + +/** + * twl_get_version - API to get TWL Si version. + * + * Api to get the TWL Si version from IDCODE value. + */ +int twl_get_version(void) +{ + return TWL_SIL_REV(twl_idcode); +} +EXPORT_SYMBOL_GPL(twl_get_version); + static struct device * add_numbered_child(unsigned chip, const char *name, int num, void *pdata, unsigned pdata_len, @@ -549,7 +606,7 @@ static inline struct device *add_child(unsigned chip, const char *name, static struct device * add_regulator_linked(int num, struct regulator_init_data *pdata, struct regulator_consumer_supply *consumers, - unsigned num_consumers) + unsigned num_consumers, unsigned long features) { unsigned sub_chip_id; /* regulator framework demands init_data ... */ @@ -561,6 +618,8 @@ add_regulator_linked(int num, struct regulator_init_data *pdata, pdata->num_consumer_supplies = num_consumers; } + pdata->driver_data = (void *)features; + /* 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, @@ -568,9 +627,10 @@ add_regulator_linked(int num, struct regulator_init_data *pdata, } static struct device * -add_regulator(int num, struct regulator_init_data *pdata) +add_regulator(int num, struct regulator_init_data *pdata, + unsigned long features) { - return add_regulator_linked(num, pdata, NULL, 0); + return add_regulator_linked(num, pdata, NULL, 0, features); } /* @@ -650,17 +710,20 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) }; child = add_regulator_linked(TWL4030_REG_VUSB1V5, - &usb_fixed, &usb1v5, 1); + &usb_fixed, &usb1v5, 1, + features); if (IS_ERR(child)) return PTR_ERR(child); child = add_regulator_linked(TWL4030_REG_VUSB1V8, - &usb_fixed, &usb1v8, 1); + &usb_fixed, &usb1v8, 1, + features); if (IS_ERR(child)) return PTR_ERR(child); child = add_regulator_linked(TWL4030_REG_VUSB3V1, - &usb_fixed, &usb3v1, 1); + &usb_fixed, &usb3v1, 1, + features); if (IS_ERR(child)) return PTR_ERR(child); @@ -685,9 +748,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) } if (twl_has_usb() && pdata->usb && twl_class_is_6030()) { - static struct regulator_consumer_supply usb3v3 = { - .supply = "vusb", - }; + static struct regulator_consumer_supply usb3v3; + int regulator; if (twl_has_regulator()) { /* this is a template that gets copied */ @@ -700,12 +762,22 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) | REGULATOR_CHANGE_STATUS, }; - child = add_regulator_linked(TWL6030_REG_VUSB, - &usb_fixed, &usb3v3, 1); + 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, @@ -718,7 +790,16 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) /* we need to connect regulators to this transceiver */ if (twl_has_regulator() && child) usb3v3.dev = child; + } else if (twl_has_regulator() && 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 (twl_has_watchdog() && twl_class_is_4030()) { @@ -755,46 +836,55 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) /* twl4030 regulators */ if (twl_has_regulator() && twl_class_is_4030()) { - child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1); + child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL4030_REG_VIO, pdata->vio); + child = add_regulator(TWL4030_REG_VIO, pdata->vio, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL4030_REG_VDD1, pdata->vdd1); + child = add_regulator(TWL4030_REG_VDD1, pdata->vdd1, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL4030_REG_VDD2, pdata->vdd2); + child = add_regulator(TWL4030_REG_VDD2, pdata->vdd2, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL4030_REG_VMMC1, pdata->vmmc1); + child = add_regulator(TWL4030_REG_VMMC1, pdata->vmmc1, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL4030_REG_VDAC, pdata->vdac); + child = add_regulator(TWL4030_REG_VDAC, pdata->vdac, + features); if (IS_ERR(child)) return PTR_ERR(child); child = add_regulator((features & TWL4030_VAUX2) ? TWL4030_REG_VAUX2_4030 : TWL4030_REG_VAUX2, - pdata->vaux2); + pdata->vaux2, features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL4030_REG_VINTANA1, pdata->vintana1); + child = add_regulator(TWL4030_REG_VINTANA1, pdata->vintana1, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL4030_REG_VINTANA2, pdata->vintana2); + child = add_regulator(TWL4030_REG_VINTANA2, pdata->vintana2, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL4030_REG_VINTDIG, pdata->vintdig); + child = add_regulator(TWL4030_REG_VINTDIG, pdata->vintdig, + features); if (IS_ERR(child)) return PTR_ERR(child); } @@ -802,72 +892,152 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) /* maybe add LDOs that are omitted on cost-reduced parts */ if (twl_has_regulator() && !(features & TPS_SUBSET) && twl_class_is_4030()) { - child = add_regulator(TWL4030_REG_VPLL2, pdata->vpll2); + child = add_regulator(TWL4030_REG_VPLL2, pdata->vpll2, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL4030_REG_VMMC2, pdata->vmmc2); + child = add_regulator(TWL4030_REG_VMMC2, pdata->vmmc2, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL4030_REG_VSIM, pdata->vsim); + child = add_regulator(TWL4030_REG_VSIM, pdata->vsim, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL4030_REG_VAUX1, pdata->vaux1); + child = add_regulator(TWL4030_REG_VAUX1, pdata->vaux1, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL4030_REG_VAUX3, pdata->vaux3); + child = add_regulator(TWL4030_REG_VAUX3, pdata->vaux3, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL4030_REG_VAUX4, pdata->vaux4); + child = add_regulator(TWL4030_REG_VAUX4, pdata->vaux4, + features); if (IS_ERR(child)) return PTR_ERR(child); } /* twl6030 regulators */ + if (twl_has_regulator() && twl_class_is_6030() && + !(features & TWL6025_SUBCLASS)) { + 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 (twl_has_regulator() && twl_class_is_6030()) { - child = add_regulator(TWL6030_REG_VMMC, pdata->vmmc); + child = add_regulator(TWL6030_REG_VANA, pdata->vana, + features); if (IS_ERR(child)) return PTR_ERR(child); + } - child = add_regulator(TWL6030_REG_VPP, pdata->vpp); + /* twl6025 regulators */ + if (twl_has_regulator() && 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(TWL6030_REG_VUSIM, pdata->vusim); + child = add_regulator(TWL6025_REG_LDO1, pdata->ldo1, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6030_REG_VANA, pdata->vana); + child = add_regulator(TWL6025_REG_LDO7, pdata->ldo7, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6030_REG_VCXIO, pdata->vcxio); + child = add_regulator(TWL6025_REG_LDO6, pdata->ldo6, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6030_REG_VDAC, pdata->vdac); + child = add_regulator(TWL6025_REG_LDOLN, pdata->ldoln, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6030_REG_VAUX1_6030, pdata->vaux1); + child = add_regulator(TWL6025_REG_LDO2, pdata->ldo2, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6030_REG_VAUX2_6030, pdata->vaux2); + child = add_regulator(TWL6025_REG_LDO4, pdata->ldo4, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6030_REG_VAUX3_6030, pdata->vaux3); + child = add_regulator(TWL6025_REG_LDO3, pdata->ldo3, + features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6030_REG_CLK32KG, pdata->clk32kg); + 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 (twl_has_bci() && pdata->bci && @@ -1014,6 +1184,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) unsigned i; struct twl4030_platform_data *pdata = client->dev.platform_data; u8 temp; + int ret = 0; if (!pdata) { dev_dbg(&client->dev, "no platform data?\n"); @@ -1060,6 +1231,12 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) /* setup clock framework */ clocks_init(&client->dev, pdata->clock); + /* read TWL IDCODE Register */ + if (twl_id == TWL4030_CLASS_ID) { + ret = twl_read_idcode_register(); + WARN(ret < 0, "Error: reading twl_idcode register value\n"); + } + /* load power event scripts */ if (twl_has_power() && pdata->power) twl4030_power_init(pdata->power); @@ -1108,6 +1285,7 @@ static const struct i2c_device_id twl_ids[] = { { "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */ { "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */ { "twl6030", TWL6030_CLASS }, /* "Phoenix power chip" */ + { "twl6025", TWL6030_CLASS | TWL6025_SUBCLASS }, /* "Phoenix lite" */ { /* end of list */ }, }; MODULE_DEVICE_TABLE(i2c, twl_ids); diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c index c02fded316c9..2bf4136464c1 100644 --- a/drivers/mfd/twl4030-codec.c +++ b/drivers/mfd/twl4030-codec.c @@ -1,7 +1,7 @@ /* * MFD driver for twl4030 codec submodule * - * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com> + * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> * * Copyright: (C) 2009 Nokia Corporation * @@ -208,13 +208,15 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) if (pdata->audio) { cell = &codec->cells[childs]; cell->name = "twl4030-codec"; - cell->mfd_data = pdata->audio; + cell->platform_data = pdata->audio; + cell->pdata_size = sizeof(*pdata->audio); childs++; } if (pdata->vibra) { cell = &codec->cells[childs]; cell->name = "twl4030-vibra"; - cell->mfd_data = pdata->vibra; + cell->platform_data = pdata->vibra; + cell->pdata_size = sizeof(*pdata->vibra); childs++; } @@ -270,6 +272,6 @@ static void __devexit twl4030_codec_exit(void) } module_exit(twl4030_codec_exit); -MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>"); +MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c index 2c0d4d16491a..a764676f0922 100644 --- a/drivers/mfd/twl4030-power.c +++ b/drivers/mfd/twl4030-power.c @@ -120,7 +120,7 @@ static u8 res_config_addrs[] = { [RES_HFCLKOUT] = 0x8b, [RES_32KCLKOUT] = 0x8e, [RES_RESET] = 0x91, - [RES_Main_Ref] = 0x94, + [RES_MAIN_REF] = 0x94, }; static int __init twl4030_write_script_byte(u8 address, u8 byte) @@ -448,7 +448,7 @@ static int __init load_twl4030_script(struct twl4030_script *tscript, goto out; } if (tscript->flags & TWL4030_SLEEP_SCRIPT) { - if (order) + if (!order) pr_warning("TWL4030: Bad order of scripts (sleep "\ "script before wakeup) Leads to boot"\ "failure on some boards\n"); @@ -485,9 +485,9 @@ int twl4030_remove_script(u8 flags) return err; } if (flags & TWL4030_WAKEUP12_SCRIPT) { - if (err) err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT, R_SEQ_ADD_S2A12); + if (err) return err; } if (flags & TWL4030_WAKEUP3_SCRIPT) { diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index dfbae34e1804..eb3b5f88e566 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -76,8 +76,8 @@ static int twl6030_interrupt_mapping[24] = { USBOTG_INTR_OFFSET, /* Bit 18 ID */ USB_PRES_INTR_OFFSET, /* Bit 19 VBUS */ CHARGER_INTR_OFFSET, /* Bit 20 CHRG_CTRL */ - CHARGER_INTR_OFFSET, /* Bit 21 EXT_CHRG */ - CHARGER_INTR_OFFSET, /* Bit 22 INT_CHRG */ + CHARGERFAULT_INTR_OFFSET, /* Bit 21 EXT_CHRG */ + CHARGERFAULT_INTR_OFFSET, /* Bit 22 INT_CHRG */ RSV_INTR_OFFSET, /* Bit 23 Reserved */ }; /*----------------------------------------------------------------------*/ diff --git a/drivers/mfd/wl1273-core.c b/drivers/mfd/wl1273-core.c index 04914f2836c0..d97a86945174 100644 --- a/drivers/mfd/wl1273-core.c +++ b/drivers/mfd/wl1273-core.c @@ -153,7 +153,6 @@ out: */ static int wl1273_fm_set_volume(struct wl1273_core *core, unsigned int volume) { - u16 val; int r; if (volume > WL1273_MAX_VOLUME) @@ -217,7 +216,8 @@ static int __devinit wl1273_core_probe(struct i2c_client *client, cell = &core->cells[children]; cell->name = "wl1273_fm_radio"; - cell->mfd_data = &core; + cell->platform_data = &core; + cell->pdata_size = sizeof(core); children++; core->read = wl1273_fm_read_reg; @@ -231,7 +231,8 @@ static int __devinit wl1273_core_probe(struct i2c_client *client, dev_dbg(&client->dev, "%s: Have codec.\n", __func__); cell->name = "wl1273-codec"; - cell->mfd_data = &core; + cell->platform_data = &core; + cell->pdata_size = sizeof(core); children++; } diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index 3fe9a58fe6c7..265f75fc6a25 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -1442,7 +1442,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) struct wm831x_pdata *pdata = wm831x->dev->platform_data; int rev; enum wm831x_parent parent; - int ret; + int ret, i; mutex_init(&wm831x->io_lock); mutex_init(&wm831x->key_lock); @@ -1581,6 +1581,17 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) } } + if (pdata) { + for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) { + if (!pdata->gpio_defaults[i]) + continue; + + wm831x_reg_write(wm831x, + WM831X_GPIO1_CONTROL + i, + pdata->gpio_defaults[i] & 0xffff); + } + } + ret = wm831x_irq_init(wm831x, irq); if (ret != 0) goto err; diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index 23e66af89dea..42b928ec891e 100644 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -515,12 +515,6 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) 0xffff); } - if (!irq) { - dev_warn(wm831x->dev, - "No interrupt specified - functionality limited\n"); - return 0; - } - if (!pdata || !pdata->irq_base) { dev_err(wm831x->dev, "No interrupt base specified, no interrupts\n"); @@ -567,15 +561,22 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) #endif } - ret = request_threaded_irq(irq, NULL, wm831x_irq_thread, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "wm831x", wm831x); - if (ret != 0) { - dev_err(wm831x->dev, "Failed to request IRQ %d: %d\n", - irq, ret); - return ret; + if (irq) { + ret = request_threaded_irq(irq, NULL, wm831x_irq_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "wm831x", wm831x); + if (ret != 0) { + dev_err(wm831x->dev, "Failed to request IRQ %d: %d\n", + irq, ret); + return ret; + } + } else { + dev_warn(wm831x->dev, + "No interrupt specified - functionality limited\n"); } + + /* Enable top level interrupts, we mask at secondary level */ wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0); diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index 3a6e78cb0384..597f82edacaa 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -245,7 +245,8 @@ static int wm8400_register_codec(struct wm8400 *wm8400) { struct mfd_cell cell = { .name = "wm8400-codec", - .mfd_data = wm8400, + .platform_data = wm8400, + .pdata_size = sizeof(*wm8400), }; return mfd_add_devices(wm8400->dev, -1, &cell, 1, NULL, 0); diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 14479f9ef53f..8d185de90d20 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -69,7 +69,7 @@ static int __devinit tmio_mmc_probe(struct platform_device *pdev) if (pdev->num_resources != 2) goto out; - pdata = mfd_get_data(pdev); + pdata = pdev->dev.platform_data; if (!pdata || !pdata->hclk) goto out; diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index 14c578707824..c004e474631b 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -372,7 +372,7 @@ static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) static int tmio_probe(struct platform_device *dev) { - struct tmio_nand_data *data = mfd_get_data(dev); + struct tmio_nand_data *data = dev->dev.platform_data; struct resource *fcr = platform_get_resource(dev, IORESOURCE_MEM, 0); struct resource *ccr = platform_get_resource(dev, diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c index 587fba48cdd9..f1942cab35f6 100644 --- a/drivers/net/can/janz-ican3.c +++ b/drivers/net/can/janz-ican3.c @@ -15,7 +15,6 @@ #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/platform_device.h> -#include <linux/mfd/core.h> #include <linux/netdevice.h> #include <linux/can.h> @@ -1644,7 +1643,7 @@ static int __devinit ican3_probe(struct platform_device *pdev) struct device *dev; int ret; - pdata = mfd_get_data(pdev); + pdata = pdev->dev.platform_data; if (!pdata) return -ENXIO; diff --git a/drivers/net/ks8842.c b/drivers/net/ks8842.c index f0d8346d0fa5..4d40626b3bfa 100644 --- a/drivers/net/ks8842.c +++ b/drivers/net/ks8842.c @@ -26,7 +26,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/mfd/core.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> @@ -1146,7 +1145,7 @@ static int __devinit ks8842_probe(struct platform_device *pdev) struct resource *iomem; struct net_device *netdev; struct ks8842_adapter *adapter; - struct ks8842_platform_data *pdata = mfd_get_data(pdev); + struct ks8842_platform_data *pdata = pdev->dev.platform_data; u16 id; unsigned i; diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c index 8e5aec260866..a70e16d3a3dc 100644 --- a/drivers/power/max8925_power.c +++ b/drivers/power/max8925_power.c @@ -425,16 +425,11 @@ static __devexit int max8925_deinit_charger(struct max8925_power_info *info) static __devinit int max8925_power_probe(struct platform_device *pdev) { struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); - struct max8925_platform_data *max8925_pdata; struct max8925_power_pdata *pdata = NULL; struct max8925_power_info *info; int ret; - if (pdev->dev.parent->platform_data) { - max8925_pdata = pdev->dev.parent->platform_data; - pdata = max8925_pdata->power; - } - + pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "platform data isn't assigned to " "power supply\n"); @@ -447,6 +442,7 @@ static __devinit int max8925_power_probe(struct platform_device *pdev) info->chip = chip; info->gpm = chip->i2c; info->adc = chip->adc; + platform_set_drvdata(pdev, info); info->ac.name = "max8925-ac"; info->ac.type = POWER_SUPPLY_TYPE_MAINS; @@ -482,8 +478,6 @@ static __devinit int max8925_power_probe(struct platform_device *pdev) info->topoff_threshold = pdata->topoff_threshold; info->fast_charge = pdata->fast_charge; info->set_charger = pdata->set_charger; - dev_set_drvdata(&pdev->dev, info); - platform_set_drvdata(pdev, info); max8925_init_charger(chip, info); return 0; diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index 859251250b55..d63fddb0fbb0 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -15,7 +15,6 @@ #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> -#include <linux/mfd/core.h> #include <linux/mfd/88pm860x.h> struct pm8607_regulator_info { @@ -399,36 +398,33 @@ static int __devinit pm8607_regulator_probe(struct platform_device *pdev) { struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); struct pm8607_regulator_info *info = NULL; - struct regulator_init_data *pdata; - struct mfd_cell *cell; + struct regulator_init_data *pdata = pdev->dev.platform_data; + struct resource *res; int i; - cell = pdev->dev.platform_data; - if (cell == NULL) - return -ENODEV; - pdata = cell->mfd_data; - if (pdata == NULL) + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (res == NULL) { + dev_err(&pdev->dev, "No I/O resource!\n"); return -EINVAL; - + } for (i = 0; i < ARRAY_SIZE(pm8607_regulator_info); i++) { info = &pm8607_regulator_info[i]; - if (!strcmp(info->desc.name, pdata->constraints.name)) + if (info->desc.id == res->start) break; } - if (i > ARRAY_SIZE(pm8607_regulator_info)) { - dev_err(&pdev->dev, "Failed to find regulator %s\n", - pdata->constraints.name); + if ((i < 0) || (i > PM8607_ID_RG_MAX)) { + dev_err(&pdev->dev, "Failed to find regulator %llu\n", + (unsigned long long)res->start); return -EINVAL; } - info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion; info->chip = chip; /* check DVC ramp slope double */ - if (!strcmp(info->desc.name, "BUCK3")) - if (info->chip->buck3_double) - info->slope_double = 1; + if ((i == PM8607_ID_BUCK3) && info->chip->buck3_double) + info->slope_double = 1; + /* replace driver_data with info */ info->regulator = regulator_register(&info->desc, &pdev->dev, pdata, info); if (IS_ERR(info->regulator)) { diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c index b1d77946e9c6..585e4946fe0a 100644 --- a/drivers/regulator/ab3100.c +++ b/drivers/regulator/ab3100.c @@ -17,7 +17,6 @@ #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/mfd/abx500.h> -#include <linux/mfd/core.h> /* LDO registers and some handy masking definitions for AB3100 */ #define AB3100_LDO_A 0x40 @@ -582,7 +581,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { static int __devinit ab3100_regulators_probe(struct platform_device *pdev) { - struct ab3100_platform_data *plfdata = mfd_get_data(pdev); + struct ab3100_platform_data *plfdata = pdev->dev.platform_data; int err = 0; u8 data; int i; diff --git a/drivers/regulator/db8500-prcmu.c b/drivers/regulator/db8500-prcmu.c index 1089a961616e..e5f7b8fe51f4 100644 --- a/drivers/regulator/db8500-prcmu.c +++ b/drivers/regulator/db8500-prcmu.c @@ -13,7 +13,6 @@ #include <linux/err.h> #include <linux/spinlock.h> #include <linux/platform_device.h> -#include <linux/mfd/core.h> #include <linux/mfd/db8500-prcmu.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> @@ -471,7 +470,8 @@ static struct db8500_regulator_info static int __devinit db8500_regulator_probe(struct platform_device *pdev) { - struct regulator_init_data *db8500_init_data = mfd_get_data(pdev); + struct regulator_init_data *db8500_init_data = + dev_get_platdata(&pdev->dev); int i, err; /* register all regulators */ diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c index 8ae147549c6a..e4dbd667c043 100644 --- a/drivers/regulator/max8925-regulator.c +++ b/drivers/regulator/max8925-regulator.c @@ -23,6 +23,10 @@ #define SD1_DVM_SHIFT 5 /* SDCTL1 bit5 */ #define SD1_DVM_EN 6 /* SDV1 bit 6 */ +/* bit definitions in SD & LDO control registers */ +#define OUT_ENABLE 0x1f /* Power U/D sequence as I2C */ +#define OUT_DISABLE 0x1e /* Power U/D sequence as I2C */ + struct max8925_regulator_info { struct regulator_desc desc; struct regulator_dev *regulator; @@ -93,8 +97,8 @@ static int max8925_enable(struct regulator_dev *rdev) struct max8925_regulator_info *info = rdev_get_drvdata(rdev); return max8925_set_bits(info->i2c, info->enable_reg, - 1 << info->enable_bit, - 1 << info->enable_bit); + OUT_ENABLE << info->enable_bit, + OUT_ENABLE << info->enable_bit); } static int max8925_disable(struct regulator_dev *rdev) @@ -102,7 +106,8 @@ static int max8925_disable(struct regulator_dev *rdev) struct max8925_regulator_info *info = rdev_get_drvdata(rdev); return max8925_set_bits(info->i2c, info->enable_reg, - 1 << info->enable_bit, 0); + OUT_ENABLE << info->enable_bit, + OUT_DISABLE << info->enable_bit); } static int max8925_is_enabled(struct regulator_dev *rdev) diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c index b8a00c7fa441..730f43ad415b 100644 --- a/drivers/regulator/mc13783-regulator.c +++ b/drivers/regulator/mc13783-regulator.c @@ -15,7 +15,6 @@ #include <linux/regulator/driver.h> #include <linux/platform_device.h> #include <linux/kernel.h> -#include <linux/mfd/core.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/err.h> @@ -337,7 +336,8 @@ static int __devinit mc13783_regulator_probe(struct platform_device *pdev) { struct mc13xxx_regulator_priv *priv; struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent); - struct mc13783_regulator_platform_data *pdata = mfd_get_data(pdev); + struct mc13783_regulator_platform_data *pdata = + dev_get_platdata(&pdev->dev); struct mc13783_regulator_init_data *init_data; int i, ret; @@ -381,7 +381,8 @@ err: static int __devexit mc13783_regulator_remove(struct platform_device *pdev) { struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); - struct mc13783_regulator_platform_data *pdata = mfd_get_data(pdev); + struct mc13783_regulator_platform_data *pdata = + dev_get_platdata(&pdev->dev); int i; platform_set_drvdata(pdev, NULL); diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c index 6f15168e5ed4..1b8f7398a4a8 100644 --- a/drivers/regulator/mc13892-regulator.c +++ b/drivers/regulator/mc13892-regulator.c @@ -15,7 +15,6 @@ #include <linux/regulator/driver.h> #include <linux/platform_device.h> #include <linux/kernel.h> -#include <linux/mfd/core.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/err.h> @@ -521,7 +520,8 @@ static int __devinit mc13892_regulator_probe(struct platform_device *pdev) { struct mc13xxx_regulator_priv *priv; struct mc13xxx *mc13892 = dev_get_drvdata(pdev->dev.parent); - struct mc13xxx_regulator_platform_data *pdata = mfd_get_data(pdev); + struct mc13xxx_regulator_platform_data *pdata = + dev_get_platdata(&pdev->dev); struct mc13xxx_regulator_init_data *init_data; int i, ret; u32 val; @@ -595,7 +595,8 @@ err_free: static int __devexit mc13892_regulator_remove(struct platform_device *pdev) { struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); - struct mc13xxx_regulator_platform_data *pdata = mfd_get_data(pdev); + struct mc13xxx_regulator_platform_data *pdata = + dev_get_platdata(&pdev->dev); int i; platform_set_drvdata(pdev, NULL); diff --git a/drivers/regulator/tps6105x-regulator.c b/drivers/regulator/tps6105x-regulator.c index 1661499feda4..a4d7f4540c18 100644 --- a/drivers/regulator/tps6105x-regulator.c +++ b/drivers/regulator/tps6105x-regulator.c @@ -137,7 +137,7 @@ static struct regulator_desc tps6105x_regulator_desc = { */ static int __devinit tps6105x_regulator_probe(struct platform_device *pdev) { - struct tps6105x *tps6105x = mfd_get_data(pdev); + struct tps6105x *tps6105x = dev_get_platdata(&pdev->dev); struct tps6105x_platform_data *pdata = tps6105x->pdata; int ret; @@ -164,7 +164,7 @@ static int __devinit tps6105x_regulator_probe(struct platform_device *pdev) static int __devexit tps6105x_regulator_remove(struct platform_device *pdev) { - struct tps6105x *tps6105x = platform_get_drvdata(pdev); + struct tps6105x *tps6105x = dev_get_platdata(&pdev->dev); regulator_unregister(tps6105x->regulator); return 0; } diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index b8f4e9e66cd5..8e437e2f6281 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -125,6 +125,16 @@ comment "I2C RTC drivers" if I2C +config RTC_DRV_88PM860X + tristate "Marvell 88PM860x" + depends on RTC_CLASS && I2C && MFD_88PM860X + help + If you say yes here you get support for RTC function in Marvell + 88PM860x chips. + + This driver can also be built as a module. If so, the module + will be called rtc-88pm860x. + config RTC_DRV_DS1307 tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 9574748d1c73..612f5a88a8ee 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -15,6 +15,7 @@ rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o # Keep the list ordered. +obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c new file mode 100644 index 000000000000..64b847b7f970 --- /dev/null +++ b/drivers/rtc/rtc-88pm860x.c @@ -0,0 +1,427 @@ +/* + * Real Time Clock driver for Marvell 88PM860x PMIC + * + * Copyright (c) 2010 Marvell International Ltd. + * Author: Haojian Zhuang <haojian.zhuang@marvell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/rtc.h> +#include <linux/delay.h> +#include <linux/mfd/core.h> +#include <linux/mfd/88pm860x.h> + +#define VRTC_CALIBRATION + +struct pm860x_rtc_info { + struct pm860x_chip *chip; + struct i2c_client *i2c; + struct rtc_device *rtc_dev; + struct device *dev; + struct delayed_work calib_work; + + int irq; + int vrtc; + int (*sync)(unsigned int ticks); +}; + +#define REG_VRTC_MEAS1 0x7D + +#define REG0_ADDR 0xB0 +#define REG1_ADDR 0xB2 +#define REG2_ADDR 0xB4 +#define REG3_ADDR 0xB6 + +#define REG0_DATA 0xB1 +#define REG1_DATA 0xB3 +#define REG2_DATA 0xB5 +#define REG3_DATA 0xB7 + +/* bit definitions of Measurement Enable Register 2 (0x51) */ +#define MEAS2_VRTC (1 << 0) + +/* bit definitions of RTC Register 1 (0xA0) */ +#define ALARM_EN (1 << 3) +#define ALARM_WAKEUP (1 << 4) +#define ALARM (1 << 5) +#define RTC1_USE_XO (1 << 7) + +#define VRTC_CALIB_INTERVAL (HZ * 60 * 10) /* 10 minutes */ + +static irqreturn_t rtc_update_handler(int irq, void *data) +{ + struct pm860x_rtc_info *info = (struct pm860x_rtc_info *)data; + int mask; + + mask = ALARM | ALARM_WAKEUP; + pm860x_set_bits(info->i2c, PM8607_RTC1, mask | ALARM_EN, mask); + rtc_update_irq(info->rtc_dev, 1, RTC_AF); + return IRQ_HANDLED; +} + +static int pm860x_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct pm860x_rtc_info *info = dev_get_drvdata(dev); + + if (enabled) + pm860x_set_bits(info->i2c, PM8607_RTC1, ALARM, ALARM); + else + pm860x_set_bits(info->i2c, PM8607_RTC1, ALARM, 0); + return 0; +} + +/* + * Calculate the next alarm time given the requested alarm time mask + * and the current time. + */ +static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, + struct rtc_time *alrm) +{ + unsigned long next_time; + unsigned long now_time; + + next->tm_year = now->tm_year; + next->tm_mon = now->tm_mon; + next->tm_mday = now->tm_mday; + next->tm_hour = alrm->tm_hour; + next->tm_min = alrm->tm_min; + next->tm_sec = alrm->tm_sec; + + rtc_tm_to_time(now, &now_time); + rtc_tm_to_time(next, &next_time); + + if (next_time < now_time) { + /* Advance one day */ + next_time += 60 * 60 * 24; + rtc_time_to_tm(next_time, next); + } +} + +static int pm860x_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pm860x_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[8]; + unsigned long ticks, base, data; + + pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf); + dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1], + buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7]; + + /* load 32-bit read-only counter */ + pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf); + data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + ticks = base + data; + dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", + base, data, ticks); + + rtc_time_to_tm(ticks, tm); + + return 0; +} + +static int pm860x_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pm860x_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[4]; + unsigned long ticks, base, data; + + if ((tm->tm_year < 70) || (tm->tm_year > 138)) { + dev_dbg(info->dev, "Set time %d out of range. " + "Please set time between 1970 to 2038.\n", + 1900 + tm->tm_year); + return -EINVAL; + } + rtc_tm_to_time(tm, &ticks); + + /* load 32-bit read-only counter */ + pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf); + data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + base = ticks - data; + dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", + base, data, ticks); + + pm860x_page_reg_write(info->i2c, REG0_DATA, (base >> 24) & 0xFF); + pm860x_page_reg_write(info->i2c, REG1_DATA, (base >> 16) & 0xFF); + pm860x_page_reg_write(info->i2c, REG2_DATA, (base >> 8) & 0xFF); + pm860x_page_reg_write(info->i2c, REG3_DATA, base & 0xFF); + + if (info->sync) + info->sync(ticks); + return 0; +} + +static int pm860x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pm860x_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[8]; + unsigned long ticks, base, data; + int ret; + + pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf); + dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1], + buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7]; + + pm860x_bulk_read(info->i2c, PM8607_RTC_EXPIRE1, 4, buf); + data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + ticks = base + data; + dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", + base, data, ticks); + + rtc_time_to_tm(ticks, &alrm->time); + ret = pm860x_reg_read(info->i2c, PM8607_RTC1); + alrm->enabled = (ret & ALARM_EN) ? 1 : 0; + alrm->pending = (ret & (ALARM | ALARM_WAKEUP)) ? 1 : 0; + return 0; +} + +static int pm860x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pm860x_rtc_info *info = dev_get_drvdata(dev); + struct rtc_time now_tm, alarm_tm; + unsigned long ticks, base, data; + unsigned char buf[8]; + int mask; + + pm860x_set_bits(info->i2c, PM8607_RTC1, ALARM_EN, 0); + + pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf); + dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1], + buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7]; + + /* load 32-bit read-only counter */ + pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf); + data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + ticks = base + data; + dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", + base, data, ticks); + + rtc_time_to_tm(ticks, &now_tm); + rtc_next_alarm_time(&alarm_tm, &now_tm, &alrm->time); + /* get new ticks for alarm in 24 hours */ + rtc_tm_to_time(&alarm_tm, &ticks); + data = ticks - base; + + buf[0] = data & 0xff; + buf[1] = (data >> 8) & 0xff; + buf[2] = (data >> 16) & 0xff; + buf[3] = (data >> 24) & 0xff; + pm860x_bulk_write(info->i2c, PM8607_RTC_EXPIRE1, 4, buf); + if (alrm->enabled) { + mask = ALARM | ALARM_WAKEUP | ALARM_EN; + pm860x_set_bits(info->i2c, PM8607_RTC1, mask, mask); + } else { + mask = ALARM | ALARM_WAKEUP | ALARM_EN; + pm860x_set_bits(info->i2c, PM8607_RTC1, mask, + ALARM | ALARM_WAKEUP); + } + return 0; +} + +static const struct rtc_class_ops pm860x_rtc_ops = { + .read_time = pm860x_rtc_read_time, + .set_time = pm860x_rtc_set_time, + .read_alarm = pm860x_rtc_read_alarm, + .set_alarm = pm860x_rtc_set_alarm, + .alarm_irq_enable = pm860x_rtc_alarm_irq_enable, +}; + +#ifdef VRTC_CALIBRATION +static void calibrate_vrtc_work(struct work_struct *work) +{ + struct pm860x_rtc_info *info = container_of(work, + struct pm860x_rtc_info, calib_work.work); + unsigned char buf[2]; + unsigned int sum, data, mean, vrtc_set; + int i; + + for (i = 0, sum = 0; i < 16; i++) { + msleep(100); + pm860x_bulk_read(info->i2c, REG_VRTC_MEAS1, 2, buf); + data = (buf[0] << 4) | buf[1]; + data = (data * 5400) >> 12; /* convert to mv */ + sum += data; + } + mean = sum >> 4; + vrtc_set = 2700 + (info->vrtc & 0x3) * 200; + dev_dbg(info->dev, "mean:%d, vrtc_set:%d\n", mean, vrtc_set); + + sum = pm860x_reg_read(info->i2c, PM8607_RTC_MISC1); + data = sum & 0x3; + if ((mean + 200) < vrtc_set) { + /* try higher voltage */ + if (++data == 4) + goto out; + data = (sum & 0xf8) | (data & 0x3); + pm860x_reg_write(info->i2c, PM8607_RTC_MISC1, data); + } else if ((mean - 200) > vrtc_set) { + /* try lower voltage */ + if (data-- == 0) + goto out; + data = (sum & 0xf8) | (data & 0x3); + pm860x_reg_write(info->i2c, PM8607_RTC_MISC1, data); + } else + goto out; + dev_dbg(info->dev, "set 0x%x to RTC_MISC1\n", data); + /* trigger next calibration since VRTC is updated */ + schedule_delayed_work(&info->calib_work, VRTC_CALIB_INTERVAL); + return; +out: + /* disable measurement */ + pm860x_set_bits(info->i2c, PM8607_MEAS_EN2, MEAS2_VRTC, 0); + dev_dbg(info->dev, "finish VRTC calibration\n"); + return; +} +#endif + +static int __devinit pm860x_rtc_probe(struct platform_device *pdev) +{ + struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct pm860x_rtc_pdata *pdata = NULL; + struct pm860x_rtc_info *info; + struct rtc_time tm; + unsigned long ticks = 0; + int ret; + + pdata = pdev->dev.platform_data; + if (pdata == NULL) + dev_warn(&pdev->dev, "No platform data!\n"); + + info = kzalloc(sizeof(struct pm860x_rtc_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->irq = platform_get_irq(pdev, 0); + if (info->irq < 0) { + dev_err(&pdev->dev, "No IRQ resource!\n"); + ret = -EINVAL; + goto out; + } + + info->chip = chip; + info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion; + info->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, info); + + ret = request_threaded_irq(info->irq, NULL, rtc_update_handler, + IRQF_ONESHOT, "rtc", info); + if (ret < 0) { + dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", + info->irq, ret); + goto out; + } + + /* set addresses of 32-bit base value for RTC time */ + pm860x_page_reg_write(info->i2c, REG0_ADDR, REG0_DATA); + pm860x_page_reg_write(info->i2c, REG1_ADDR, REG1_DATA); + pm860x_page_reg_write(info->i2c, REG2_ADDR, REG2_DATA); + pm860x_page_reg_write(info->i2c, REG3_ADDR, REG3_DATA); + + ret = pm860x_rtc_read_time(&pdev->dev, &tm); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read initial time.\n"); + goto out_rtc; + } + if ((tm.tm_year < 70) || (tm.tm_year > 138)) { + tm.tm_year = 70; + tm.tm_mon = 0; + tm.tm_mday = 1; + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + ret = pm860x_rtc_set_time(&pdev->dev, &tm); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to set initial time.\n"); + goto out_rtc; + } + } + rtc_tm_to_time(&tm, &ticks); + if (pdata && pdata->sync) { + pdata->sync(ticks); + info->sync = pdata->sync; + } + + info->rtc_dev = rtc_device_register("88pm860x-rtc", &pdev->dev, + &pm860x_rtc_ops, THIS_MODULE); + ret = PTR_ERR(info->rtc_dev); + if (IS_ERR(info->rtc_dev)) { + dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); + goto out_rtc; + } + + /* + * enable internal XO instead of internal 3.25MHz clock since it can + * free running in PMIC power-down state. + */ + pm860x_set_bits(info->i2c, PM8607_RTC1, RTC1_USE_XO, RTC1_USE_XO); + +#ifdef VRTC_CALIBRATION + /* <00> -- 2.7V, <01> -- 2.9V, <10> -- 3.1V, <11> -- 3.3V */ + if (pdata && pdata->vrtc) + info->vrtc = pdata->vrtc & 0x3; + else + info->vrtc = 1; + pm860x_set_bits(info->i2c, PM8607_MEAS_EN2, MEAS2_VRTC, MEAS2_VRTC); + + /* calibrate VRTC */ + INIT_DELAYED_WORK(&info->calib_work, calibrate_vrtc_work); + schedule_delayed_work(&info->calib_work, VRTC_CALIB_INTERVAL); +#endif /* VRTC_CALIBRATION */ + return 0; +out_rtc: + free_irq(info->irq, info); +out: + kfree(info); + return ret; +} + +static int __devexit pm860x_rtc_remove(struct platform_device *pdev) +{ + struct pm860x_rtc_info *info = platform_get_drvdata(pdev); + +#ifdef VRTC_CALIBRATION + flush_scheduled_work(); + /* disable measurement */ + pm860x_set_bits(info->i2c, PM8607_MEAS_EN2, MEAS2_VRTC, 0); +#endif /* VRTC_CALIBRATION */ + + platform_set_drvdata(pdev, NULL); + rtc_device_unregister(info->rtc_dev); + free_irq(info->irq, info); + kfree(info); + return 0; +} + +static struct platform_driver pm860x_rtc_driver = { + .driver = { + .name = "88pm860x-rtc", + .owner = THIS_MODULE, + }, + .probe = pm860x_rtc_probe, + .remove = __devexit_p(pm860x_rtc_remove), +}; + +static int __init pm860x_rtc_init(void) +{ + return platform_driver_register(&pm860x_rtc_driver); +} +module_init(pm860x_rtc_init); + +static void __exit pm860x_rtc_exit(void) +{ + platform_driver_unregister(&pm860x_rtc_driver); +} +module_exit(pm860x_rtc_exit); + +MODULE_DESCRIPTION("Marvell 88PM860x RTC driver"); +MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index c69c6f2c2c5c..4d2c75df886c 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -18,7 +18,6 @@ #include <linux/interrupt.h> #include <linux/of.h> #include <linux/platform_device.h> -#include <linux/mfd/core.h> #include <linux/spi/spi.h> #include <linux/spi/spi_bitbang.h> #include <linux/spi/xilinx_spi.h> @@ -471,7 +470,7 @@ static int __devinit xilinx_spi_probe(struct platform_device *dev) struct spi_master *master; u8 i; - pdata = mfd_get_data(dev); + pdata = dev->dev.platform_data; if (pdata) { num_cs = pdata->num_chipselect; little_endian = pdata->little_endian; diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c index c8b520e9a11a..c04b94da81f7 100644 --- a/drivers/video/backlight/88pm860x_bl.c +++ b/drivers/video/backlight/88pm860x_bl.c @@ -16,7 +16,6 @@ #include <linux/fb.h> #include <linux/i2c.h> #include <linux/backlight.h> -#include <linux/mfd/core.h> #include <linux/mfd/88pm860x.h> #define MAX_BRIGHTNESS (0xFF) @@ -168,7 +167,6 @@ static int pm860x_backlight_probe(struct platform_device *pdev) struct pm860x_backlight_pdata *pdata = NULL; struct pm860x_backlight_data *data; struct backlight_device *bl; - struct mfd_cell *cell; struct resource *res; struct backlight_properties props; unsigned char value; @@ -181,10 +179,7 @@ static int pm860x_backlight_probe(struct platform_device *pdev) return -EINVAL; } - cell = pdev->dev.platform_data; - if (cell == NULL) - return -ENODEV; - pdata = cell->mfd_data; + pdata = pdev->dev.platform_data; if (pdata == NULL) { dev_err(&pdev->dev, "platform data isn't assigned to " "backlight\n"); diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c index 0c341d739604..cd1c4dcef8fd 100644 --- a/drivers/video/tmiofb.c +++ b/drivers/video/tmiofb.c @@ -250,7 +250,7 @@ static irqreturn_t tmiofb_irq(int irq, void *__info) */ static int tmiofb_hw_stop(struct platform_device *dev) { - struct tmio_fb_data *data = mfd_get_data(dev); + struct tmio_fb_data *data = dev->dev.platform_data; struct fb_info *info = platform_get_drvdata(dev); struct tmiofb_par *par = info->par; @@ -311,7 +311,7 @@ static int tmiofb_hw_init(struct platform_device *dev) */ static void tmiofb_hw_mode(struct platform_device *dev) { - struct tmio_fb_data *data = mfd_get_data(dev); + struct tmio_fb_data *data = dev->dev.platform_data; struct fb_info *info = platform_get_drvdata(dev); struct fb_videomode *mode = info->mode; struct tmiofb_par *par = info->par; @@ -557,8 +557,7 @@ static int tmiofb_ioctl(struct fb_info *fbi, static struct fb_videomode * tmiofb_find_mode(struct fb_info *info, struct fb_var_screeninfo *var) { - struct tmio_fb_data *data = - mfd_get_data(to_platform_device(info->device)); + struct tmio_fb_data *data = info->device->platform_data; struct fb_videomode *best = NULL; int i; @@ -578,8 +577,7 @@ static int tmiofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct fb_videomode *mode; - struct tmio_fb_data *data = - mfd_get_data(to_platform_device(info->device)); + struct tmio_fb_data *data = info->device->platform_data; mode = tmiofb_find_mode(info, var); if (!mode || var->bits_per_pixel > 16) @@ -680,7 +678,7 @@ static struct fb_ops tmiofb_ops = { static int __devinit tmiofb_probe(struct platform_device *dev) { const struct mfd_cell *cell = mfd_get_cell(dev); - struct tmio_fb_data *data = mfd_get_data(dev); + struct tmio_fb_data *data = dev->dev.platform_data; struct resource *ccr = platform_get_resource(dev, IORESOURCE_MEM, 1); struct resource *lcr = platform_get_resource(dev, IORESOURCE_MEM, 0); struct resource *vram = platform_get_resource(dev, IORESOURCE_MEM, 2); diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c index 2f4fa02744a5..0855d6cce3c1 100644 --- a/drivers/w1/masters/ds1wm.c +++ b/drivers/w1/masters/ds1wm.c @@ -216,7 +216,7 @@ static int ds1wm_find_divisor(int gclk) static void ds1wm_up(struct ds1wm_data *ds1wm_data) { int divisor; - struct ds1wm_driver_data *plat = mfd_get_data(ds1wm_data->pdev); + struct ds1wm_driver_data *plat = ds1wm_data->pdev->dev.platform_data; if (ds1wm_data->cell->enable) ds1wm_data->cell->enable(ds1wm_data->pdev); @@ -351,13 +351,21 @@ static int ds1wm_probe(struct platform_device *pdev) ret = -ENOMEM; goto err0; } - plat = mfd_get_data(pdev); /* calculate bus shift from mem resource */ ds1wm_data->bus_shift = resource_size(res) >> 3; ds1wm_data->pdev = pdev; ds1wm_data->cell = mfd_get_cell(pdev); + if (!ds1wm_data->cell) { + ret = -ENODEV; + goto err1; + } + plat = pdev->dev.platform_data; + if (!plat) { + ret = -ENODEV; + goto err1; + } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { diff --git a/drivers/watchdog/rdc321x_wdt.c b/drivers/watchdog/rdc321x_wdt.c index d8e725082fdc..428f8a1583e8 100644 --- a/drivers/watchdog/rdc321x_wdt.c +++ b/drivers/watchdog/rdc321x_wdt.c @@ -37,7 +37,6 @@ #include <linux/io.h> #include <linux/uaccess.h> #include <linux/mfd/rdc321x.h> -#include <linux/mfd/core.h> #define RDC_WDT_MASK 0x80000000 /* Mask */ #define RDC_WDT_EN 0x00800000 /* Enable bit */ @@ -232,7 +231,7 @@ static int __devinit rdc321x_wdt_probe(struct platform_device *pdev) struct resource *r; struct rdc321x_wdt_pdata *pdata; - pdata = mfd_get_data(pdev); + pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "no platform data supplied\n"); return -ENODEV; |