root/drivers/mfd/menelaus.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (C) 2004 Texas Instruments, Inc.
 *
 * Some parts based tps65010.c:
 * Copyright (C) 2004 Texas Instruments and
 * Copyright (C) 2004-2005 David Brownell
 *
 * Some parts based on tlv320aic24.c:
 * Copyright (C) by Kai Svahn <kai.svahn@nokia.com>
 *
 * Changes for interrupt handling and clean-up by
 * Tony Lindgren <tony@atomide.com> and Imre Deak <imre.deak@nokia.com>
 * Cleanup and generalized support for voltage setting by
 * Juha Yrjola
 * Added support for controlling VCORE and regulator sleep states,
 * Amit Kucheria <amit.kucheria@nokia.com>
 * Copyright (C) 2005, 2006 Nokia Corporation
 */

#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/slab.h>
#include <linux/mfd/menelaus.h>

#include <asm/mach/irq.h>


#define DRIVER_NAME                     "menelaus"

#define MENELAUS_I2C_ADDRESS            0x72

#define MENELAUS_REV                    0x01
#define MENELAUS_VCORE_CTRL1            0x02
#define MENELAUS_VCORE_CTRL2            0x03
#define MENELAUS_VCORE_CTRL3            0x04
#define MENELAUS_VCORE_CTRL4            0x05
#define MENELAUS_VCORE_CTRL5            0x06
#define MENELAUS_DCDC_CTRL1             0x07
#define MENELAUS_DCDC_CTRL2             0x08
#define MENELAUS_DCDC_CTRL3             0x09
#define MENELAUS_LDO_CTRL1              0x0A
#define MENELAUS_LDO_CTRL2              0x0B
#define MENELAUS_LDO_CTRL3              0x0C
#define MENELAUS_LDO_CTRL4              0x0D
#define MENELAUS_LDO_CTRL5              0x0E
#define MENELAUS_LDO_CTRL6              0x0F
#define MENELAUS_LDO_CTRL7              0x10
#define MENELAUS_LDO_CTRL8              0x11
#define MENELAUS_SLEEP_CTRL1            0x12
#define MENELAUS_SLEEP_CTRL2            0x13
#define MENELAUS_DEVICE_OFF             0x14
#define MENELAUS_OSC_CTRL               0x15
#define MENELAUS_DETECT_CTRL            0x16
#define MENELAUS_INT_MASK1              0x17
#define MENELAUS_INT_MASK2              0x18
#define MENELAUS_INT_STATUS1            0x19
#define MENELAUS_INT_STATUS2            0x1A
#define MENELAUS_INT_ACK1               0x1B
#define MENELAUS_INT_ACK2               0x1C
#define MENELAUS_GPIO_CTRL              0x1D
#define MENELAUS_GPIO_IN                0x1E
#define MENELAUS_GPIO_OUT               0x1F
#define MENELAUS_BBSMS                  0x20
#define MENELAUS_RTC_CTRL               0x21
#define MENELAUS_RTC_UPDATE             0x22
#define MENELAUS_RTC_SEC                0x23
#define MENELAUS_RTC_MIN                0x24
#define MENELAUS_RTC_HR                 0x25
#define MENELAUS_RTC_DAY                0x26
#define MENELAUS_RTC_MON                0x27
#define MENELAUS_RTC_YR                 0x28
#define MENELAUS_RTC_WKDAY              0x29
#define MENELAUS_RTC_AL_SEC             0x2A
#define MENELAUS_RTC_AL_MIN             0x2B
#define MENELAUS_RTC_AL_HR              0x2C
#define MENELAUS_RTC_AL_DAY             0x2D
#define MENELAUS_RTC_AL_MON             0x2E
#define MENELAUS_RTC_AL_YR              0x2F
#define MENELAUS_RTC_COMP_MSB           0x30
#define MENELAUS_RTC_COMP_LSB           0x31
#define MENELAUS_S1_PULL_EN             0x32
#define MENELAUS_S1_PULL_DIR            0x33
#define MENELAUS_S2_PULL_EN             0x34
#define MENELAUS_S2_PULL_DIR            0x35
#define MENELAUS_MCT_CTRL1              0x36
#define MENELAUS_MCT_CTRL2              0x37
#define MENELAUS_MCT_CTRL3              0x38
#define MENELAUS_MCT_PIN_ST             0x39
#define MENELAUS_DEBOUNCE1              0x3A

#define IH_MENELAUS_IRQS                12
#define MENELAUS_MMC_S1CD_IRQ           0       /* MMC slot 1 card change */
#define MENELAUS_MMC_S2CD_IRQ           1       /* MMC slot 2 card change */
#define MENELAUS_MMC_S1D1_IRQ           2       /* MMC DAT1 low in slot 1 */
#define MENELAUS_MMC_S2D1_IRQ           3       /* MMC DAT1 low in slot 2 */
#define MENELAUS_LOWBAT_IRQ             4       /* Low battery */
#define MENELAUS_HOTDIE_IRQ             5       /* Hot die detect */
#define MENELAUS_UVLO_IRQ               6       /* UVLO detect */
#define MENELAUS_TSHUT_IRQ              7       /* Thermal shutdown */
#define MENELAUS_RTCTMR_IRQ             8       /* RTC timer */
#define MENELAUS_RTCALM_IRQ             9       /* RTC alarm */
#define MENELAUS_RTCERR_IRQ             10      /* RTC error */
#define MENELAUS_PSHBTN_IRQ             11      /* Push button */
#define MENELAUS_RESERVED12_IRQ         12      /* Reserved */
#define MENELAUS_RESERVED13_IRQ         13      /* Reserved */
#define MENELAUS_RESERVED14_IRQ         14      /* Reserved */
#define MENELAUS_RESERVED15_IRQ         15      /* Reserved */

