[PATCH 1/2] mfd: Add Freescale's PMIC MC34708 support
Marc Reilly
marc at cpdesign.com.au
Sun Apr 22 19:32:23 EDT 2012
Hi,
> From: "Ying-Chun Liu (PaulLiu)" <paul.liu at linaro.org>
>
> Freescale MC34708 is a PMIC which supports the following features:
> * 6 multi-mode buck regulators
> * Boost regulator for USB OTG.
> * 8 regulators for thermal budget optimization
> * 10-bit ADC
> * Real time clock
>
> Signed-off-by: Robin Gong <B38343 at freescale.com>
> Signed-off-by: Ying-Chun Liu (PaulLiu) <paul.liu at linaro.org>
> Cc: Samuel Ortiz <sameo at linux.intel.com>
> Cc: Mark Brown <broonie at opensource.wolfsonmicro.com>
> Cc: Shawn Guo <shawn.guo at linaro.org>
> ---
> Documentation/devicetree/bindings/mfd/mc34708.txt | 61 ++
> drivers/mfd/Kconfig | 11 +
> drivers/mfd/Makefile | 1 +
> drivers/mfd/mc34708-core.c | 634
> +++++++++++++++++++++ include/linux/mfd/mc34708.h |
> 134 +++++
> 5 files changed, 841 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/mc34708.txt
> create mode 100644 drivers/mfd/mc34708-core.c
> create mode 100644 include/linux/mfd/mc34708.h
Why not add the MC34708 to the existing mc13xxx driver? (apart from the
obvious 13/34)
I haven't looked at the 34708 in detail but this code looks remarkably
similar. It seems unnecessary to duplicate all of this ...
Aside from that, I'd look at using regmap, as Mark Brown suggested. You can
look for my patches for the mc13xxx for an example.
Cheers,
Marc
>
> diff --git a/Documentation/devicetree/bindings/mfd/mc34708.txt
> b/Documentation/devicetree/bindings/mfd/mc34708.txt new file mode 100644
> index 0000000..2bb5c9e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/mc34708.txt
> @@ -0,0 +1,61 @@
> +* Freescale MC34708 Power Management Integrated Circuit (PMIC)
> +
> +Required properties:
> +- compatible : Must be "fsl,mc34708"
> +
> +Optional properties:
> +- fsl,mc34708-uses-adc : Indicate the ADC is being used
> +- fsl,mc34708-uses-rtc : Indicate the RTC is being used
> +- fsl,mc34708-uses-ts : Indicate the touchscreen controller is being
> used +
> +Sub-nodes:
> +- regulators : Contain the regulator nodes. The MC34708 regulators are
> + bound using their names as listed below for enabling.
> +
> + mc34708__sw1a : regulator SW1A
> + mc34708__sw1b : regulator SW1B
> + mc34708__sw2 : regulator SW2
> + mc34708__sw3 : regulator SW3
> + mc34708__sw4A : regulator SW4A
> + mc34708__sw4b : regulator SW4B
> + mc34708__swbst : regulator SWBST
> + mc34708__vpll : regulator VPLL
> + mc34708__vrefddr : regulator VREFDDR
> + mc34708__vusb : regulator VUSB
> + mc34708__vusb2 : regulator VUSB2
> + mc34708__vdac : regulator VDAC
> + mc34708__vgen1 : regulator VGEN1
> + mc34708__vgen2 : regulator VGEN2
> +
> + The bindings details of individual regulator device can be found in:
> + Documentation/devicetree/bindings/regulator/regulator.txt
> +
> +Examples:
> +
> +i2c at 63fc8000 {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + compatible = "fsl,imx53-i2c", "fsl,imx1-i2c";
> + reg = <0x63fc8000 0x4000>;
> + interrupts = <62>;
> + status = "okay";
> +
> + pmic: mc34708 at 8 {
> + compatible = "fsl,mc34708";
> + reg = <0x08>;
> +
> + regulators {
> + mc34708__sw1a {
> + regulator-min-microvolt = <650000>;
> + regulator-max-microvolt = <1437500>;
> + regulator-boot-on;
> + regulator-always-on;
> + };
> +
> + mc34708__vusb {
> + regulator-boot-on;
> + regulator-always-on;
> + };
> + };
> + };
> +};
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 29f463c..17b6cc5 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -600,6 +600,17 @@ config MFD_MC13XXX
> additional drivers must be enabled in order to use the
> functionality of the device.
>
> +config MFD_MC34708
> + tristate "Support for Freescale's PMIC MC34708"
> + depends on I2C
> + depends on OF
> + select MFD_CORE
> + help
> + Support for the Freescale's PMIC MC34708
> + This driver provides common support for accessing the device,
> + additional drivers must be enabled in order to use the
> + functionality of the device.
> +
> config ABX500_CORE
> bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
> default y if ARCH_U300 || ARCH_U8500
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 05fa538..b98d943 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -54,6 +54,7 @@ obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
> obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o
>
> obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
> +obj-$(CONFIG_MFD_MC34708) += mc34708-core.o
>
> obj-$(CONFIG_MFD_CORE) += mfd-core.o
>
> diff --git a/drivers/mfd/mc34708-core.c b/drivers/mfd/mc34708-core.c
> new file mode 100644
> index 0000000..54f469b
> --- /dev/null
> +++ b/drivers/mfd/mc34708-core.c
> @@ -0,0 +1,634 @@
> +/*
> + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + */
> +
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/mc34708.h>
> +#include <linux/delay.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +
> +struct mc34708 {
> + struct i2c_client *i2c_client;
> + struct mutex lock;
> + int irq;
> +
> + irq_handler_t irqhandler[MC34708_NUM_IRQ];
> + void *irqdata[MC34708_NUM_IRQ];
> +};
> +
> +/*!
> + * This is the enumeration of versions of PMIC
> + */
> +enum mc34708_id {
> + MC_PMIC_ID_MC34708,
> + MC_PMIC_ID_INVALID,
> +};
> +
> +struct mc34708_version_t {
> + /*!
> + * PMIC version identifier.
> + */
> + enum mc34708_id id;
> + /*!
> + * Revision of the PMIC.
> + */
> + int revision;
> +};
> +
> +#define PMIC_I2C_RETRY_TIMES 10
> +
> +static const struct i2c_device_id mc34708_device_id[] = {
> + {"mc34708", MC_PMIC_ID_MC34708},
> + {},
> +};
> +
> +static const char * const mc34708_chipname[] = {
> + [MC_PMIC_ID_MC34708] = "mc34708",
> +};
> +
> +void mc34708_lock(struct mc34708 *mc_pmic)
> +{
> + if (!mutex_trylock(&mc_pmic->lock)) {
> + dev_dbg(&mc_pmic->i2c_client->dev, "wait for %s from %pf\n",
> + __func__, __builtin_return_address(0));
> +
> + mutex_lock(&mc_pmic->lock);
> + }
> + dev_dbg(&mc_pmic->i2c_client->dev, "%s from %pf\n",
> + __func__, __builtin_return_address(0));
> +}
> +EXPORT_SYMBOL(mc34708_lock);
> +
> +void mc34708_unlock(struct mc34708 *mc_pmic)
> +{
> + dev_dbg(&mc_pmic->i2c_client->dev, "%s from %pf\n",
> + __func__, __builtin_return_address(0));
> + mutex_unlock(&mc_pmic->lock);
> +}
> +EXPORT_SYMBOL(mc34708_unlock);
> +
> +static int mc34708_i2c_24bit_read(struct i2c_client *client,
> + unsigned int offset,
> + unsigned int *value)
> +{
> + unsigned char buf[3];
> + int ret;
> + int i;
> +
> + memset(buf, 0, 3);
> + for (i = 0; i < PMIC_I2C_RETRY_TIMES; i++) {
> + ret = i2c_smbus_read_i2c_block_data(client, offset, 3, buf);
> + if (ret == 3)
> + break;
> + usleep_range(1000, 1500);
> + }
> +
> + if (ret == 3) {
> + *value = buf[0] << 16 | buf[1] << 8 | buf[2];
> + return ret;
> + } else {
> + dev_err(&client->dev, "24bit read error, ret = %d\n", ret);
> + return -1; /* return -1 on failure */
> + }
> +}
> +
> +int mc34708_reg_read(struct mc34708 *mc_pmic, unsigned int offset,
> + u32 *val)
> +{
> + BUG_ON(!mutex_is_locked(&mc_pmic->lock));
> +
> + if (offset > MC34708_NUMREGS)
> + return -EINVAL;
> +
> + if (mc34708_i2c_24bit_read(mc_pmic->i2c_client, offset, val) == -1)
> + return -1;
> + *val &= 0xffffff;
> +
> + dev_vdbg(&mc_pmic->i2c_client->dev, "mc_pmic read [%02d] -> 0x%06x\n",
> + offset, *val);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(mc34708_reg_read);
> +
> +static int mc34708_i2c_24bit_write(struct i2c_client *client,
> + unsigned int offset, unsigned int reg_val)
> +{
> + char buf[3];
> + int ret;
> + int i;
> +
> + buf[0] = (reg_val >> 16) & 0xff;
> + buf[1] = (reg_val >> 8) & 0xff;
> + buf[2] = (reg_val) & 0xff;
> +
> + for (i = 0; i < PMIC_I2C_RETRY_TIMES; i++) {
> + ret = i2c_smbus_write_i2c_block_data(client, offset, 3, buf);
> + if (ret == 0)
> + break;
> + usleep_range(1000, 1500);
> + }
> + if (i == PMIC_I2C_RETRY_TIMES)
> + dev_err(&client->dev, "24bit write error, ret = %d\n", ret);
> +
> + return ret;
> +}
> +
> +int mc34708_reg_write(struct mc34708 *mc_pmic, unsigned int offset, u32
> val) +{
> + BUG_ON(!mutex_is_locked(&mc_pmic->lock));
> +
> + if (offset > MC34708_NUMREGS)
> + return -EINVAL;
> +
> + if (mc34708_i2c_24bit_write(mc_pmic->i2c_client, offset, val))
> + return -1;
> + val &= 0xffffff;
> +
> + dev_vdbg(&mc_pmic->i2c_client->dev, "mc_pmic write[%02d] -> 0x%06x\n",
> + offset, val);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(mc34708_reg_write);
> +
> +int mc34708_reg_rmw(struct mc34708 *mc_pmic, unsigned int offset, u32
> mask, + u32 val)
> +{
> + int ret;
> + u32 valread;
> +
> + BUG_ON(val & ~mask);
> +
> + ret = mc34708_reg_read(mc_pmic, offset, &valread);
> + if (ret)
> + return ret;
> +
> + valread = (valread & ~mask) | val;
> +
> + return mc34708_reg_write(mc_pmic, offset, valread);
> +}
> +EXPORT_SYMBOL(mc34708_reg_rmw);
> +
> +int mc34708_irq_mask(struct mc34708 *mc_pmic, int irq)
> +{
> + int ret;
> + unsigned int offmask =
> + irq < 24 ? MC34708_REG_INT_MASK0 : MC34708_REG_INT_MASK1;
> + u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
> + u32 mask;
> +
> + if (irq < 0 || irq >= MC34708_NUM_IRQ)
> + return -EINVAL;
> +
> + ret = mc34708_reg_read(mc_pmic, offmask, &mask);
> + if (ret)
> + return ret;
> +
> + if (mask & irqbit)
> + /* already masked */
> + return 0;
> +
> + return mc34708_reg_write(mc_pmic, offmask, mask | irqbit);
> +}
> +EXPORT_SYMBOL(mc34708_irq_mask);
> +
> +int mc34708_irq_unmask(struct mc34708 *mc_pmic, int irq)
> +{
> + int ret;
> + unsigned int offmask =
> + irq < 24 ? MC34708_REG_INT_MASK0 : MC34708_REG_INT_MASK1;
> + u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
> + u32 mask;
> +
> + if (irq < 0 || irq >= MC34708_NUM_IRQ)
> + return -EINVAL;
> +
> + ret = mc34708_reg_read(mc_pmic, offmask, &mask);
> + if (ret)
> + return ret;
> +
> + if (!(mask & irqbit))
> + /* already unmasked */
> + return 0;
> +
> + return mc34708_reg_write(mc_pmic, offmask, mask & ~irqbit);
> +}
> +EXPORT_SYMBOL(mc34708_irq_unmask);
> +
> +int mc34708_irq_status(struct mc34708 *mc_pmic, int irq, int *enabled,
> + int *pending)
> +{
> + int ret;
> + unsigned int offmask =
> + irq < 24 ? MC34708_REG_INT_MASK0 : MC34708_REG_INT_MASK1;
> + unsigned int offstat =
> + irq < 24 ? MC34708_REG_INT_STATUS0 : MC34708_REG_INT_STATUS1;
> + u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
> +
> + if (irq < 0 || irq >= MC34708_NUM_IRQ)
> + return -EINVAL;
> +
> + if (enabled) {
> + u32 mask;
> +
> + ret = mc34708_reg_read(mc_pmic, offmask, &mask);
> + if (ret)
> + return ret;
> +
> + *enabled = mask & irqbit;
> + }
> +
> + if (pending) {
> + u32 stat;
> +
> + ret = mc34708_reg_read(mc_pmic, offstat, &stat);
> + if (ret)
> + return ret;
> +
> + *pending = stat & irqbit;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(mc34708_irq_status);
> +
> +int mc34708_irq_ack(struct mc34708 *mc_pmic, int irq)
> +{
> + unsigned int offstat =
> + irq < 24 ? MC34708_REG_INT_STATUS0 : MC34708_REG_INT_STATUS1;
> + unsigned int val = 1 << (irq < 24 ? irq : irq - 24);
> +
> + BUG_ON(irq < 0 || irq >= MC34708_NUM_IRQ);
> +
> + return mc34708_reg_write(mc_pmic, offstat, val);
> +}
> +EXPORT_SYMBOL(mc34708_irq_ack);
> +
> +int mc34708_irq_request_nounmask(struct mc34708 *mc_pmic, int irq,
> + irq_handler_t handler, const char *name,
> + void *dev)
> +{
> + BUG_ON(!mutex_is_locked(&mc_pmic->lock));
> + BUG_ON(!handler);
> +
> + if (irq < 0 || irq >= MC34708_NUM_IRQ)
> + return -EINVAL;
> +
> + if (mc_pmic->irqhandler[irq])
> + return -EBUSY;
> +
> + mc_pmic->irqhandler[irq] = handler;
> + mc_pmic->irqdata[irq] = dev;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(mc34708_irq_request_nounmask);
> +
> +int mc34708_irq_request(struct mc34708 *mc_pmic, int irq,
> + irq_handler_t handler, const char *name, void *dev)
> +{
> + int ret;
> +
> + ret = mc34708_irq_request_nounmask(mc_pmic, irq, handler, name, dev);
> + if (ret)
> + return ret;
> +
> + ret = mc34708_irq_unmask(mc_pmic, irq);
> + if (ret) {
> + mc_pmic->irqhandler[irq] = NULL;
> + mc_pmic->irqdata[irq] = NULL;
> + return ret;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(mc34708_irq_request);
> +
> +int mc34708_irq_free(struct mc34708 *mc_pmic, int irq, void *dev)
> +{
> + int ret;
> + BUG_ON(!mutex_is_locked(&mc_pmic->lock));
> +
> + if (irq < 0 || irq >= MC34708_NUM_IRQ || !mc_pmic->irqhandler[irq] ||
> + mc_pmic->irqdata[irq] != dev)
> + return -EINVAL;
> +
> + ret = mc34708_irq_mask(mc_pmic, irq);
> + if (ret)
> + return ret;
> +
> + mc_pmic->irqhandler[irq] = NULL;
> + mc_pmic->irqdata[irq] = NULL;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(mc34708_irq_free);
> +
> +static inline irqreturn_t mc34708_irqhandler(struct mc34708 *mc_pmic, int
> irq) +{
> + return mc_pmic->irqhandler[irq] (irq, mc_pmic->irqdata[irq]);
> +}
> +
> +/*
> + * returns: number of handled irqs or negative error
> + * locking: holds mc_pmic->lock
> + */
> +static int mc34708_irq_handle(struct mc34708 *mc_pmic,
> + unsigned int offstat, unsigned int offmask,
> + int baseirq)
> +{
> + u32 stat, mask;
> + int ret = mc34708_reg_read(mc_pmic, offstat, &stat);
> + int num_handled = 0;
> +
> + if (ret)
> + return ret;
> +
> + ret = mc34708_reg_read(mc_pmic, offmask, &mask);
> + if (ret)
> + return ret;
> +
> + while (stat & ~mask) {
> + int irq = __ffs(stat & ~mask);
> +
> + stat &= ~(1 << irq);
> +
> + if (likely(mc_pmic->irqhandler[baseirq + irq])) {
> + irqreturn_t handled;
> +
> + handled = mc34708_irqhandler(mc_pmic, baseirq + irq);
> + if (handled == IRQ_HANDLED)
> + num_handled++;
> + } else {
> + dev_err(&mc_pmic->i2c_client->dev,
> + "BUG: irq %u but no handler\n", baseirq + irq);
> +
> + mask |= 1 << irq;
> +
> + ret = mc34708_reg_write(mc_pmic, offmask, mask);
> + }
> + }
> +
> + return num_handled;
> +}
> +
> +static irqreturn_t mc34708_irq_thread(int irq, void *data)
> +{
> + struct mc34708 *mc_pmic = data;
> + irqreturn_t ret;
> + int handled = 0;
> +
> + mc34708_lock(mc_pmic);
> +
> + ret = mc34708_irq_handle(mc_pmic, MC34708_REG_INT_STATUS0,
> + MC34708_REG_INT_MASK0, 0);
> + if (ret > 0)
> + handled = 1;
> +
> + ret = mc34708_irq_handle(mc_pmic, MC34708_REG_INT_STATUS1,
> + MC34708_REG_INT_MASK1, 24);
> + if (ret > 0)
> + handled = 1;
> +
> + mc34708_unlock(mc_pmic);
> +
> + return IRQ_RETVAL(handled);
> +}
> +
> +#define maskval(reg, mask) (((reg) & (mask)) >> __ffs(mask))
> +static int mc34708_identify(struct mc34708 *mc_pmic,
> + struct mc34708_version_t *ver)
> +{
> + int rev_id = 0;
> + int rev1 = 0;
> + int rev2 = 0;
> + int finid = 0;
> + int icid = 0;
> + int ret;
> + ret = mc34708_reg_read(mc_pmic, MC34708_REG_IDENTIFICATION, &rev_id);
> + if (ret) {
> + dev_dbg(&mc_pmic->i2c_client->dev, "read ID error!%x\n", ret);
> + return ret;
> + }
> + rev1 = (rev_id & 0x018) >> 3;
> + rev2 = (rev_id & 0x007);
> + icid = (rev_id & 0x01C0) >> 6;
> + finid = (rev_id & 0x01E00) >> 9;
> + ver->id = MC_PMIC_ID_MC34708;
> +
> + ver->revision = ((rev1 * 10) + rev2);
> + dev_dbg(&mc_pmic->i2c_client->dev,
> + "mc_pmic Rev %d.%d FinVer %x detected\n", rev1, rev2, finid);
> +
> + return 0;
> +}
> +
> +static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id
> *id, + const struct i2c_client *client)
> +{
> + while (id->name[0]) {
> + if (strcmp(client->name, id->name) == 0)
> + return id;
> + id++;
> + }
> +
> + return NULL;
> +}
> +
> +static const struct i2c_device_id *i2c_get_device_id(const struct
> i2c_client + *idev)
> +{
> + const struct i2c_driver *idrv = to_i2c_driver(idev->dev.driver);
> +
> + return i2c_match_id(idrv->id_table, idev);
> +}
> +
> +static const char *mc34708_get_chipname(struct mc34708 *mc_pmic)
> +{
> + const struct i2c_device_id *devid =
> + i2c_get_device_id(mc_pmic->i2c_client);
> +
> + if (!devid)
> + return NULL;
> +
> + return mc34708_chipname[devid->driver_data];
> +}
> +
> +int mc34708_get_flags(struct mc34708 *mc_pmic)
> +{
> + struct mc34708_platform_data *pdata =
> + dev_get_platdata(&mc_pmic->i2c_client->dev);
> +
> + return pdata->flags;
> +}
> +EXPORT_SYMBOL(mc34708_get_flags);
> +
> +static int mc34708_add_subdevice_pdata(struct mc34708 *mc_pmic,
> + const char *format, void *pdata,
> + size_t pdata_size)
> +{
> + char buf[30];
> + const char *name = mc34708_get_chipname(mc_pmic);
> +
> + struct mfd_cell cell = {
> + .platform_data = pdata,
> + .pdata_size = pdata_size,
> + };
> +
> + /* there is no asnprintf in the kernel :-( */
> + if (snprintf(buf, sizeof(buf), format, name) > sizeof(buf))
> + return -E2BIG;
> +
> + cell.name = kmemdup(buf, strlen(buf) + 1, GFP_KERNEL);
> + if (!cell.name)
> + return -ENOMEM;
> +
> + return mfd_add_devices(&mc_pmic->i2c_client->dev, -1, &cell, 1, NULL,
> + 0);
> +}
> +
> +static int mc34708_add_subdevice(struct mc34708 *mc_pmic, const char
> *format) +{
> + return mc34708_add_subdevice_pdata(mc_pmic, format, NULL, 0);
> +}
> +
> +static int mc34708_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct mc34708 *mc_pmic;
> + struct mc34708_version_t version;
> + struct mc34708_platform_data *pdata = client->dev.platform_data;
> + struct device_node *np = client->dev.of_node;
> + int ret;
> +
> + mc_pmic = kzalloc(sizeof(*mc_pmic), GFP_KERNEL);
> + if (!mc_pmic)
> + return -ENOMEM;
> + i2c_set_clientdata(client, mc_pmic);
> + mc_pmic->i2c_client = client;
> +
> + mutex_init(&mc_pmic->lock);
> + mc34708_lock(mc_pmic);
> + mc34708_identify(mc_pmic, &version);
> + /* mask all irqs */
> + ret = mc34708_reg_write(mc_pmic, MC34708_REG_INT_MASK0, 0x00ffffff);
> + if (ret)
> + goto err_mask;
> +
> + ret = mc34708_reg_write(mc_pmic, MC34708_REG_INT_MASK1, 0x00ffffff);
> + if (ret)
> + goto err_mask;
> +
> + ret = request_threaded_irq(client->irq, NULL, mc34708_irq_thread,
> + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc_pmic",
> + mc_pmic);
> +
> + if (ret) {
> + err_mask:
> + mc34708_unlock(mc_pmic);
> + dev_set_drvdata(&client->dev, NULL);
> + kfree(mc_pmic);
> + return ret;
> + }
> +
> + mc34708_unlock(mc_pmic);
> +
> + if (pdata && pdata->flags & MC34708_USE_ADC)
> + mc34708_add_subdevice(mc_pmic, "%s-adc");
> + else if (of_get_property(np, "fsl,mc34708-uses-adc", NULL))
> + mc34708_add_subdevice(mc_pmic, "%s-adc");
> +
> +
> + if (pdata && pdata->flags & MC34708_USE_REGULATOR) {
> + struct mc34708_regulator_platform_data regulator_pdata = {
> + .num_regulators = pdata->num_regulators,
> + .regulators = pdata->regulators,
> + };
> +
> + mc34708_add_subdevice_pdata(mc_pmic, "%s-regulator",
> + ®ulator_pdata,
> + sizeof(regulator_pdata));
> + } else if (of_find_node_by_name(np, "regulators")) {
> + mc34708_add_subdevice(mc_pmic, "%s-regulator");
> + }
> +
> + if (pdata && pdata->flags & MC34708_USE_RTC)
> + mc34708_add_subdevice(mc_pmic, "%s-rtc");
> + else if (of_get_property(np, "fsl,mc34708-uses-rtc", NULL))
> + mc34708_add_subdevice(mc_pmic, "%s-rtc");
> +
> + if (pdata && pdata->flags & MC34708_USE_TOUCHSCREEN)
> + mc34708_add_subdevice(mc_pmic, "%s-ts");
> + else if (of_get_property(np, "fsl,mc34708-uses-ts", NULL))
> + mc34708_add_subdevice(mc_pmic, "%s-ts");
> +
> + return 0;
> +}
> +
> +static int __devexit mc34708_remove(struct i2c_client *client)
> +{
> + struct mc34708 *mc_pmic = dev_get_drvdata(&client->dev);
> +
> + free_irq(mc_pmic->i2c_client->irq, mc_pmic);
> +
> + mfd_remove_devices(&client->dev);
> +
> + kfree(mc_pmic);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id mc34708_dt_ids[] = {
> + { .compatible = "fsl,mc34708" },
> + { /* sentinel */ }
> +};
> +
> +static struct i2c_driver mc34708_driver = {
> + .id_table = mc34708_device_id,
> + .driver = {
> + .name = "mc34708",
> + .owner = THIS_MODULE,
> + .of_match_table = mc34708_dt_ids,
> + },
> + .probe = mc34708_probe,
> + .remove = __devexit_p(mc34708_remove),
> +};
> +
> +static int __init mc34708_init(void)
> +{
> + return i2c_add_driver(&mc34708_driver);
> +}
> +subsys_initcall(mc34708_init);
> +
> +static void __exit mc34708_exit(void)
> +{
> + i2c_del_driver(&mc34708_driver);
> +}
> +module_exit(mc34708_exit);
> +
> +MODULE_DESCRIPTION("Core driver for Freescale MC34708 PMIC");
> +MODULE_AUTHOR("Robin Gong <B38343 at freescale.com>, "
> + "Ying-Chun Liu (PaulLiu) <paul.liu at linaro.org>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mfd/mc34708.h b/include/linux/mfd/mc34708.h
> new file mode 100644
> index 0000000..505813d
> --- /dev/null
> +++ b/include/linux/mfd/mc34708.h
> @@ -0,0 +1,134 @@
> +/* For mc34708's pmic driver
> + * Copyright (C) 2004-2011 Freescale Semiconductor, Inc.
> + *
> + * based on:
> + * Copyright 2009-2010 Pengutronix, Uwe Kleine-Koenig
> + * <u.kleine-koenig at pengutronix.de>
> + *
> + * 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.
> + */
> +#ifndef __LINUX_MFD_MC_34708_H
> +#define __LINUX_MFD_MC_34708_H
> +
> +#include <linux/interrupt.h>
> +#include <linux/regulator/driver.h>
> +
> +struct mc34708;
> +
> +void mc34708_lock(struct mc34708 *mc_pmic);
> +void mc34708_unlock(struct mc34708 *mc_pmic);
> +
> +int mc34708_reg_read(struct mc34708 *mc_pmic, unsigned int offset, u32
> *val); +int mc34708_reg_write(struct mc34708 *mc_pmic, unsigned int
> offset, u32 val); +int mc34708_reg_rmw(struct mc34708 *mc_pmic, unsigned
> int offset, + u32 mask, u32 val);
> +
> +int mc34708_get_flags(struct mc34708 *mc_pmic);
> +
> +int mc34708_irq_request(struct mc34708 *mc_pmic, int irq,
> + irq_handler_t handler, const char *name, void *dev);
> +int mc34708_irq_request_nounmask(struct mc34708 *mc_pmic, int irq,
> + irq_handler_t handler, const char *name,
> + void *dev);
> +int mc34708_irq_free(struct mc34708 *mc_pmic, int irq, void *dev);
> +int mc34708_irq_mask(struct mc34708 *mc_pmic, int irq);
> +int mc34708_irq_unmask(struct mc34708 *mc_pmic, int irq);
> +int mc34708_irq_status(struct mc34708 *mc_pmic, int irq,
> + int *enabled, int *pending);
> +int mc34708_irq_ack(struct mc34708 *mc_pmic, int irq);
> +
> +int mc34708_get_flags(struct mc34708 *mc_pmic);
> +
> +#define MC34708_SW1A 0
> +#define MC34708_SW1B 1
> +#define MC34708_SW2 2
> +#define MC34708_SW3 3
> +#define MC34708_SW4A 4
> +#define MC34708_SW4B 5
> +#define MC34708_SW5 6
> +#define MC34708_SWBST 7
> +#define MC34708_VPLL 8
> +#define MC34708_VREFDDR 9
> +#define MC34708_VUSB 10
> +#define MC34708_VUSB2 11
> +#define MC34708_VDAC 12
> +#define MC34708_VGEN1 13
> +#define MC34708_VGEN2 14
> +#define MC34708_REGU_NUM 15
> +
> +#define MC34708_REG_INT_STATUS0 0
> +#define MC34708_REG_INT_MASK0 1
> +#define MC34708_REG_INT_STATUS1 3
> +#define MC34708_REG_INT_MASK1 4
> +#define MC34708_REG_IDENTIFICATION 7
> +
> +#define MC34708_IRQ_ADCDONE 0
> +#define MC34708_IRQ_TSDONE 1
> +#define MC34708_IRQ_TSPENDET 2
> +#define MC34708_IRQ_USBDET 3
> +#define MC34708_IRQ_AUXDET 4
> +#define MC34708_IRQ_USBOVP 5
> +#define MC34708_IRQ_AUXOVP 6
> +#define MC34708_IRQ_CHRTIMEEXP 7
> +#define MC34708_IRQ_BATTOTP 8
> +#define MC34708_IRQ_BATTOVP 9
> +#define MC34708_IRQ_CHRCMPL 10
> +#define MC34708_IRQ_WKVBUSDET 11
> +#define MC34708_IRQ_WKAUXDET 12
> +#define MC34708_IRQ_LOWBATT 13
> +#define MC34708_IRQ_VBUSREGMI 14
> +#define MC34708_IRQ_ATTACH 15
> +#define MC34708_IRQ_DETACH 16
> +#define MC34708_IRQ_KP 17
> +#define MC34708_IRQ_LKP 18
> +#define MC34708_IRQ_LKR 19
> +#define MC34708_IRQ_UKNOW_ATTA 20
> +#define MC34708_IRQ_ADC_CHANGE 21
> +#define MC34708_IRQ_STUCK_KEY 22
> +#define MC34708_IRQ_STUCK_KEY_RCV 23
> +#define MC34708_IRQ_1HZ 24
> +#define MC34708_IRQ_TODA 25
> +#define MC34708_IRQ_UNUSED1 26
> +#define MC34708_IRQ_PWRON1 27
> +#define MC34708_IRQ_PWRON2 28
> +#define MC34708_IRQ_WDIRESET 29
> +#define MC34708_IRQ_SYSRST 30
> +#define MC34708_IRQ_RTCRST 31
> +#define MC34708_IRQ_PCI 32
> +#define MC34708_IRQ_WARM 33
> +#define MC34708_IRQ_MEMHLD 34
> +#define MC34708_IRQ_UNUSED2 35
> +#define MC34708_IRQ_THWARNL 36
> +#define MC34708_IRQ_THWARNH 37
> +#define MC34708_IRQ_CLK 38
> +#define MC34708_IRQ_UNUSED3 39
> +#define MC34708_IRQ_SCP 40
> +#define MC34708_NUMREGS 0x3f
> +#define MC34708_NUM_IRQ 46
> +
> +struct mc34708_regulator_init_data {
> + int id;
> + struct regulator_init_data *init_data;
> +};
> +
> +struct mc34708_regulator_platform_data {
> + int num_regulators;
> + struct mc34708_regulator_init_data *regulators;
> +};
> +
> +struct mc34708_platform_data {
> +#define MC34708_USE_TOUCHSCREEN (1 << 0)
> +#define MC34708_USE_CODEC (1 << 1)
> +#define MC34708_USE_ADC (1 << 2)
> +#define MC34708_USE_RTC (1 << 3)
> +#define MC34708_USE_REGULATOR (1 << 4)
> +#define MC34708_USE_LED (1 << 5)
> + unsigned int flags;
> +
> + int num_regulators;
> + struct mc34708_regulator_init_data *regulators;
> +};
> +
> +#endif /* ifndef __LINUX_MFD_MC_PMIC_H */
More information about the linux-arm-kernel
mailing list