/* VCORE_CTRL1 register */
#define VCORE_CTRL1_BYP_COMP            (1 << 5)
#define VCORE_CTRL1_HW_NSW              (1 << 7)

/* GPIO_CTRL register */
#define GPIO_CTRL_SLOTSELEN             (1 << 5)
#define GPIO_CTRL_SLPCTLEN              (1 << 6)
#define GPIO1_DIR_INPUT                 (1 << 0)
#define GPIO2_DIR_INPUT                 (1 << 1)
#define GPIO3_DIR_INPUT                 (1 << 2)

/* MCT_CTRL1 register */
#define MCT_CTRL1_S1_CMD_OD             (1 << 2)
#define MCT_CTRL1_S2_CMD_OD             (1 << 3)

/* MCT_CTRL2 register */
#define MCT_CTRL2_VS2_SEL_D0            (1 << 0)
#define MCT_CTRL2_VS2_SEL_D1            (1 << 1)
#define MCT_CTRL2_S1CD_BUFEN            (1 << 4)
#define MCT_CTRL2_S2CD_BUFEN            (1 << 5)
#define MCT_CTRL2_S1CD_DBEN             (1 << 6)
#define MCT_CTRL2_S2CD_BEN              (1 << 7)

/* MCT_CTRL3 register */
#define MCT_CTRL3_SLOT1_EN              (1 << 0)
#define MCT_CTRL3_SLOT2_EN              (1 << 1)
#define MCT_CTRL3_S1_AUTO_EN            (1 << 2)
#define MCT_CTRL3_S2_AUTO_EN            (1 << 3)

/* MCT_PIN_ST register */
#define MCT_PIN_ST_S1_CD_ST             (1 << 0)
#define MCT_PIN_ST_S2_CD_ST             (1 << 1)

static void menelaus_work(struct work_struct *_menelaus);

struct menelaus_chip {
        struct mutex            lock;
        struct i2c_client       *client;
        struct work_struct      work;
#ifdef CONFIG_RTC_DRV_TWL92330
        struct rtc_device       *rtc;
        u8                      rtc_control;
        unsigned                uie:1;
#endif
        unsigned                vcore_hw_mode:1;
        u8                      mask1, mask2;
        void                    (*handlers[16])(struct menelaus_chip *);
        void                    (*mmc_callback)(void *data, u8 mask);
        void                    *mmc_callback_data;
};

static struct menelaus_chip *the_menelaus;

static int menelaus_write_reg(int reg, u8 value)
{
        int val = i2c_smbus_write_byte_data(the_menelaus->client, reg, value);

        if (val < 0) {
                pr_err(DRIVER_NAME ": write error");
                return val;
        }

        return 0;
}

static int menelaus_read_reg(int reg)
{
        int val = i2c_smbus_read_byte_data(the_menelaus->client, reg);

        if (val < 0)
                pr_err(DRIVER_NAME ": read error");

        return val;
}

static int menelaus_enable_irq(int irq)
{
        if (irq > 7) {
                irq -= 8;
                the_menelaus->mask2 &= ~(1 << irq);
                return menelaus_write_reg(MENELAUS_INT_MASK2,
                                the_menelaus->mask2);
        } else {
                the_menelaus->mask1 &= ~(1 << irq);
                return menelaus_write_reg(MENELAUS_INT_MASK1,
                                the_menelaus->mask1);
        }
}

static int menelaus_disable_irq(int irq)
{
        if (irq > 7) {
                irq -= 8;
                the_menelaus->mask2 |= (1 << irq);
                return menelaus_write_reg(MENELAUS_INT_MASK2,
                                the_menelaus->mask2);
        } else {
                the_menelaus->mask1 |= (1 << irq);
                return menelaus_write_reg(MENELAUS_INT_MASK1,
                                the_menelaus->mask1);
        }
}

static int menelaus_ack_irq(int irq)
{
        if (irq > 7)
                return menelaus_write_reg(MENELAUS_INT_ACK2, 1 << (irq - 8));
        else
                return menelaus_write_reg(MENELAUS_INT_ACK1, 1 << irq);
}

/* Adds a handler for an interrupt. Does not run in interrupt context */
static int menelaus_add_irq_work(int irq,
                void (*handler)(struct menelaus_chip *))
{
        int ret = 0;

        mutex_lock(&the_menelaus->lock);
        the_menelaus->handlers[irq] = handler;
        ret = menelaus_enable_irq(irq);
        mutex_unlock(&the_menelaus->lock);

        return ret;
}

/* Removes handler for an interrupt */
static int menelaus_remove_irq_work(int irq)
{
        int ret = 0;

        mutex_lock(&the_menelaus->lock);
        ret = menelaus_disable_irq(irq);
        the_menelaus->handlers[irq] = NULL;
        mutex_unlock(&the_menelaus->lock);

        return ret;
}

/*
 * Gets scheduled when a card detect interrupt happens. Note that in some cases
 * this line is wired to card cover switch rather than the card detect switch
 * in each slot. In this case the cards are not seen by menelaus.
 * FIXME: Add handling for D1 too
 */
static void menelaus_mmc_cd_work(struct menelaus_chip *menelaus_hw)
{
        int reg;
        unsigned char card_mask = 0;

        reg = menelaus_read_reg(MENELAUS_MCT_PIN_ST);
        if (reg < 0)
                return;

        if (!(reg & 0x1))
                card_mask |= MCT_PIN_ST_S1_CD_ST;

        if (!(reg & 0x2))
                card_mask |= MCT_PIN_ST_S2_CD_ST;

        if (menelaus_hw->mmc_callback)
                menelaus_hw->mmc_callback(menelaus_hw->mmc_callback_data,
                                          card_mask);
}

/*
 * Toggles the MMC slots between open-drain and push-pull mode.
 */
int menelaus_set_mmc_opendrain(int slot, int enable)
{
        int ret, val;

        if (slot != 1 && slot != 2)
                return -EINVAL;
        mutex_lock(&the_menelaus->lock);
        ret = menelaus_read_reg(MENELAUS_MCT_CTRL1);
        if (ret < 0) {
                mutex_unlock(&the_menelaus->lock);
                return ret;
        }
        val = ret;
        if (slot == 1) {
                if (enable)
                        val |= MCT_CTRL1_S1_CMD_OD;
                else
                        val &= ~MCT_CTRL1_S1_CMD_OD;
        } else {
                if (enable)
                        val |= MCT_CTRL1_S2_CMD_OD;
                else
                        val &= ~MCT_CTRL1_S2_CMD_OD;
        }
        ret = menelaus_write_reg(MENELAUS_MCT_CTRL1, val);
        mutex_unlock(&the_menelaus->lock);

        return ret;
}
EXPORT_SYMBOL(menelaus_set_mmc_opendrain);

int menelaus_set_slot_sel(int enable)
{
        int ret;

        mutex_lock(&the_menelaus->lock);
        ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
        if (ret < 0)
                goto out;
        ret |= GPIO2_DIR_INPUT;
        if (enable)
                ret |= GPIO_CTRL_SLOTSELEN;
        else
                ret &= ~GPIO_CTRL_SLOTSELEN;
        ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);
out:
        mutex_unlock(&the_menelaus->lock);
        return ret;
}
EXPORT_SYMBOL(menelaus_set_slot_sel);

int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_en)
{
        int ret, val;

        if (slot != 1 && slot != 2)
                return -EINVAL;
        if (power >= 3)
                return -EINVAL;

        mutex_lock(&the_menelaus->lock);

        ret = menelaus_read_reg(MENELAUS_MCT_CTRL2);
        if (ret < 0)
                goto out;
        val = ret;
        if (slot == 1) {
                if (cd_en)
                        val |= MCT_CTRL2_S1CD_BUFEN | MCT_CTRL2_S1CD_DBEN;
                else
                        val &= ~(MCT_CTRL2_S1CD_BUFEN | MCT_CTRL2_S1CD_DBEN);
        } else {
                if (cd_en)
                        val |= MCT_CTRL2_S2CD_BUFEN | MCT_CTRL2_S2CD_BEN;
                else
                        val &= ~(MCT_CTRL2_S2CD_BUFEN | MCT_CTRL2_S2CD_BEN);
        }
        ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, val);
        if (ret < 0)
                goto out;

        ret = menelaus_read_reg(MENELAUS_MCT_CTRL3);
        if (ret < 0)
                goto out;
        val = ret;
        if (slot == 1) {
                if (enable)
                        val |= MCT_CTRL3_SLOT1_EN;
                else
                        val &= ~MCT_CTRL3_SLOT1_EN;
        } else {
                int b;

                if (enable)
                        val |= MCT_CTRL3_SLOT2_EN;
                else
                        val &= ~MCT_CTRL3_SLOT2_EN;
                b = menelaus_read_reg(MENELAUS_MCT_CTRL2);
                b &= ~(MCT_CTRL2_VS2_SEL_D0 | MCT_CTRL2_VS2_SEL_D1);
                b |= power;
                ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, b);
                if (ret < 0)
                        goto out;
        }
        /* Disable autonomous shutdown */
        val &= ~(MCT_CTRL3_S1_AUTO_EN | MCT_CTRL3_S2_AUTO_EN);
        ret = menelaus_write_reg(MENELAUS_MCT_CTRL3, val);
out:
        mutex_unlock(&the_menelaus->lock);
        return ret;
}
EXPORT_SYMBOL(menelaus_set_mmc_slot);

int menelaus_register_mmc_callback(void (*callback)(void *data, u8 card_mask),
                                   void *data)
{
        int ret = 0;

        the_menelaus->mmc_callback_data = data;
        the_menelaus->mmc_callback = callback;
        ret = menelaus_add_irq_work(MENELAUS_MMC_S1CD_IRQ,
                                    menelaus_mmc_cd_work);
        if (ret < 0)
                return ret;
        ret = menelaus_add_irq_work(MENELAUS_MMC_S2CD_IRQ,
                                    menelaus_mmc_cd_work);
        if (ret < 0)
                return ret;
        ret = menelaus_add_irq_work(MENELAUS_MMC_S1D1_IRQ,
                                    menelaus_mmc_cd_work);
        if (ret < 0)
                return ret;
        ret = menelaus_add_irq_work(MENELAUS_MMC_S2D1_IRQ,
                                    menelaus_mmc_cd_work);

        return ret;
}
EXPORT_SYMBOL(menelaus_register_mmc_callback);

void menelaus_unregister_mmc_callback(void)
{
        menelaus_remove_irq_work(MENELAUS_MMC_S1CD_IRQ);
        menelaus_remove_irq_work(MENELAUS_MMC_S2CD_IRQ);
        menelaus_remove_irq_work(MENELAUS_MMC_S1D1_IRQ);
        menelaus_remove_irq_work(MENELAUS_MMC_S2D1_IRQ);

        the_menelaus->mmc_callback = NULL;
        the_menelaus->mmc_callback_data = NULL;
}
EXPORT_SYMBOL(menelaus_unregister_mmc_callback);

struct menelaus_vtg {
        const char *name;
        u8 vtg_reg;
        u8 vtg_shift;
        u8 vtg_bits;
        u8 mode_reg;
};

struct menelaus_vtg_value {
        u16 vtg;
        u16 val;
};

static int menelaus_set_voltage(const struct menelaus_vtg *vtg, int mV,
                                int vtg_val, int mode)
{
        int val, ret;
        struct i2c_client *c = the_menelaus->client;

        mutex_lock(&the_menelaus->lock);

        ret = menelaus_read_reg(vtg->vtg_reg);
        if (ret < 0)
                goto out;
        val = ret & ~(((1 << vtg->vtg_bits) - 1) << vtg->vtg_shift);
        val |= vtg_val << vtg->vtg_shift;

        dev_dbg(&c->dev, "Setting voltage '%s'"
                         "to %d mV (reg 0x%02x, val 0x%02x)\n",
                        vtg->name, mV, vtg->vtg_reg, val);

        ret = menelaus_write_reg(vtg->vtg_reg, val);
        if (ret < 0)
                goto out;
        ret = menelaus_write_reg(vtg->mode_reg, mode);
out:
        mutex_unlock(&the_menelaus->lock);
        if (ret == 0) {
                /* Wait for voltage to stabilize */
                msleep(1);
        }
        return ret;
}

static int menelaus_get_vtg_value(int vtg, const struct menelaus_vtg_value *tbl,
                                  int n)
{
        int i;

        for (i = 0; i < n; i++, tbl++)
                if (tbl->vtg == vtg)
                        return tbl->val;
        return -EINVAL;
}

/*
 * Vcore can be programmed in two ways:
 * SW-controlled: Required voltage is programmed into VCORE_CTRL1
 * HW-controlled: Required range (roof-floor) is programmed into VCORE_CTRL3
 * and VCORE_CTRL4
 *
 * Call correct 'set' function accordingly
 */

static const struct menelaus_vtg_value vcore_values[] = {
        { 1000, 0 },
        { 1025, 1 },
        { 1050, 2 },
        { 1075, 3 },
        { 1100, 4 },
        { 1125, 5 },
        { 1150, 6 },
        { 1175, 7 },
        { 1200, 8 },
        { 1225, 9 },
        { 1250, 10 },
        { 1275, 11 },
        { 1300, 12 },
        { 1325, 13 },
        { 1350, 14 },
        { 1375, 15 },
        { 1400, 16 },
        { 1425, 17 },
        { 1450, 18 },
};

int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV)
{
        int fval, rval, val, ret;
        struct i2c_client *c = the_menelaus->client;

        rval = menelaus_get_vtg_value(roof_mV, vcore_values,
                                      ARRAY_SIZE(vcore_values));
        if (rval < 0)
                return -EINVAL;
        fval = menelaus_get_vtg_value(floor_mV, vcore_values,
                                      ARRAY_SIZE(vcore_values));
        if (fval < 0)
                return -EINVAL;

        dev_dbg(&c->dev, "Setting VCORE FLOOR to %d mV and ROOF to %d mV\n",
               floor_mV, roof_mV);

        mutex_lock(&the_menelaus->lock);
        ret = menelaus_write_reg(MENELAUS_VCORE_CTRL3, fval);
        if (ret < 0)
                goto out;
        ret = menelaus_write_reg(MENELAUS_VCORE_CTRL4, rval);
        if (ret < 0)
                goto out;
        if (!the_menelaus->vcore_hw_mode) {
                val = menelaus_read_reg(MENELAUS_VCORE_CTRL1);
                /* HW mode, turn OFF byte comparator */
                val |= (VCORE_CTRL1_HW_NSW | VCORE_CTRL1_BYP_COMP);
                ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val);
                the_menelaus->vcore_hw_mode = 1;
        }
        msleep(1);
out:
        mutex_unlock(&the_menelaus->lock);
        return ret;
}

static const struct menelaus_vtg vmem_vtg = {
        .name = "VMEM",
        .vtg_reg = MENELAUS_LDO_CTRL1,
        .vtg_shift = 0,
        .vtg_bits = 2,
        .mode_reg = MENELAUS_LDO_CTRL3,
};

static const struct menelaus_vtg_value vmem_values[] = {
        { 1500, 0 },
        { 1800, 1 },
        { 1900, 2 },
        { 2500, 3 },
};

int menelaus_set_vmem(unsigned int mV)
{
        int val;

        if (mV == 0)
                return menelaus_set_voltage(&vmem_vtg, 0, 0, 0);

        val = menelaus_get_vtg_value(mV, vmem_values, ARRAY_SIZE(vmem_values));
        if (val < 0)
                return -EINVAL;
        return menelaus_set_voltage(&vmem_vtg, mV, val, 0x02);
}
EXPORT_SYMBOL(menelaus_set_vmem);

static const struct menelaus_vtg vio_vtg = {
        .name = "VIO",
        .vtg_reg = MENELAUS_LDO_CTRL1,
        .vtg_shift = 2,
        .vtg_bits = 2,
        .mode_reg = MENELAUS_LDO_CTRL4,
};

static const struct menelaus_vtg_value vio_values[] = {
        { 1500, 0 },
        { 1800, 1 },
        { 2500, 2 },
        { 2800, 3 },
};

int menelaus_set_vio(unsigned int mV)
{
        int val;

        if (mV == 0)
                return menelaus_set_voltage(&vio_vtg, 0, 0, 0);

        val = menelaus_get_vtg_value(mV, vio_values, ARRAY_SIZE(vio_values));
        if (val < 0)
                return -EINVAL;
        return menelaus_set_voltage(&vio_vtg, mV, val, 0x02);
}
EXPORT_SYMBOL(menelaus_set_vio);

static const struct menelaus_vtg_value vdcdc_values[] = {
        { 1500, 0 },
        { 1800, 1 },
        { 2000, 2 },
        { 2200, 3 },
        { 2400, 4 },
        { 2800, 5 },
        { 3000, 6 },
        { 3300, 7 },
};

static const struct menelaus_vtg vdcdc2_vtg = {
        .name = "VDCDC2",
        .vtg_reg = MENELAUS_DCDC_CTRL1,
        .vtg_shift = 0,
        .vtg_bits = 3,
        .mode_reg = MENELAUS_DCDC_CTRL2,
};

static const struct menelaus_vtg vdcdc3_vtg = {
        .name = "VDCDC3",
        .vtg_reg = MENELAUS_DCDC_CTRL1,
        .vtg_shift = 3,
        .vtg_bits = 3,
        .mode_reg = MENELAUS_DCDC_CTRL3,
};

int menelaus_set_vdcdc(int dcdc, unsigned int mV)
{
        const struct menelaus_vtg *vtg;
        int val;

        if (dcdc != 2 && dcdc != 3)
                return -EINVAL;
        if (dcdc == 2)
                vtg = &vdcdc2_vtg;
        else
                vtg = &vdcdc3_vtg;

        if (mV == 0)
                return menelaus_set_voltage(vtg, 0, 0, 0);

        val = menelaus_get_vtg_value(mV, vdcdc_values,
                                     ARRAY_SIZE(vdcdc_values));
        if (val < 0)
                return -EINVAL;
        return menelaus_set_voltage(vtg, mV, val, 0x03);
}

static const struct menelaus_vtg_value vmmc_values[] = {
        { 1850, 0 },
        { 2800, 1 },
        { 3000, 2 },
        { 3100, 3 },
};

static const struct menelaus_vtg vmmc_vtg = {
        .name = "VMMC",
        .vtg_reg = MENELAUS_LDO_CTRL1,
        .vtg_shift = 6,
        .vtg_bits = 2,
        .mode_reg = MENELAUS_LDO_CTRL7,
};

int menelaus_set_vmmc(unsigned int mV)
{
        int val;

        if (mV == 0)
                return menelaus_set_voltage(&vmmc_vtg, 0, 0, 0);

        val = menelaus_get_vtg_value(mV, vmmc_values, ARRAY_SIZE(vmmc_values));
        if (val < 0)
                return -EINVAL;
        return menelaus_set_voltage(&vmmc_vtg, mV, val, 0x02);
}
EXPORT_SYMBOL(menelaus_set_vmmc);


static const struct menelaus_vtg_value vaux_values[] = {
        { 1500, 0 },
        { 1800, 1 },
        { 2500, 2 },
        { 2800, 3 },
};

static const struct menelaus_vtg vaux_vtg = {
        .name = "VAUX",
        .vtg_reg = MENELAUS_LDO_CTRL1,
        .vtg_shift = 4,
        .vtg_bits = 2,
        .mode_reg = MENELAUS_LDO_CTRL6,
};

int menelaus_set_vaux(unsigned int mV)
{
        int val;

        if (mV == 0)
                return menelaus_set_voltage(&vaux_vtg, 0, 0, 0);

        val = menelaus_get_vtg_value(mV, vaux_values, ARRAY_SIZE(vaux_values));
        if (val < 0)
                return -EINVAL;
        return menelaus_set_voltage(&vaux_vtg, mV, val, 0x02);
}
EXPORT_SYMBOL(menelaus_set_vaux);

int menelaus_get_slot_pin_states(void)
{
        return menelaus_read_reg(MENELAUS_MCT_PIN_ST);
}
EXPORT_SYMBOL(menelaus_get_slot_pin_states);

int menelaus_set_regulator_sleep(int enable, u32 val)
{
        int t, ret;
        struct i2c_client *c = the_menelaus->client;

        mutex_lock(&the_menelaus->lock);
        ret = menelaus_write_reg(MENELAUS_SLEEP_CTRL2, val);
        if (ret < 0)
                goto out;

        dev_dbg(&c->dev, "regulator sleep configuration: %02x\n", val);

        ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
        if (ret < 0)
                goto out;
        t = (GPIO_CTRL_SLPCTLEN | GPIO3_DIR_INPUT);
        if (enable)
                ret |= t;
        else
                ret &= ~t;
        ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);
out:
        mutex_unlock(&the_menelaus->lock);
        return ret;
}

/*-----------------------------------------------------------------------*/

/* Handles Menelaus interrupts. Does not run in interrupt context */
static void menelaus_work(struct work_struct *_menelaus)
{
        struct menelaus_chip *menelaus =
                        container_of(_menelaus, struct menelaus_chip, work);
        void (*handler)(struct menelaus_chip *menelaus);

        while (1) {
                unsigned isr;

                isr = (menelaus_read_reg(MENELAUS_INT_STATUS2)
                                & ~menelaus->mask2) << 8;
                isr |= menelaus_read_reg(MENELAUS_INT_STATUS1)
                                & ~menelaus->mask1;
                if (!isr)
                        break;

                while (isr) {
                        int irq = fls(isr) - 1;
                        isr &= ~(1 << irq);

                        mutex_lock(&menelaus->lock);
                        menelaus_disable_irq(irq);
                        menelaus_ack_irq(irq);
                        handler = menelaus->handlers[irq];
                        if (handler)
                                handler(menelaus);
                        menelaus_enable_irq(irq);
                        mutex_unlock(&menelaus->lock);
                }
        }
        enable_irq(menelaus->client->irq);
}

/*
 * We cannot use I2C in interrupt context, so we just schedule work.
 */
static irqreturn_t menelaus_irq(int irq, void *_menelaus)
{
        struct menelaus_chip *menelaus = _menelaus;

        disable_irq_nosync(irq);
        (void)schedule_work(&menelaus->work);

        return IRQ_HANDLED;
}

/*-----------------------------------------------------------------------*/

/*
 * The RTC needs to be set once, then it runs on backup battery power.
 * It supports alarms, including system wake alarms (from some modes);
 * and 1/second IRQs if requested.
 */
#ifdef CONFIG_RTC_DRV_TWL92330

#define RTC_CTRL_RTC_EN         (1 << 0)
#define RTC_CTRL_AL_EN          (1 << 1)
#define RTC_CTRL_MODE12         (1 << 2)
#define RTC_CTRL_EVERY_MASK     (3 << 3)
#define RTC_CTRL_EVERY_SEC      (0 << 3)
#define RTC_CTRL_EVERY_MIN      (1 << 3)
#define RTC_CTRL_EVERY_HR       (2 << 3)
#define RTC_CTRL_EVERY_DAY      (3 << 3)

#define RTC_UPDATE_EVERY        0x08

#define RTC_HR_PM               (1 << 7)

static void menelaus_to_time(char *regs, struct rtc_time *t)
{
        t->tm_sec = bcd2bin(regs[0]);
        t->tm_min = bcd2bin(regs[1]);
        if (the_menelaus->rtc_control & RTC_CTRL_MODE12) {
                t->tm_hour = bcd2bin(regs[2] & 0x1f) - 1;
                if (regs[2] & RTC_HR_PM)
                        t->tm_hour += 12;
        } else
                t->tm_hour = bcd2bin(regs[2] & 0x3f);
        t->tm_mday = bcd2bin(regs[3]);
        t->tm_mon = bcd2bin(regs[4]) - 1;
        t->tm_year = bcd2bin(regs[5]) + 100;
}

static int time_to_menelaus(struct rtc_time *t, int regnum)
{
        int     hour, status;

        status = menelaus_write_reg(regnum++, bin2bcd(t->tm_sec));
        if (status < 0)
                goto fail;

        status = menelaus_write_reg(regnum++, bin2bcd(t->tm_min));
        if (status < 0)
                goto fail;

        if (the_menelaus->rtc_control & RTC_CTRL_MODE12) {
                hour = t->tm_hour + 1;
                if (hour > 12)
                        hour = RTC_HR_PM | bin2bcd(hour - 12);
                else
                        hour = bin2bcd(hour);
        } else
                hour = bin2bcd(t->tm_hour);
        status = menelaus_write_reg(regnum++, hour);
        if (status < 0)
                goto fail;

        status = menelaus_write_reg(regnum++, bin2bcd(t->tm_mday));
        if (status < 0)
                goto fail;

        status = menelaus_write_reg(regnum++, bin2bcd(t->tm_mon + 1));
        if (status < 0)
                goto fail;

        status = menelaus_write_reg(regnum++, bin2bcd(t->tm_year - 100));
        if (status < 0)
                goto fail;

        return 0;
fail:
        dev_err(&the_menelaus->client->dev, "rtc write reg %02x, err %d\n",
                        --regnum, status);
        return status;
}

static int menelaus_read_time(struct device *dev, struct rtc_time *t)
{
        struct i2c_msg  msg[2];
        char            regs[7];
        int             status;

        /* block read date and time registers */
        regs[0] = MENELAUS_RTC_SEC;

        msg[0].addr = MENELAUS_I2C_ADDRESS;
        msg[0].flags = 0;
        msg[0].len = 1;
        msg[0].buf = regs;

        msg[1].addr = MENELAUS_I2C_ADDRESS;
        msg[1].flags = I2C_M_RD;
        msg[1].len = sizeof(regs);
        msg[1].buf = regs;

        status = i2c_transfer(the_menelaus->client->adapter, msg, 2);
        if (status != 2) {
                dev_err(dev, "%s error %d\n", "read", status);
                return -EIO;
        }

        menelaus_to_time(regs, t);
        t->tm_wday = bcd2bin(regs[6]);

        return 0;
}

static int menelaus_set_time(struct device *dev, struct rtc_time *t)
{
        int             status;

        /* write date and time registers */
        status = time_to_menelaus(t, MENELAUS_RTC_SEC);
        if (status < 0)
                return status;
        status = menelaus_write_reg(MENELAUS_RTC_WKDAY, bin2bcd(t->tm_wday));
        if (status < 0) {
                dev_err(&the_menelaus->client->dev, "rtc write reg %02x "
                                "err %d\n", MENELAUS_RTC_WKDAY, status);
                return status;
        }

        /* now commit the write */
        status = menelaus_write_reg(MENELAUS_RTC_UPDATE, RTC_UPDATE_EVERY);
        if (status < 0)
                dev_err(&the_menelaus->client->dev, "rtc commit time, err %d\n",
                                status);

        return 0;
}

static int menelaus_read_alarm(struct device *dev, struct rtc_wkalrm *w)
{
        struct i2c_msg  msg[2];
        char            regs[6];
        int             status;

        /* block read alarm registers */
        regs[0] = MENELAUS_RTC_AL_SEC;

        msg[0].addr = MENELAUS_I2C_ADDRESS;
        msg[0].flags = 0;
        msg[0].len = 1;
        msg[0].buf = regs;

        msg[1].addr = MENELAUS_I2C_ADDRESS;
        msg[1].flags = I2C_M_RD;
        msg[1].len = sizeof(regs);
        msg[1].buf = regs;

        status = i2c_transfer(the_menelaus->client->adapter, msg, 2);
        if (status != 2) {
                dev_err(dev, "%s error %d\n", "alarm read", status);
                return -EIO;
        }

        menelaus_to_time(regs, &w->time);

        w->enabled = !!(the_menelaus->rtc_control & RTC_CTRL_AL_EN);

        /* NOTE we *could* check if actually pending... */
        w->pending = 0;

        return 0;
}

static int menelaus_set_alarm(struct device *dev, struct rtc_wkalrm *w)
{
        int             status;

        if (the_menelaus->client->irq <= 0 && w->enabled)
                return -ENODEV;

        /* clear previous alarm enable */
        if (the_menelaus->rtc_control & RTC_CTRL_AL_EN) {
                the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
                status = menelaus_write_reg(MENELAUS_RTC_CTRL,
                                the_menelaus->rtc_control);
                if (status < 0)
                        return status;
        }

        /* write alarm registers */
        status = time_to_menelaus(&w->time, MENELAUS_RTC_AL_SEC);
        if (status < 0)
                return status;

        /* enable alarm if requested */
        if (w->enabled) {
                the_menelaus->rtc_control |= RTC_CTRL_AL_EN;
                status = menelaus_write_reg(MENELAUS_RTC_CTRL,
                                the_menelaus->rtc_control);
        }

        return status;
}

#ifdef CONFIG_RTC_INTF_DEV

static void menelaus_rtc_update_work(struct menelaus_chip *m)
{
        /* report 1/sec update */
        rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_UF);
}

static int menelaus_ioctl(struct device *dev, unsigned cmd, unsigned long arg)
{
        int     status;

        if (the_menelaus->client->irq <= 0)
                return -ENOIOCTLCMD;

        switch (cmd) {
        /* alarm IRQ */
        case RTC_AIE_ON:
                if (the_menelaus->rtc_control & RTC_CTRL_AL_EN)
                        return 0;
                the_menelaus->rtc_control |= RTC_CTRL_AL_EN;
                break;
        case RTC_AIE_OFF:
                if (!(the_menelaus->rtc_control & RTC_CTRL_AL_EN))
                        return 0;
                the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
                break;
        /* 1/second "update" IRQ */
        case RTC_UIE_ON:
                if (the_menelaus->uie)
                        return 0;
                status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ);
                status = menelaus_add_irq_work(MENELAUS_RTCTMR_IRQ,
                                menelaus_rtc_update_work);
                if (status == 0)
                        the_menelaus->uie = 1;
                return status;
        case RTC_UIE_OFF:
                if (!the_menelaus->uie)
                        return 0;
                status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ);
                if (status == 0)
                        the_menelaus->uie = 0;
                return status;
        default:
                return -ENOIOCTLCMD;
        }
        return menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control);
}

#else
#define menelaus_ioctl  NULL
#endif

/* REVISIT no compensation register support ... */

static const struct rtc_class_ops menelaus_rtc_ops = {
        .ioctl                  = menelaus_ioctl,
        .read_time              = menelaus_read_time,
        .set_time               = menelaus_set_time,
        .read_alarm             = menelaus_read_alarm,
        .set_alarm              = menelaus_set_alarm,
};

static void menelaus_rtc_alarm_work(struct menelaus_chip *m)
{
        /* report alarm */
        rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_AF);

        /* then disable it; alarms are oneshot */
        the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
        menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control);
}

static inline void menelaus_rtc_init(struct menelaus_chip *m)
{
        int     alarm = (m->client->irq > 0);
        int     err;

        /* assume 32KDETEN pin is pulled high */
        if (!(menelaus_read_reg(MENELAUS_OSC_CTRL) & 0x80)) {
                dev_dbg(&m->client->dev, "no 32k oscillator\n");
                return;
        }

        m->rtc = devm_rtc_allocate_device(&m->client->dev);
        if (IS_ERR(m->rtc))
                return;

        m->rtc->ops = &menelaus_rtc_ops;

        /* support RTC alarm; it can issue wakeups */
        if (alarm) {
                if (menelaus_add_irq_work(MENELAUS_RTCALM_IRQ,
                                menelaus_rtc_alarm_work) < 0) {
                        dev_err(&m->client->dev, "can't handle RTC alarm\n");
                        return;
                }
                device_init_wakeup(&m->client->dev, 1);
        }

        /* be sure RTC is enabled; allow 1/sec irqs; leave 12hr mode alone */
        m->rtc_control = menelaus_read_reg(MENELAUS_RTC_CTRL);
        if (!(m->rtc_control & RTC_CTRL_RTC_EN)
                        || (m->rtc_control & RTC_CTRL_AL_EN)
                        || (m->rtc_control & RTC_CTRL_EVERY_MASK)) {
                if (!(m->rtc_control & RTC_CTRL_RTC_EN)) {
                        dev_warn(&m->client->dev, "rtc clock needs setting\n");
                        m->rtc_control |= RTC_CTRL_RTC_EN;
                }
                m->rtc_control &= ~RTC_CTRL_EVERY_MASK;
                m->rtc_control &= ~RTC_CTRL_AL_EN;
                menelaus_write_reg(MENELAUS_RTC_CTRL, m->rtc_control);
        }

        err = devm_rtc_register_device(m->rtc);
        if (err) {
                if (alarm) {
                        menelaus_remove_irq_work(MENELAUS_RTCALM_IRQ);
                        device_init_wakeup(&m->client->dev, 0);
                }
                the_menelaus->rtc = NULL;
        }
}

#else

static inline void menelaus_rtc_init(struct menelaus_chip *m)
{
        /* nothing */
}

#endif

/*-----------------------------------------------------------------------*/

static struct i2c_driver menelaus_i2c_driver;

static int menelaus_probe(struct i2c_client *client)
{
        struct menelaus_chip    *menelaus;
        int                     rev = 0;
        int                     err = 0;
        struct menelaus_platform_data *menelaus_pdata =
                                        dev_get_platdata(&client->dev);

        if (the_menelaus) {
                dev_dbg(&client->dev, "only one %s for now\n",
                                DRIVER_NAME);
                return -ENODEV;
        }

        menelaus = devm_kzalloc(&client->dev, sizeof(*menelaus), GFP_KERNEL);
        if (!menelaus)
                return -ENOMEM;

        i2c_set_clientdata(client, menelaus);

        the_menelaus = menelaus;
        menelaus->client = client;

        /* If a true probe check the device */
        rev = menelaus_read_reg(MENELAUS_REV);
        if (rev < 0) {
                pr_err(DRIVER_NAME ": device not found");
                return -ENODEV;
        }

        /* Ack and disable all Menelaus interrupts */
        menelaus_write_reg(MENELAUS_INT_ACK1, 0xff);
        menelaus_write_reg(MENELAUS_INT_ACK2, 0xff);
        menelaus_write_reg(MENELAUS_INT_MASK1, 0xff);
        menelaus_write_reg(MENELAUS_INT_MASK2, 0xff);
        menelaus->mask1 = 0xff;
        menelaus->mask2 = 0xff;

        /* Set output buffer strengths */
        menelaus_write_reg(MENELAUS_MCT_CTRL1, 0x73);

        if (client->irq > 0) {
                err = request_irq(client->irq, menelaus_irq, 0,
                                  DRIVER_NAME, menelaus);
                if (err) {
                        dev_dbg(&client->dev,  "can't get IRQ %d, err %d\n",
                                        client->irq, err);
                        return err;
                }
        }

        mutex_init(&menelaus->lock);
        INIT_WORK(&menelaus->work, menelaus_work);

        pr_info("Menelaus rev %d.%d\n", rev >> 4, rev & 0x0f);

        err = menelaus_read_reg(MENELAUS_VCORE_CTRL1);
        if (err < 0)
                goto fail;
        if (err & VCORE_CTRL1_HW_NSW)
                menelaus->vcore_hw_mode = 1;
        else
                menelaus->vcore_hw_mode = 0;

        if (menelaus_pdata != NULL && menelaus_pdata->late_init != NULL) {
                err = menelaus_pdata->late_init(&client->dev);
                if (err < 0)
                        goto fail;
        }

        menelaus_rtc_init(menelaus);

        return 0;
fail:
        free_irq(client->irq, menelaus);
        flush_work(&menelaus->work);
        return err;
}

static void menelaus_remove(struct i2c_client *client)
{
        struct menelaus_chip    *menelaus = i2c_get_clientdata(client);

        free_irq(client->irq, menelaus);
        flush_work(&menelaus->work);
        the_menelaus = NULL;
}

static const struct i2c_device_id menelaus_id[] = {
        { "menelaus" },
        { }
};
MODULE_DEVICE_TABLE(i2c, menelaus_id);

static struct i2c_driver menelaus_i2c_driver = {
        .driver = {
                .name           = DRIVER_NAME,
        },
        .probe          = menelaus_probe,
        .remove         = menelaus_remove,
        .id_table       = menelaus_id,
};

module_i2c_driver(menelaus_i2c_driver);

MODULE_AUTHOR("Texas Instruments, Inc. (and others)");
MODULE_DESCRIPTION("I2C interface for Menelaus.");
MODULE_LICENSE("GPL");