188e75cc3SDavid Brownell /* 288e75cc3SDavid Brownell * Copyright (C) 2004 Texas Instruments, Inc. 388e75cc3SDavid Brownell * 488e75cc3SDavid Brownell * Some parts based tps65010.c: 588e75cc3SDavid Brownell * Copyright (C) 2004 Texas Instruments and 688e75cc3SDavid Brownell * Copyright (C) 2004-2005 David Brownell 788e75cc3SDavid Brownell * 888e75cc3SDavid Brownell * Some parts based on tlv320aic24.c: 988e75cc3SDavid Brownell * Copyright (C) by Kai Svahn <kai.svahn@nokia.com> 1088e75cc3SDavid Brownell * 1188e75cc3SDavid Brownell * Changes for interrupt handling and clean-up by 1288e75cc3SDavid Brownell * Tony Lindgren <tony@atomide.com> and Imre Deak <imre.deak@nokia.com> 1388e75cc3SDavid Brownell * Cleanup and generalized support for voltage setting by 1488e75cc3SDavid Brownell * Juha Yrjola 1588e75cc3SDavid Brownell * Added support for controlling VCORE and regulator sleep states, 1688e75cc3SDavid Brownell * Amit Kucheria <amit.kucheria@nokia.com> 1788e75cc3SDavid Brownell * Copyright (C) 2005, 2006 Nokia Corporation 1888e75cc3SDavid Brownell * 1988e75cc3SDavid Brownell * This program is free software; you can redistribute it and/or modify 2088e75cc3SDavid Brownell * it under the terms of the GNU General Public License as published by 2188e75cc3SDavid Brownell * the Free Software Foundation; either version 2 of the License, or 2288e75cc3SDavid Brownell * (at your option) any later version. 2388e75cc3SDavid Brownell * 2488e75cc3SDavid Brownell * This program is distributed in the hope that it will be useful, 2588e75cc3SDavid Brownell * but WITHOUT ANY WARRANTY; without even the implied warranty of 2688e75cc3SDavid Brownell * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 2788e75cc3SDavid Brownell * GNU General Public License for more details. 2888e75cc3SDavid Brownell * 2988e75cc3SDavid Brownell * You should have received a copy of the GNU General Public License 3088e75cc3SDavid Brownell * along with this program; if not, write to the Free Software 3188e75cc3SDavid Brownell * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 3288e75cc3SDavid Brownell */ 3388e75cc3SDavid Brownell 3488e75cc3SDavid Brownell #include <linux/module.h> 3588e75cc3SDavid Brownell #include <linux/i2c.h> 3688e75cc3SDavid Brownell #include <linux/interrupt.h> 3788e75cc3SDavid Brownell #include <linux/sched.h> 3888e75cc3SDavid Brownell #include <linux/mutex.h> 3988e75cc3SDavid Brownell #include <linux/workqueue.h> 4088e75cc3SDavid Brownell #include <linux/delay.h> 4188e75cc3SDavid Brownell #include <linux/rtc.h> 4288e75cc3SDavid Brownell #include <linux/bcd.h> 435a0e3ad6STejun Heo #include <linux/slab.h> 447bd3b618STony Lindgren #include <linux/mfd/menelaus.h> 4588e75cc3SDavid Brownell 4688e75cc3SDavid Brownell #include <asm/mach/irq.h> 4788e75cc3SDavid Brownell 481bc857f7SRussell King #include <asm/gpio.h> 4988e75cc3SDavid Brownell 5088e75cc3SDavid Brownell #define DRIVER_NAME "menelaus" 5188e75cc3SDavid Brownell 5288e75cc3SDavid Brownell #define MENELAUS_I2C_ADDRESS 0x72 5388e75cc3SDavid Brownell 5488e75cc3SDavid Brownell #define MENELAUS_REV 0x01 5588e75cc3SDavid Brownell #define MENELAUS_VCORE_CTRL1 0x02 5688e75cc3SDavid Brownell #define MENELAUS_VCORE_CTRL2 0x03 5788e75cc3SDavid Brownell #define MENELAUS_VCORE_CTRL3 0x04 5888e75cc3SDavid Brownell #define MENELAUS_VCORE_CTRL4 0x05 5988e75cc3SDavid Brownell #define MENELAUS_VCORE_CTRL5 0x06 6088e75cc3SDavid Brownell #define MENELAUS_DCDC_CTRL1 0x07 6188e75cc3SDavid Brownell #define MENELAUS_DCDC_CTRL2 0x08 6288e75cc3SDavid Brownell #define MENELAUS_DCDC_CTRL3 0x09 6388e75cc3SDavid Brownell #define MENELAUS_LDO_CTRL1 0x0A 6488e75cc3SDavid Brownell #define MENELAUS_LDO_CTRL2 0x0B 6588e75cc3SDavid Brownell #define MENELAUS_LDO_CTRL3 0x0C 6688e75cc3SDavid Brownell #define MENELAUS_LDO_CTRL4 0x0D 6788e75cc3SDavid Brownell #define MENELAUS_LDO_CTRL5 0x0E 6888e75cc3SDavid Brownell #define MENELAUS_LDO_CTRL6 0x0F 6988e75cc3SDavid Brownell #define MENELAUS_LDO_CTRL7 0x10 7088e75cc3SDavid Brownell #define MENELAUS_LDO_CTRL8 0x11 7188e75cc3SDavid Brownell #define MENELAUS_SLEEP_CTRL1 0x12 7288e75cc3SDavid Brownell #define MENELAUS_SLEEP_CTRL2 0x13 7388e75cc3SDavid Brownell #define MENELAUS_DEVICE_OFF 0x14 7488e75cc3SDavid Brownell #define MENELAUS_OSC_CTRL 0x15 7588e75cc3SDavid Brownell #define MENELAUS_DETECT_CTRL 0x16 7688e75cc3SDavid Brownell #define MENELAUS_INT_MASK1 0x17 7788e75cc3SDavid Brownell #define MENELAUS_INT_MASK2 0x18 7888e75cc3SDavid Brownell #define MENELAUS_INT_STATUS1 0x19 7988e75cc3SDavid Brownell #define MENELAUS_INT_STATUS2 0x1A 8088e75cc3SDavid Brownell #define MENELAUS_INT_ACK1 0x1B 8188e75cc3SDavid Brownell #define MENELAUS_INT_ACK2 0x1C 8288e75cc3SDavid Brownell #define MENELAUS_GPIO_CTRL 0x1D 8388e75cc3SDavid Brownell #define MENELAUS_GPIO_IN 0x1E 8488e75cc3SDavid Brownell #define MENELAUS_GPIO_OUT 0x1F 8588e75cc3SDavid Brownell #define MENELAUS_BBSMS 0x20 8688e75cc3SDavid Brownell #define MENELAUS_RTC_CTRL 0x21 8788e75cc3SDavid Brownell #define MENELAUS_RTC_UPDATE 0x22 8888e75cc3SDavid Brownell #define MENELAUS_RTC_SEC 0x23 8988e75cc3SDavid Brownell #define MENELAUS_RTC_MIN 0x24 9088e75cc3SDavid Brownell #define MENELAUS_RTC_HR 0x25 9188e75cc3SDavid Brownell #define MENELAUS_RTC_DAY 0x26 9288e75cc3SDavid Brownell #define MENELAUS_RTC_MON 0x27 9388e75cc3SDavid Brownell #define MENELAUS_RTC_YR 0x28 9488e75cc3SDavid Brownell #define MENELAUS_RTC_WKDAY 0x29 9588e75cc3SDavid Brownell #define MENELAUS_RTC_AL_SEC 0x2A 9688e75cc3SDavid Brownell #define MENELAUS_RTC_AL_MIN 0x2B 9788e75cc3SDavid Brownell #define MENELAUS_RTC_AL_HR 0x2C 9888e75cc3SDavid Brownell #define MENELAUS_RTC_AL_DAY 0x2D 9988e75cc3SDavid Brownell #define MENELAUS_RTC_AL_MON 0x2E 10088e75cc3SDavid Brownell #define MENELAUS_RTC_AL_YR 0x2F 10188e75cc3SDavid Brownell #define MENELAUS_RTC_COMP_MSB 0x30 10288e75cc3SDavid Brownell #define MENELAUS_RTC_COMP_LSB 0x31 10388e75cc3SDavid Brownell #define MENELAUS_S1_PULL_EN 0x32 10488e75cc3SDavid Brownell #define MENELAUS_S1_PULL_DIR 0x33 10588e75cc3SDavid Brownell #define MENELAUS_S2_PULL_EN 0x34 10688e75cc3SDavid Brownell #define MENELAUS_S2_PULL_DIR 0x35 10788e75cc3SDavid Brownell #define MENELAUS_MCT_CTRL1 0x36 10888e75cc3SDavid Brownell #define MENELAUS_MCT_CTRL2 0x37 10988e75cc3SDavid Brownell #define MENELAUS_MCT_CTRL3 0x38 11088e75cc3SDavid Brownell #define MENELAUS_MCT_PIN_ST 0x39 11188e75cc3SDavid Brownell #define MENELAUS_DEBOUNCE1 0x3A 11288e75cc3SDavid Brownell 11388e75cc3SDavid Brownell #define IH_MENELAUS_IRQS 12 11488e75cc3SDavid Brownell #define MENELAUS_MMC_S1CD_IRQ 0 /* MMC slot 1 card change */ 11588e75cc3SDavid Brownell #define MENELAUS_MMC_S2CD_IRQ 1 /* MMC slot 2 card change */ 11688e75cc3SDavid Brownell #define MENELAUS_MMC_S1D1_IRQ 2 /* MMC DAT1 low in slot 1 */ 11788e75cc3SDavid Brownell #define MENELAUS_MMC_S2D1_IRQ 3 /* MMC DAT1 low in slot 2 */ 11888e75cc3SDavid Brownell #define MENELAUS_LOWBAT_IRQ 4 /* Low battery */ 11988e75cc3SDavid Brownell #define MENELAUS_HOTDIE_IRQ 5 /* Hot die detect */ 12088e75cc3SDavid Brownell #define MENELAUS_UVLO_IRQ 6 /* UVLO detect */ 12188e75cc3SDavid Brownell #define MENELAUS_TSHUT_IRQ 7 /* Thermal shutdown */ 12288e75cc3SDavid Brownell #define MENELAUS_RTCTMR_IRQ 8 /* RTC timer */ 12388e75cc3SDavid Brownell #define MENELAUS_RTCALM_IRQ 9 /* RTC alarm */ 12488e75cc3SDavid Brownell #define MENELAUS_RTCERR_IRQ 10 /* RTC error */ 12588e75cc3SDavid Brownell #define MENELAUS_PSHBTN_IRQ 11 /* Push button */ 12688e75cc3SDavid Brownell #define MENELAUS_RESERVED12_IRQ 12 /* Reserved */ 12788e75cc3SDavid Brownell #define MENELAUS_RESERVED13_IRQ 13 /* Reserved */ 12888e75cc3SDavid Brownell #define MENELAUS_RESERVED14_IRQ 14 /* Reserved */ 12988e75cc3SDavid Brownell #define MENELAUS_RESERVED15_IRQ 15 /* Reserved */ 13088e75cc3SDavid Brownell 1311c888e2eSJarkko Nikula /* VCORE_CTRL1 register */ 1321c888e2eSJarkko Nikula #define VCORE_CTRL1_BYP_COMP (1 << 5) 1331c888e2eSJarkko Nikula #define VCORE_CTRL1_HW_NSW (1 << 7) 1341c888e2eSJarkko Nikula 1351c888e2eSJarkko Nikula /* GPIO_CTRL register */ 1361c888e2eSJarkko Nikula #define GPIO_CTRL_SLOTSELEN (1 << 5) 1371c888e2eSJarkko Nikula #define GPIO_CTRL_SLPCTLEN (1 << 6) 1381c888e2eSJarkko Nikula #define GPIO1_DIR_INPUT (1 << 0) 1391c888e2eSJarkko Nikula #define GPIO2_DIR_INPUT (1 << 1) 1401c888e2eSJarkko Nikula #define GPIO3_DIR_INPUT (1 << 2) 1411c888e2eSJarkko Nikula 1421c888e2eSJarkko Nikula /* MCT_CTRL1 register */ 1431c888e2eSJarkko Nikula #define MCT_CTRL1_S1_CMD_OD (1 << 2) 1441c888e2eSJarkko Nikula #define MCT_CTRL1_S2_CMD_OD (1 << 3) 1451c888e2eSJarkko Nikula 1461c888e2eSJarkko Nikula /* MCT_CTRL2 register */ 1471c888e2eSJarkko Nikula #define MCT_CTRL2_VS2_SEL_D0 (1 << 0) 1481c888e2eSJarkko Nikula #define MCT_CTRL2_VS2_SEL_D1 (1 << 1) 1491c888e2eSJarkko Nikula #define MCT_CTRL2_S1CD_BUFEN (1 << 4) 1501c888e2eSJarkko Nikula #define MCT_CTRL2_S2CD_BUFEN (1 << 5) 1511c888e2eSJarkko Nikula #define MCT_CTRL2_S1CD_DBEN (1 << 6) 1521c888e2eSJarkko Nikula #define MCT_CTRL2_S2CD_BEN (1 << 7) 1531c888e2eSJarkko Nikula 1541c888e2eSJarkko Nikula /* MCT_CTRL3 register */ 1551c888e2eSJarkko Nikula #define MCT_CTRL3_SLOT1_EN (1 << 0) 1561c888e2eSJarkko Nikula #define MCT_CTRL3_SLOT2_EN (1 << 1) 1571c888e2eSJarkko Nikula #define MCT_CTRL3_S1_AUTO_EN (1 << 2) 1581c888e2eSJarkko Nikula #define MCT_CTRL3_S2_AUTO_EN (1 << 3) 1591c888e2eSJarkko Nikula 1601c888e2eSJarkko Nikula /* MCT_PIN_ST register */ 1611c888e2eSJarkko Nikula #define MCT_PIN_ST_S1_CD_ST (1 << 0) 1621c888e2eSJarkko Nikula #define MCT_PIN_ST_S2_CD_ST (1 << 1) 1631c888e2eSJarkko Nikula 16488e75cc3SDavid Brownell static void menelaus_work(struct work_struct *_menelaus); 16588e75cc3SDavid Brownell 16688e75cc3SDavid Brownell struct menelaus_chip { 16788e75cc3SDavid Brownell struct mutex lock; 16888e75cc3SDavid Brownell struct i2c_client *client; 16988e75cc3SDavid Brownell struct work_struct work; 17088e75cc3SDavid Brownell #ifdef CONFIG_RTC_DRV_TWL92330 17188e75cc3SDavid Brownell struct rtc_device *rtc; 17288e75cc3SDavid Brownell u8 rtc_control; 17388e75cc3SDavid Brownell unsigned uie:1; 17488e75cc3SDavid Brownell #endif 17588e75cc3SDavid Brownell unsigned vcore_hw_mode:1; 17688e75cc3SDavid Brownell u8 mask1, mask2; 17788e75cc3SDavid Brownell void (*handlers[16])(struct menelaus_chip *); 17888e75cc3SDavid Brownell void (*mmc_callback)(void *data, u8 mask); 17988e75cc3SDavid Brownell void *mmc_callback_data; 18088e75cc3SDavid Brownell }; 18188e75cc3SDavid Brownell 18288e75cc3SDavid Brownell static struct menelaus_chip *the_menelaus; 18388e75cc3SDavid Brownell 18488e75cc3SDavid Brownell static int menelaus_write_reg(int reg, u8 value) 18588e75cc3SDavid Brownell { 18688e75cc3SDavid Brownell int val = i2c_smbus_write_byte_data(the_menelaus->client, reg, value); 18788e75cc3SDavid Brownell 18888e75cc3SDavid Brownell if (val < 0) { 18988e75cc3SDavid Brownell pr_err(DRIVER_NAME ": write error"); 19088e75cc3SDavid Brownell return val; 19188e75cc3SDavid Brownell } 19288e75cc3SDavid Brownell 19388e75cc3SDavid Brownell return 0; 19488e75cc3SDavid Brownell } 19588e75cc3SDavid Brownell 19688e75cc3SDavid Brownell static int menelaus_read_reg(int reg) 19788e75cc3SDavid Brownell { 19888e75cc3SDavid Brownell int val = i2c_smbus_read_byte_data(the_menelaus->client, reg); 19988e75cc3SDavid Brownell 20088e75cc3SDavid Brownell if (val < 0) 20188e75cc3SDavid Brownell pr_err(DRIVER_NAME ": read error"); 20288e75cc3SDavid Brownell 20388e75cc3SDavid Brownell return val; 20488e75cc3SDavid Brownell } 20588e75cc3SDavid Brownell 20688e75cc3SDavid Brownell static int menelaus_enable_irq(int irq) 20788e75cc3SDavid Brownell { 20888e75cc3SDavid Brownell if (irq > 7) { 20988e75cc3SDavid Brownell irq -= 8; 21088e75cc3SDavid Brownell the_menelaus->mask2 &= ~(1 << irq); 21188e75cc3SDavid Brownell return menelaus_write_reg(MENELAUS_INT_MASK2, 21288e75cc3SDavid Brownell the_menelaus->mask2); 21388e75cc3SDavid Brownell } else { 21488e75cc3SDavid Brownell the_menelaus->mask1 &= ~(1 << irq); 21588e75cc3SDavid Brownell return menelaus_write_reg(MENELAUS_INT_MASK1, 21688e75cc3SDavid Brownell the_menelaus->mask1); 21788e75cc3SDavid Brownell } 21888e75cc3SDavid Brownell } 21988e75cc3SDavid Brownell 22088e75cc3SDavid Brownell static int menelaus_disable_irq(int irq) 22188e75cc3SDavid Brownell { 22288e75cc3SDavid Brownell if (irq > 7) { 22388e75cc3SDavid Brownell irq -= 8; 22488e75cc3SDavid Brownell the_menelaus->mask2 |= (1 << irq); 22588e75cc3SDavid Brownell return menelaus_write_reg(MENELAUS_INT_MASK2, 22688e75cc3SDavid Brownell the_menelaus->mask2); 22788e75cc3SDavid Brownell } else { 22888e75cc3SDavid Brownell the_menelaus->mask1 |= (1 << irq); 22988e75cc3SDavid Brownell return menelaus_write_reg(MENELAUS_INT_MASK1, 23088e75cc3SDavid Brownell the_menelaus->mask1); 23188e75cc3SDavid Brownell } 23288e75cc3SDavid Brownell } 23388e75cc3SDavid Brownell 23488e75cc3SDavid Brownell static int menelaus_ack_irq(int irq) 23588e75cc3SDavid Brownell { 23688e75cc3SDavid Brownell if (irq > 7) 23788e75cc3SDavid Brownell return menelaus_write_reg(MENELAUS_INT_ACK2, 1 << (irq - 8)); 23888e75cc3SDavid Brownell else 23988e75cc3SDavid Brownell return menelaus_write_reg(MENELAUS_INT_ACK1, 1 << irq); 24088e75cc3SDavid Brownell } 24188e75cc3SDavid Brownell 24288e75cc3SDavid Brownell /* Adds a handler for an interrupt. Does not run in interrupt context */ 24388e75cc3SDavid Brownell static int menelaus_add_irq_work(int irq, 24488e75cc3SDavid Brownell void (*handler)(struct menelaus_chip *)) 24588e75cc3SDavid Brownell { 24688e75cc3SDavid Brownell int ret = 0; 24788e75cc3SDavid Brownell 24888e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 24988e75cc3SDavid Brownell the_menelaus->handlers[irq] = handler; 25088e75cc3SDavid Brownell ret = menelaus_enable_irq(irq); 25188e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 25288e75cc3SDavid Brownell 25388e75cc3SDavid Brownell return ret; 25488e75cc3SDavid Brownell } 25588e75cc3SDavid Brownell 25688e75cc3SDavid Brownell /* Removes handler for an interrupt */ 25788e75cc3SDavid Brownell static int menelaus_remove_irq_work(int irq) 25888e75cc3SDavid Brownell { 25988e75cc3SDavid Brownell int ret = 0; 26088e75cc3SDavid Brownell 26188e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 26288e75cc3SDavid Brownell ret = menelaus_disable_irq(irq); 26388e75cc3SDavid Brownell the_menelaus->handlers[irq] = NULL; 26488e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 26588e75cc3SDavid Brownell 26688e75cc3SDavid Brownell return ret; 26788e75cc3SDavid Brownell } 26888e75cc3SDavid Brownell 26988e75cc3SDavid Brownell /* 27088e75cc3SDavid Brownell * Gets scheduled when a card detect interrupt happens. Note that in some cases 27188e75cc3SDavid Brownell * this line is wired to card cover switch rather than the card detect switch 27288e75cc3SDavid Brownell * in each slot. In this case the cards are not seen by menelaus. 27388e75cc3SDavid Brownell * FIXME: Add handling for D1 too 27488e75cc3SDavid Brownell */ 27588e75cc3SDavid Brownell static void menelaus_mmc_cd_work(struct menelaus_chip *menelaus_hw) 27688e75cc3SDavid Brownell { 27788e75cc3SDavid Brownell int reg; 27888e75cc3SDavid Brownell unsigned char card_mask = 0; 27988e75cc3SDavid Brownell 28088e75cc3SDavid Brownell reg = menelaus_read_reg(MENELAUS_MCT_PIN_ST); 28188e75cc3SDavid Brownell if (reg < 0) 28288e75cc3SDavid Brownell return; 28388e75cc3SDavid Brownell 28488e75cc3SDavid Brownell if (!(reg & 0x1)) 2851c888e2eSJarkko Nikula card_mask |= MCT_PIN_ST_S1_CD_ST; 28688e75cc3SDavid Brownell 28788e75cc3SDavid Brownell if (!(reg & 0x2)) 2881c888e2eSJarkko Nikula card_mask |= MCT_PIN_ST_S2_CD_ST; 28988e75cc3SDavid Brownell 29088e75cc3SDavid Brownell if (menelaus_hw->mmc_callback) 29188e75cc3SDavid Brownell menelaus_hw->mmc_callback(menelaus_hw->mmc_callback_data, 29288e75cc3SDavid Brownell card_mask); 29388e75cc3SDavid Brownell } 29488e75cc3SDavid Brownell 29588e75cc3SDavid Brownell /* 29688e75cc3SDavid Brownell * Toggles the MMC slots between open-drain and push-pull mode. 29788e75cc3SDavid Brownell */ 29888e75cc3SDavid Brownell int menelaus_set_mmc_opendrain(int slot, int enable) 29988e75cc3SDavid Brownell { 30088e75cc3SDavid Brownell int ret, val; 30188e75cc3SDavid Brownell 30288e75cc3SDavid Brownell if (slot != 1 && slot != 2) 30388e75cc3SDavid Brownell return -EINVAL; 30488e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 30588e75cc3SDavid Brownell ret = menelaus_read_reg(MENELAUS_MCT_CTRL1); 30688e75cc3SDavid Brownell if (ret < 0) { 30788e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 30888e75cc3SDavid Brownell return ret; 30988e75cc3SDavid Brownell } 31088e75cc3SDavid Brownell val = ret; 31188e75cc3SDavid Brownell if (slot == 1) { 31288e75cc3SDavid Brownell if (enable) 3131c888e2eSJarkko Nikula val |= MCT_CTRL1_S1_CMD_OD; 31488e75cc3SDavid Brownell else 3151c888e2eSJarkko Nikula val &= ~MCT_CTRL1_S1_CMD_OD; 31688e75cc3SDavid Brownell } else { 31788e75cc3SDavid Brownell if (enable) 3181c888e2eSJarkko Nikula val |= MCT_CTRL1_S2_CMD_OD; 31988e75cc3SDavid Brownell else 3201c888e2eSJarkko Nikula val &= ~MCT_CTRL1_S2_CMD_OD; 32188e75cc3SDavid Brownell } 32288e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_MCT_CTRL1, val); 32388e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 32488e75cc3SDavid Brownell 32588e75cc3SDavid Brownell return ret; 32688e75cc3SDavid Brownell } 32788e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_mmc_opendrain); 32888e75cc3SDavid Brownell 32988e75cc3SDavid Brownell int menelaus_set_slot_sel(int enable) 33088e75cc3SDavid Brownell { 33188e75cc3SDavid Brownell int ret; 33288e75cc3SDavid Brownell 33388e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 33488e75cc3SDavid Brownell ret = menelaus_read_reg(MENELAUS_GPIO_CTRL); 33588e75cc3SDavid Brownell if (ret < 0) 33688e75cc3SDavid Brownell goto out; 3371c888e2eSJarkko Nikula ret |= GPIO2_DIR_INPUT; 33888e75cc3SDavid Brownell if (enable) 3391c888e2eSJarkko Nikula ret |= GPIO_CTRL_SLOTSELEN; 34088e75cc3SDavid Brownell else 3411c888e2eSJarkko Nikula ret &= ~GPIO_CTRL_SLOTSELEN; 34288e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret); 34388e75cc3SDavid Brownell out: 34488e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 34588e75cc3SDavid Brownell return ret; 34688e75cc3SDavid Brownell } 34788e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_slot_sel); 34888e75cc3SDavid Brownell 34988e75cc3SDavid Brownell int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_en) 35088e75cc3SDavid Brownell { 35188e75cc3SDavid Brownell int ret, val; 35288e75cc3SDavid Brownell 35388e75cc3SDavid Brownell if (slot != 1 && slot != 2) 35488e75cc3SDavid Brownell return -EINVAL; 35588e75cc3SDavid Brownell if (power >= 3) 35688e75cc3SDavid Brownell return -EINVAL; 35788e75cc3SDavid Brownell 35888e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 35988e75cc3SDavid Brownell 36088e75cc3SDavid Brownell ret = menelaus_read_reg(MENELAUS_MCT_CTRL2); 36188e75cc3SDavid Brownell if (ret < 0) 36288e75cc3SDavid Brownell goto out; 36388e75cc3SDavid Brownell val = ret; 36488e75cc3SDavid Brownell if (slot == 1) { 36588e75cc3SDavid Brownell if (cd_en) 3661c888e2eSJarkko Nikula val |= MCT_CTRL2_S1CD_BUFEN | MCT_CTRL2_S1CD_DBEN; 36788e75cc3SDavid Brownell else 3681c888e2eSJarkko Nikula val &= ~(MCT_CTRL2_S1CD_BUFEN | MCT_CTRL2_S1CD_DBEN); 36988e75cc3SDavid Brownell } else { 37088e75cc3SDavid Brownell if (cd_en) 3711c888e2eSJarkko Nikula val |= MCT_CTRL2_S2CD_BUFEN | MCT_CTRL2_S2CD_BEN; 37288e75cc3SDavid Brownell else 3731c888e2eSJarkko Nikula val &= ~(MCT_CTRL2_S2CD_BUFEN | MCT_CTRL2_S2CD_BEN); 37488e75cc3SDavid Brownell } 37588e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, val); 37688e75cc3SDavid Brownell if (ret < 0) 37788e75cc3SDavid Brownell goto out; 37888e75cc3SDavid Brownell 37988e75cc3SDavid Brownell ret = menelaus_read_reg(MENELAUS_MCT_CTRL3); 38088e75cc3SDavid Brownell if (ret < 0) 38188e75cc3SDavid Brownell goto out; 38288e75cc3SDavid Brownell val = ret; 38388e75cc3SDavid Brownell if (slot == 1) { 38488e75cc3SDavid Brownell if (enable) 3851c888e2eSJarkko Nikula val |= MCT_CTRL3_SLOT1_EN; 38688e75cc3SDavid Brownell else 3871c888e2eSJarkko Nikula val &= ~MCT_CTRL3_SLOT1_EN; 38888e75cc3SDavid Brownell } else { 38988e75cc3SDavid Brownell int b; 39088e75cc3SDavid Brownell 39188e75cc3SDavid Brownell if (enable) 3921c888e2eSJarkko Nikula val |= MCT_CTRL3_SLOT2_EN; 39388e75cc3SDavid Brownell else 3941c888e2eSJarkko Nikula val &= ~MCT_CTRL3_SLOT2_EN; 39588e75cc3SDavid Brownell b = menelaus_read_reg(MENELAUS_MCT_CTRL2); 3961c888e2eSJarkko Nikula b &= ~(MCT_CTRL2_VS2_SEL_D0 | MCT_CTRL2_VS2_SEL_D1); 39788e75cc3SDavid Brownell b |= power; 39888e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, b); 39988e75cc3SDavid Brownell if (ret < 0) 40088e75cc3SDavid Brownell goto out; 40188e75cc3SDavid Brownell } 40288e75cc3SDavid Brownell /* Disable autonomous shutdown */ 4031c888e2eSJarkko Nikula val &= ~(MCT_CTRL3_S1_AUTO_EN | MCT_CTRL3_S2_AUTO_EN); 40488e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_MCT_CTRL3, val); 40588e75cc3SDavid Brownell out: 40688e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 40788e75cc3SDavid Brownell return ret; 40888e75cc3SDavid Brownell } 40988e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_mmc_slot); 41088e75cc3SDavid Brownell 41188e75cc3SDavid Brownell int menelaus_register_mmc_callback(void (*callback)(void *data, u8 card_mask), 41288e75cc3SDavid Brownell void *data) 41388e75cc3SDavid Brownell { 41488e75cc3SDavid Brownell int ret = 0; 41588e75cc3SDavid Brownell 41688e75cc3SDavid Brownell the_menelaus->mmc_callback_data = data; 41788e75cc3SDavid Brownell the_menelaus->mmc_callback = callback; 41888e75cc3SDavid Brownell ret = menelaus_add_irq_work(MENELAUS_MMC_S1CD_IRQ, 41988e75cc3SDavid Brownell menelaus_mmc_cd_work); 42088e75cc3SDavid Brownell if (ret < 0) 42188e75cc3SDavid Brownell return ret; 42288e75cc3SDavid Brownell ret = menelaus_add_irq_work(MENELAUS_MMC_S2CD_IRQ, 42388e75cc3SDavid Brownell menelaus_mmc_cd_work); 42488e75cc3SDavid Brownell if (ret < 0) 42588e75cc3SDavid Brownell return ret; 42688e75cc3SDavid Brownell ret = menelaus_add_irq_work(MENELAUS_MMC_S1D1_IRQ, 42788e75cc3SDavid Brownell menelaus_mmc_cd_work); 42888e75cc3SDavid Brownell if (ret < 0) 42988e75cc3SDavid Brownell return ret; 43088e75cc3SDavid Brownell ret = menelaus_add_irq_work(MENELAUS_MMC_S2D1_IRQ, 43188e75cc3SDavid Brownell menelaus_mmc_cd_work); 43288e75cc3SDavid Brownell 43388e75cc3SDavid Brownell return ret; 43488e75cc3SDavid Brownell } 43588e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_register_mmc_callback); 43688e75cc3SDavid Brownell 43788e75cc3SDavid Brownell void menelaus_unregister_mmc_callback(void) 43888e75cc3SDavid Brownell { 43988e75cc3SDavid Brownell menelaus_remove_irq_work(MENELAUS_MMC_S1CD_IRQ); 44088e75cc3SDavid Brownell menelaus_remove_irq_work(MENELAUS_MMC_S2CD_IRQ); 44188e75cc3SDavid Brownell menelaus_remove_irq_work(MENELAUS_MMC_S1D1_IRQ); 44288e75cc3SDavid Brownell menelaus_remove_irq_work(MENELAUS_MMC_S2D1_IRQ); 44388e75cc3SDavid Brownell 44488e75cc3SDavid Brownell the_menelaus->mmc_callback = NULL; 44559a9f7a3SJingoo Han the_menelaus->mmc_callback_data = NULL; 44688e75cc3SDavid Brownell } 44788e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_unregister_mmc_callback); 44888e75cc3SDavid Brownell 44988e75cc3SDavid Brownell struct menelaus_vtg { 45088e75cc3SDavid Brownell const char *name; 45188e75cc3SDavid Brownell u8 vtg_reg; 45288e75cc3SDavid Brownell u8 vtg_shift; 45388e75cc3SDavid Brownell u8 vtg_bits; 45488e75cc3SDavid Brownell u8 mode_reg; 45588e75cc3SDavid Brownell }; 45688e75cc3SDavid Brownell 45788e75cc3SDavid Brownell struct menelaus_vtg_value { 45888e75cc3SDavid Brownell u16 vtg; 45988e75cc3SDavid Brownell u16 val; 46088e75cc3SDavid Brownell }; 46188e75cc3SDavid Brownell 46288e75cc3SDavid Brownell static int menelaus_set_voltage(const struct menelaus_vtg *vtg, int mV, 46388e75cc3SDavid Brownell int vtg_val, int mode) 46488e75cc3SDavid Brownell { 46588e75cc3SDavid Brownell int val, ret; 46688e75cc3SDavid Brownell struct i2c_client *c = the_menelaus->client; 46788e75cc3SDavid Brownell 46888e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 46988e75cc3SDavid Brownell 47088e75cc3SDavid Brownell ret = menelaus_read_reg(vtg->vtg_reg); 47188e75cc3SDavid Brownell if (ret < 0) 47288e75cc3SDavid Brownell goto out; 47388e75cc3SDavid Brownell val = ret & ~(((1 << vtg->vtg_bits) - 1) << vtg->vtg_shift); 47488e75cc3SDavid Brownell val |= vtg_val << vtg->vtg_shift; 47588e75cc3SDavid Brownell 47688e75cc3SDavid Brownell dev_dbg(&c->dev, "Setting voltage '%s'" 47788e75cc3SDavid Brownell "to %d mV (reg 0x%02x, val 0x%02x)\n", 47888e75cc3SDavid Brownell vtg->name, mV, vtg->vtg_reg, val); 47988e75cc3SDavid Brownell 48088e75cc3SDavid Brownell ret = menelaus_write_reg(vtg->vtg_reg, val); 48188e75cc3SDavid Brownell if (ret < 0) 48288e75cc3SDavid Brownell goto out; 48388e75cc3SDavid Brownell ret = menelaus_write_reg(vtg->mode_reg, mode); 48488e75cc3SDavid Brownell out: 48588e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 48688e75cc3SDavid Brownell if (ret == 0) { 48788e75cc3SDavid Brownell /* Wait for voltage to stabilize */ 48888e75cc3SDavid Brownell msleep(1); 48988e75cc3SDavid Brownell } 49088e75cc3SDavid Brownell return ret; 49188e75cc3SDavid Brownell } 49288e75cc3SDavid Brownell 49388e75cc3SDavid Brownell static int menelaus_get_vtg_value(int vtg, const struct menelaus_vtg_value *tbl, 49488e75cc3SDavid Brownell int n) 49588e75cc3SDavid Brownell { 49688e75cc3SDavid Brownell int i; 49788e75cc3SDavid Brownell 49888e75cc3SDavid Brownell for (i = 0; i < n; i++, tbl++) 49988e75cc3SDavid Brownell if (tbl->vtg == vtg) 50088e75cc3SDavid Brownell return tbl->val; 50188e75cc3SDavid Brownell return -EINVAL; 50288e75cc3SDavid Brownell } 50388e75cc3SDavid Brownell 50488e75cc3SDavid Brownell /* 50588e75cc3SDavid Brownell * Vcore can be programmed in two ways: 50688e75cc3SDavid Brownell * SW-controlled: Required voltage is programmed into VCORE_CTRL1 50788e75cc3SDavid Brownell * HW-controlled: Required range (roof-floor) is programmed into VCORE_CTRL3 50888e75cc3SDavid Brownell * and VCORE_CTRL4 50988e75cc3SDavid Brownell * 51088e75cc3SDavid Brownell * Call correct 'set' function accordingly 51188e75cc3SDavid Brownell */ 51288e75cc3SDavid Brownell 51388e75cc3SDavid Brownell static const struct menelaus_vtg_value vcore_values[] = { 51488e75cc3SDavid Brownell { 1000, 0 }, 51588e75cc3SDavid Brownell { 1025, 1 }, 51688e75cc3SDavid Brownell { 1050, 2 }, 51788e75cc3SDavid Brownell { 1075, 3 }, 51888e75cc3SDavid Brownell { 1100, 4 }, 51988e75cc3SDavid Brownell { 1125, 5 }, 52088e75cc3SDavid Brownell { 1150, 6 }, 52188e75cc3SDavid Brownell { 1175, 7 }, 52288e75cc3SDavid Brownell { 1200, 8 }, 52388e75cc3SDavid Brownell { 1225, 9 }, 52488e75cc3SDavid Brownell { 1250, 10 }, 52588e75cc3SDavid Brownell { 1275, 11 }, 52688e75cc3SDavid Brownell { 1300, 12 }, 52788e75cc3SDavid Brownell { 1325, 13 }, 52888e75cc3SDavid Brownell { 1350, 14 }, 52988e75cc3SDavid Brownell { 1375, 15 }, 53088e75cc3SDavid Brownell { 1400, 16 }, 53188e75cc3SDavid Brownell { 1425, 17 }, 53288e75cc3SDavid Brownell { 1450, 18 }, 53388e75cc3SDavid Brownell }; 53488e75cc3SDavid Brownell 53588e75cc3SDavid Brownell int menelaus_set_vcore_sw(unsigned int mV) 53688e75cc3SDavid Brownell { 53788e75cc3SDavid Brownell int val, ret; 53888e75cc3SDavid Brownell struct i2c_client *c = the_menelaus->client; 53988e75cc3SDavid Brownell 54088e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vcore_values, 54188e75cc3SDavid Brownell ARRAY_SIZE(vcore_values)); 54288e75cc3SDavid Brownell if (val < 0) 54388e75cc3SDavid Brownell return -EINVAL; 54488e75cc3SDavid Brownell 54588e75cc3SDavid Brownell dev_dbg(&c->dev, "Setting VCORE to %d mV (val 0x%02x)\n", mV, val); 54688e75cc3SDavid Brownell 54788e75cc3SDavid Brownell /* Set SW mode and the voltage in one go. */ 54888e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 54988e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val); 55088e75cc3SDavid Brownell if (ret == 0) 55188e75cc3SDavid Brownell the_menelaus->vcore_hw_mode = 0; 55288e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 55388e75cc3SDavid Brownell msleep(1); 55488e75cc3SDavid Brownell 55588e75cc3SDavid Brownell return ret; 55688e75cc3SDavid Brownell } 55788e75cc3SDavid Brownell 55888e75cc3SDavid Brownell int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV) 55988e75cc3SDavid Brownell { 56088e75cc3SDavid Brownell int fval, rval, val, ret; 56188e75cc3SDavid Brownell struct i2c_client *c = the_menelaus->client; 56288e75cc3SDavid Brownell 56388e75cc3SDavid Brownell rval = menelaus_get_vtg_value(roof_mV, vcore_values, 56488e75cc3SDavid Brownell ARRAY_SIZE(vcore_values)); 56588e75cc3SDavid Brownell if (rval < 0) 56688e75cc3SDavid Brownell return -EINVAL; 56788e75cc3SDavid Brownell fval = menelaus_get_vtg_value(floor_mV, vcore_values, 56888e75cc3SDavid Brownell ARRAY_SIZE(vcore_values)); 56988e75cc3SDavid Brownell if (fval < 0) 57088e75cc3SDavid Brownell return -EINVAL; 57188e75cc3SDavid Brownell 57288e75cc3SDavid Brownell dev_dbg(&c->dev, "Setting VCORE FLOOR to %d mV and ROOF to %d mV\n", 57388e75cc3SDavid Brownell floor_mV, roof_mV); 57488e75cc3SDavid Brownell 57588e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 57688e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_VCORE_CTRL3, fval); 57788e75cc3SDavid Brownell if (ret < 0) 57888e75cc3SDavid Brownell goto out; 57988e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_VCORE_CTRL4, rval); 58088e75cc3SDavid Brownell if (ret < 0) 58188e75cc3SDavid Brownell goto out; 58288e75cc3SDavid Brownell if (!the_menelaus->vcore_hw_mode) { 58388e75cc3SDavid Brownell val = menelaus_read_reg(MENELAUS_VCORE_CTRL1); 58488e75cc3SDavid Brownell /* HW mode, turn OFF byte comparator */ 5851c888e2eSJarkko Nikula val |= (VCORE_CTRL1_HW_NSW | VCORE_CTRL1_BYP_COMP); 58688e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val); 58788e75cc3SDavid Brownell the_menelaus->vcore_hw_mode = 1; 58888e75cc3SDavid Brownell } 58988e75cc3SDavid Brownell msleep(1); 59088e75cc3SDavid Brownell out: 59188e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 59288e75cc3SDavid Brownell return ret; 59388e75cc3SDavid Brownell } 59488e75cc3SDavid Brownell 59588e75cc3SDavid Brownell static const struct menelaus_vtg vmem_vtg = { 59688e75cc3SDavid Brownell .name = "VMEM", 59788e75cc3SDavid Brownell .vtg_reg = MENELAUS_LDO_CTRL1, 59888e75cc3SDavid Brownell .vtg_shift = 0, 59988e75cc3SDavid Brownell .vtg_bits = 2, 60088e75cc3SDavid Brownell .mode_reg = MENELAUS_LDO_CTRL3, 60188e75cc3SDavid Brownell }; 60288e75cc3SDavid Brownell 60388e75cc3SDavid Brownell static const struct menelaus_vtg_value vmem_values[] = { 60488e75cc3SDavid Brownell { 1500, 0 }, 60588e75cc3SDavid Brownell { 1800, 1 }, 60688e75cc3SDavid Brownell { 1900, 2 }, 60788e75cc3SDavid Brownell { 2500, 3 }, 60888e75cc3SDavid Brownell }; 60988e75cc3SDavid Brownell 61088e75cc3SDavid Brownell int menelaus_set_vmem(unsigned int mV) 61188e75cc3SDavid Brownell { 61288e75cc3SDavid Brownell int val; 61388e75cc3SDavid Brownell 61488e75cc3SDavid Brownell if (mV == 0) 61588e75cc3SDavid Brownell return menelaus_set_voltage(&vmem_vtg, 0, 0, 0); 61688e75cc3SDavid Brownell 61788e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vmem_values, ARRAY_SIZE(vmem_values)); 61888e75cc3SDavid Brownell if (val < 0) 61988e75cc3SDavid Brownell return -EINVAL; 62088e75cc3SDavid Brownell return menelaus_set_voltage(&vmem_vtg, mV, val, 0x02); 62188e75cc3SDavid Brownell } 62288e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_vmem); 62388e75cc3SDavid Brownell 62488e75cc3SDavid Brownell static const struct menelaus_vtg vio_vtg = { 62588e75cc3SDavid Brownell .name = "VIO", 62688e75cc3SDavid Brownell .vtg_reg = MENELAUS_LDO_CTRL1, 62788e75cc3SDavid Brownell .vtg_shift = 2, 62888e75cc3SDavid Brownell .vtg_bits = 2, 62988e75cc3SDavid Brownell .mode_reg = MENELAUS_LDO_CTRL4, 63088e75cc3SDavid Brownell }; 63188e75cc3SDavid Brownell 63288e75cc3SDavid Brownell static const struct menelaus_vtg_value vio_values[] = { 63388e75cc3SDavid Brownell { 1500, 0 }, 63488e75cc3SDavid Brownell { 1800, 1 }, 63588e75cc3SDavid Brownell { 2500, 2 }, 63688e75cc3SDavid Brownell { 2800, 3 }, 63788e75cc3SDavid Brownell }; 63888e75cc3SDavid Brownell 63988e75cc3SDavid Brownell int menelaus_set_vio(unsigned int mV) 64088e75cc3SDavid Brownell { 64188e75cc3SDavid Brownell int val; 64288e75cc3SDavid Brownell 64388e75cc3SDavid Brownell if (mV == 0) 64488e75cc3SDavid Brownell return menelaus_set_voltage(&vio_vtg, 0, 0, 0); 64588e75cc3SDavid Brownell 64688e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vio_values, ARRAY_SIZE(vio_values)); 64788e75cc3SDavid Brownell if (val < 0) 64888e75cc3SDavid Brownell return -EINVAL; 64988e75cc3SDavid Brownell return menelaus_set_voltage(&vio_vtg, mV, val, 0x02); 65088e75cc3SDavid Brownell } 65188e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_vio); 65288e75cc3SDavid Brownell 65388e75cc3SDavid Brownell static const struct menelaus_vtg_value vdcdc_values[] = { 65488e75cc3SDavid Brownell { 1500, 0 }, 65588e75cc3SDavid Brownell { 1800, 1 }, 65688e75cc3SDavid Brownell { 2000, 2 }, 65788e75cc3SDavid Brownell { 2200, 3 }, 65888e75cc3SDavid Brownell { 2400, 4 }, 65988e75cc3SDavid Brownell { 2800, 5 }, 66088e75cc3SDavid Brownell { 3000, 6 }, 66188e75cc3SDavid Brownell { 3300, 7 }, 66288e75cc3SDavid Brownell }; 66388e75cc3SDavid Brownell 66488e75cc3SDavid Brownell static const struct menelaus_vtg vdcdc2_vtg = { 66588e75cc3SDavid Brownell .name = "VDCDC2", 66688e75cc3SDavid Brownell .vtg_reg = MENELAUS_DCDC_CTRL1, 66788e75cc3SDavid Brownell .vtg_shift = 0, 66888e75cc3SDavid Brownell .vtg_bits = 3, 66988e75cc3SDavid Brownell .mode_reg = MENELAUS_DCDC_CTRL2, 67088e75cc3SDavid Brownell }; 67188e75cc3SDavid Brownell 67288e75cc3SDavid Brownell static const struct menelaus_vtg vdcdc3_vtg = { 67388e75cc3SDavid Brownell .name = "VDCDC3", 67488e75cc3SDavid Brownell .vtg_reg = MENELAUS_DCDC_CTRL1, 67588e75cc3SDavid Brownell .vtg_shift = 3, 67688e75cc3SDavid Brownell .vtg_bits = 3, 67788e75cc3SDavid Brownell .mode_reg = MENELAUS_DCDC_CTRL3, 67888e75cc3SDavid Brownell }; 67988e75cc3SDavid Brownell 68088e75cc3SDavid Brownell int menelaus_set_vdcdc(int dcdc, unsigned int mV) 68188e75cc3SDavid Brownell { 68288e75cc3SDavid Brownell const struct menelaus_vtg *vtg; 68388e75cc3SDavid Brownell int val; 68488e75cc3SDavid Brownell 68588e75cc3SDavid Brownell if (dcdc != 2 && dcdc != 3) 68688e75cc3SDavid Brownell return -EINVAL; 68788e75cc3SDavid Brownell if (dcdc == 2) 68888e75cc3SDavid Brownell vtg = &vdcdc2_vtg; 68988e75cc3SDavid Brownell else 69088e75cc3SDavid Brownell vtg = &vdcdc3_vtg; 69188e75cc3SDavid Brownell 69288e75cc3SDavid Brownell if (mV == 0) 69388e75cc3SDavid Brownell return menelaus_set_voltage(vtg, 0, 0, 0); 69488e75cc3SDavid Brownell 69588e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vdcdc_values, 69688e75cc3SDavid Brownell ARRAY_SIZE(vdcdc_values)); 69788e75cc3SDavid Brownell if (val < 0) 69888e75cc3SDavid Brownell return -EINVAL; 69988e75cc3SDavid Brownell return menelaus_set_voltage(vtg, mV, val, 0x03); 70088e75cc3SDavid Brownell } 70188e75cc3SDavid Brownell 70288e75cc3SDavid Brownell static const struct menelaus_vtg_value vmmc_values[] = { 70388e75cc3SDavid Brownell { 1850, 0 }, 70488e75cc3SDavid Brownell { 2800, 1 }, 70588e75cc3SDavid Brownell { 3000, 2 }, 70688e75cc3SDavid Brownell { 3100, 3 }, 70788e75cc3SDavid Brownell }; 70888e75cc3SDavid Brownell 70988e75cc3SDavid Brownell static const struct menelaus_vtg vmmc_vtg = { 71088e75cc3SDavid Brownell .name = "VMMC", 71188e75cc3SDavid Brownell .vtg_reg = MENELAUS_LDO_CTRL1, 71288e75cc3SDavid Brownell .vtg_shift = 6, 71388e75cc3SDavid Brownell .vtg_bits = 2, 71488e75cc3SDavid Brownell .mode_reg = MENELAUS_LDO_CTRL7, 71588e75cc3SDavid Brownell }; 71688e75cc3SDavid Brownell 71788e75cc3SDavid Brownell int menelaus_set_vmmc(unsigned int mV) 71888e75cc3SDavid Brownell { 71988e75cc3SDavid Brownell int val; 72088e75cc3SDavid Brownell 72188e75cc3SDavid Brownell if (mV == 0) 72288e75cc3SDavid Brownell return menelaus_set_voltage(&vmmc_vtg, 0, 0, 0); 72388e75cc3SDavid Brownell 72488e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vmmc_values, ARRAY_SIZE(vmmc_values)); 72588e75cc3SDavid Brownell if (val < 0) 72688e75cc3SDavid Brownell return -EINVAL; 72788e75cc3SDavid Brownell return menelaus_set_voltage(&vmmc_vtg, mV, val, 0x02); 72888e75cc3SDavid Brownell } 72988e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_vmmc); 73088e75cc3SDavid Brownell 73188e75cc3SDavid Brownell 73288e75cc3SDavid Brownell static const struct menelaus_vtg_value vaux_values[] = { 73388e75cc3SDavid Brownell { 1500, 0 }, 73488e75cc3SDavid Brownell { 1800, 1 }, 73588e75cc3SDavid Brownell { 2500, 2 }, 73688e75cc3SDavid Brownell { 2800, 3 }, 73788e75cc3SDavid Brownell }; 73888e75cc3SDavid Brownell 73988e75cc3SDavid Brownell static const struct menelaus_vtg vaux_vtg = { 74088e75cc3SDavid Brownell .name = "VAUX", 74188e75cc3SDavid Brownell .vtg_reg = MENELAUS_LDO_CTRL1, 74288e75cc3SDavid Brownell .vtg_shift = 4, 74388e75cc3SDavid Brownell .vtg_bits = 2, 74488e75cc3SDavid Brownell .mode_reg = MENELAUS_LDO_CTRL6, 74588e75cc3SDavid Brownell }; 74688e75cc3SDavid Brownell 74788e75cc3SDavid Brownell int menelaus_set_vaux(unsigned int mV) 74888e75cc3SDavid Brownell { 74988e75cc3SDavid Brownell int val; 75088e75cc3SDavid Brownell 75188e75cc3SDavid Brownell if (mV == 0) 75288e75cc3SDavid Brownell return menelaus_set_voltage(&vaux_vtg, 0, 0, 0); 75388e75cc3SDavid Brownell 75488e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vaux_values, ARRAY_SIZE(vaux_values)); 75588e75cc3SDavid Brownell if (val < 0) 75688e75cc3SDavid Brownell return -EINVAL; 75788e75cc3SDavid Brownell return menelaus_set_voltage(&vaux_vtg, mV, val, 0x02); 75888e75cc3SDavid Brownell } 75988e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_vaux); 76088e75cc3SDavid Brownell 76188e75cc3SDavid Brownell int menelaus_get_slot_pin_states(void) 76288e75cc3SDavid Brownell { 76388e75cc3SDavid Brownell return menelaus_read_reg(MENELAUS_MCT_PIN_ST); 76488e75cc3SDavid Brownell } 76588e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_get_slot_pin_states); 76688e75cc3SDavid Brownell 76788e75cc3SDavid Brownell int menelaus_set_regulator_sleep(int enable, u32 val) 76888e75cc3SDavid Brownell { 76988e75cc3SDavid Brownell int t, ret; 77088e75cc3SDavid Brownell struct i2c_client *c = the_menelaus->client; 77188e75cc3SDavid Brownell 77288e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 77388e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_SLEEP_CTRL2, val); 77488e75cc3SDavid Brownell if (ret < 0) 77588e75cc3SDavid Brownell goto out; 77688e75cc3SDavid Brownell 77788e75cc3SDavid Brownell dev_dbg(&c->dev, "regulator sleep configuration: %02x\n", val); 77888e75cc3SDavid Brownell 77988e75cc3SDavid Brownell ret = menelaus_read_reg(MENELAUS_GPIO_CTRL); 78088e75cc3SDavid Brownell if (ret < 0) 78188e75cc3SDavid Brownell goto out; 7821c888e2eSJarkko Nikula t = (GPIO_CTRL_SLPCTLEN | GPIO3_DIR_INPUT); 78388e75cc3SDavid Brownell if (enable) 78488e75cc3SDavid Brownell ret |= t; 78588e75cc3SDavid Brownell else 78688e75cc3SDavid Brownell ret &= ~t; 78788e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret); 78888e75cc3SDavid Brownell out: 78988e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 79088e75cc3SDavid Brownell return ret; 79188e75cc3SDavid Brownell } 79288e75cc3SDavid Brownell 79388e75cc3SDavid Brownell /*-----------------------------------------------------------------------*/ 79488e75cc3SDavid Brownell 79588e75cc3SDavid Brownell /* Handles Menelaus interrupts. Does not run in interrupt context */ 79688e75cc3SDavid Brownell static void menelaus_work(struct work_struct *_menelaus) 79788e75cc3SDavid Brownell { 79888e75cc3SDavid Brownell struct menelaus_chip *menelaus = 79988e75cc3SDavid Brownell container_of(_menelaus, struct menelaus_chip, work); 80088e75cc3SDavid Brownell void (*handler)(struct menelaus_chip *menelaus); 80188e75cc3SDavid Brownell 80288e75cc3SDavid Brownell while (1) { 80388e75cc3SDavid Brownell unsigned isr; 80488e75cc3SDavid Brownell 80588e75cc3SDavid Brownell isr = (menelaus_read_reg(MENELAUS_INT_STATUS2) 80688e75cc3SDavid Brownell & ~menelaus->mask2) << 8; 80788e75cc3SDavid Brownell isr |= menelaus_read_reg(MENELAUS_INT_STATUS1) 80888e75cc3SDavid Brownell & ~menelaus->mask1; 80988e75cc3SDavid Brownell if (!isr) 81088e75cc3SDavid Brownell break; 81188e75cc3SDavid Brownell 81288e75cc3SDavid Brownell while (isr) { 81388e75cc3SDavid Brownell int irq = fls(isr) - 1; 81488e75cc3SDavid Brownell isr &= ~(1 << irq); 81588e75cc3SDavid Brownell 81688e75cc3SDavid Brownell mutex_lock(&menelaus->lock); 81788e75cc3SDavid Brownell menelaus_disable_irq(irq); 81888e75cc3SDavid Brownell menelaus_ack_irq(irq); 81988e75cc3SDavid Brownell handler = menelaus->handlers[irq]; 82088e75cc3SDavid Brownell if (handler) 82188e75cc3SDavid Brownell handler(menelaus); 82288e75cc3SDavid Brownell menelaus_enable_irq(irq); 82388e75cc3SDavid Brownell mutex_unlock(&menelaus->lock); 82488e75cc3SDavid Brownell } 82588e75cc3SDavid Brownell } 82688e75cc3SDavid Brownell enable_irq(menelaus->client->irq); 82788e75cc3SDavid Brownell } 82888e75cc3SDavid Brownell 82988e75cc3SDavid Brownell /* 83088e75cc3SDavid Brownell * We cannot use I2C in interrupt context, so we just schedule work. 83188e75cc3SDavid Brownell */ 83288e75cc3SDavid Brownell static irqreturn_t menelaus_irq(int irq, void *_menelaus) 83388e75cc3SDavid Brownell { 83488e75cc3SDavid Brownell struct menelaus_chip *menelaus = _menelaus; 83588e75cc3SDavid Brownell 83688e75cc3SDavid Brownell disable_irq_nosync(irq); 83788e75cc3SDavid Brownell (void)schedule_work(&menelaus->work); 83888e75cc3SDavid Brownell 83988e75cc3SDavid Brownell return IRQ_HANDLED; 84088e75cc3SDavid Brownell } 84188e75cc3SDavid Brownell 84288e75cc3SDavid Brownell /*-----------------------------------------------------------------------*/ 84388e75cc3SDavid Brownell 84488e75cc3SDavid Brownell /* 84588e75cc3SDavid Brownell * The RTC needs to be set once, then it runs on backup battery power. 84688e75cc3SDavid Brownell * It supports alarms, including system wake alarms (from some modes); 84788e75cc3SDavid Brownell * and 1/second IRQs if requested. 84888e75cc3SDavid Brownell */ 84988e75cc3SDavid Brownell #ifdef CONFIG_RTC_DRV_TWL92330 85088e75cc3SDavid Brownell 85188e75cc3SDavid Brownell #define RTC_CTRL_RTC_EN (1 << 0) 85288e75cc3SDavid Brownell #define RTC_CTRL_AL_EN (1 << 1) 85388e75cc3SDavid Brownell #define RTC_CTRL_MODE12 (1 << 2) 85488e75cc3SDavid Brownell #define RTC_CTRL_EVERY_MASK (3 << 3) 85588e75cc3SDavid Brownell #define RTC_CTRL_EVERY_SEC (0 << 3) 85688e75cc3SDavid Brownell #define RTC_CTRL_EVERY_MIN (1 << 3) 85788e75cc3SDavid Brownell #define RTC_CTRL_EVERY_HR (2 << 3) 85888e75cc3SDavid Brownell #define RTC_CTRL_EVERY_DAY (3 << 3) 85988e75cc3SDavid Brownell 86088e75cc3SDavid Brownell #define RTC_UPDATE_EVERY 0x08 86188e75cc3SDavid Brownell 86288e75cc3SDavid Brownell #define RTC_HR_PM (1 << 7) 86388e75cc3SDavid Brownell 86488e75cc3SDavid Brownell static void menelaus_to_time(char *regs, struct rtc_time *t) 86588e75cc3SDavid Brownell { 86688e75cc3SDavid Brownell t->tm_sec = bcd2bin(regs[0]); 86788e75cc3SDavid Brownell t->tm_min = bcd2bin(regs[1]); 86888e75cc3SDavid Brownell if (the_menelaus->rtc_control & RTC_CTRL_MODE12) { 86988e75cc3SDavid Brownell t->tm_hour = bcd2bin(regs[2] & 0x1f) - 1; 87088e75cc3SDavid Brownell if (regs[2] & RTC_HR_PM) 87188e75cc3SDavid Brownell t->tm_hour += 12; 87288e75cc3SDavid Brownell } else 87388e75cc3SDavid Brownell t->tm_hour = bcd2bin(regs[2] & 0x3f); 87488e75cc3SDavid Brownell t->tm_mday = bcd2bin(regs[3]); 87588e75cc3SDavid Brownell t->tm_mon = bcd2bin(regs[4]) - 1; 87688e75cc3SDavid Brownell t->tm_year = bcd2bin(regs[5]) + 100; 87788e75cc3SDavid Brownell } 87888e75cc3SDavid Brownell 87988e75cc3SDavid Brownell static int time_to_menelaus(struct rtc_time *t, int regnum) 88088e75cc3SDavid Brownell { 88188e75cc3SDavid Brownell int hour, status; 88288e75cc3SDavid Brownell 88388e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, bin2bcd(t->tm_sec)); 88488e75cc3SDavid Brownell if (status < 0) 88588e75cc3SDavid Brownell goto fail; 88688e75cc3SDavid Brownell 88788e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, bin2bcd(t->tm_min)); 88888e75cc3SDavid Brownell if (status < 0) 88988e75cc3SDavid Brownell goto fail; 89088e75cc3SDavid Brownell 89188e75cc3SDavid Brownell if (the_menelaus->rtc_control & RTC_CTRL_MODE12) { 89288e75cc3SDavid Brownell hour = t->tm_hour + 1; 89388e75cc3SDavid Brownell if (hour > 12) 89488e75cc3SDavid Brownell hour = RTC_HR_PM | bin2bcd(hour - 12); 89588e75cc3SDavid Brownell else 89688e75cc3SDavid Brownell hour = bin2bcd(hour); 89788e75cc3SDavid Brownell } else 89888e75cc3SDavid Brownell hour = bin2bcd(t->tm_hour); 89988e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, hour); 90088e75cc3SDavid Brownell if (status < 0) 90188e75cc3SDavid Brownell goto fail; 90288e75cc3SDavid Brownell 90388e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, bin2bcd(t->tm_mday)); 90488e75cc3SDavid Brownell if (status < 0) 90588e75cc3SDavid Brownell goto fail; 90688e75cc3SDavid Brownell 90788e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, bin2bcd(t->tm_mon + 1)); 90888e75cc3SDavid Brownell if (status < 0) 90988e75cc3SDavid Brownell goto fail; 91088e75cc3SDavid Brownell 91188e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, bin2bcd(t->tm_year - 100)); 91288e75cc3SDavid Brownell if (status < 0) 91388e75cc3SDavid Brownell goto fail; 91488e75cc3SDavid Brownell 91588e75cc3SDavid Brownell return 0; 91688e75cc3SDavid Brownell fail: 91788e75cc3SDavid Brownell dev_err(&the_menelaus->client->dev, "rtc write reg %02x, err %d\n", 91888e75cc3SDavid Brownell --regnum, status); 91988e75cc3SDavid Brownell return status; 92088e75cc3SDavid Brownell } 92188e75cc3SDavid Brownell 92288e75cc3SDavid Brownell static int menelaus_read_time(struct device *dev, struct rtc_time *t) 92388e75cc3SDavid Brownell { 92488e75cc3SDavid Brownell struct i2c_msg msg[2]; 92588e75cc3SDavid Brownell char regs[7]; 92688e75cc3SDavid Brownell int status; 92788e75cc3SDavid Brownell 92888e75cc3SDavid Brownell /* block read date and time registers */ 92988e75cc3SDavid Brownell regs[0] = MENELAUS_RTC_SEC; 93088e75cc3SDavid Brownell 93188e75cc3SDavid Brownell msg[0].addr = MENELAUS_I2C_ADDRESS; 93288e75cc3SDavid Brownell msg[0].flags = 0; 93388e75cc3SDavid Brownell msg[0].len = 1; 93488e75cc3SDavid Brownell msg[0].buf = regs; 93588e75cc3SDavid Brownell 93688e75cc3SDavid Brownell msg[1].addr = MENELAUS_I2C_ADDRESS; 93788e75cc3SDavid Brownell msg[1].flags = I2C_M_RD; 93888e75cc3SDavid Brownell msg[1].len = sizeof(regs); 93988e75cc3SDavid Brownell msg[1].buf = regs; 94088e75cc3SDavid Brownell 94188e75cc3SDavid Brownell status = i2c_transfer(the_menelaus->client->adapter, msg, 2); 94288e75cc3SDavid Brownell if (status != 2) { 94388e75cc3SDavid Brownell dev_err(dev, "%s error %d\n", "read", status); 94488e75cc3SDavid Brownell return -EIO; 94588e75cc3SDavid Brownell } 94688e75cc3SDavid Brownell 94788e75cc3SDavid Brownell menelaus_to_time(regs, t); 94888e75cc3SDavid Brownell t->tm_wday = bcd2bin(regs[6]); 94988e75cc3SDavid Brownell 95088e75cc3SDavid Brownell return 0; 95188e75cc3SDavid Brownell } 95288e75cc3SDavid Brownell 95388e75cc3SDavid Brownell static int menelaus_set_time(struct device *dev, struct rtc_time *t) 95488e75cc3SDavid Brownell { 95588e75cc3SDavid Brownell int status; 95688e75cc3SDavid Brownell 95788e75cc3SDavid Brownell /* write date and time registers */ 95888e75cc3SDavid Brownell status = time_to_menelaus(t, MENELAUS_RTC_SEC); 95988e75cc3SDavid Brownell if (status < 0) 96088e75cc3SDavid Brownell return status; 96188e75cc3SDavid Brownell status = menelaus_write_reg(MENELAUS_RTC_WKDAY, bin2bcd(t->tm_wday)); 96288e75cc3SDavid Brownell if (status < 0) { 96388e75cc3SDavid Brownell dev_err(&the_menelaus->client->dev, "rtc write reg %02x " 96488e75cc3SDavid Brownell "err %d\n", MENELAUS_RTC_WKDAY, status); 96588e75cc3SDavid Brownell return status; 96688e75cc3SDavid Brownell } 96788e75cc3SDavid Brownell 96888e75cc3SDavid Brownell /* now commit the write */ 96988e75cc3SDavid Brownell status = menelaus_write_reg(MENELAUS_RTC_UPDATE, RTC_UPDATE_EVERY); 97088e75cc3SDavid Brownell if (status < 0) 97188e75cc3SDavid Brownell dev_err(&the_menelaus->client->dev, "rtc commit time, err %d\n", 97288e75cc3SDavid Brownell status); 97388e75cc3SDavid Brownell 97488e75cc3SDavid Brownell return 0; 97588e75cc3SDavid Brownell } 97688e75cc3SDavid Brownell 97788e75cc3SDavid Brownell static int menelaus_read_alarm(struct device *dev, struct rtc_wkalrm *w) 97888e75cc3SDavid Brownell { 97988e75cc3SDavid Brownell struct i2c_msg msg[2]; 98088e75cc3SDavid Brownell char regs[6]; 98188e75cc3SDavid Brownell int status; 98288e75cc3SDavid Brownell 98388e75cc3SDavid Brownell /* block read alarm registers */ 98488e75cc3SDavid Brownell regs[0] = MENELAUS_RTC_AL_SEC; 98588e75cc3SDavid Brownell 98688e75cc3SDavid Brownell msg[0].addr = MENELAUS_I2C_ADDRESS; 98788e75cc3SDavid Brownell msg[0].flags = 0; 98888e75cc3SDavid Brownell msg[0].len = 1; 98988e75cc3SDavid Brownell msg[0].buf = regs; 99088e75cc3SDavid Brownell 99188e75cc3SDavid Brownell msg[1].addr = MENELAUS_I2C_ADDRESS; 99288e75cc3SDavid Brownell msg[1].flags = I2C_M_RD; 99388e75cc3SDavid Brownell msg[1].len = sizeof(regs); 99488e75cc3SDavid Brownell msg[1].buf = regs; 99588e75cc3SDavid Brownell 99688e75cc3SDavid Brownell status = i2c_transfer(the_menelaus->client->adapter, msg, 2); 99788e75cc3SDavid Brownell if (status != 2) { 99888e75cc3SDavid Brownell dev_err(dev, "%s error %d\n", "alarm read", status); 99988e75cc3SDavid Brownell return -EIO; 100088e75cc3SDavid Brownell } 100188e75cc3SDavid Brownell 100288e75cc3SDavid Brownell menelaus_to_time(regs, &w->time); 100388e75cc3SDavid Brownell 100488e75cc3SDavid Brownell w->enabled = !!(the_menelaus->rtc_control & RTC_CTRL_AL_EN); 100588e75cc3SDavid Brownell 100688e75cc3SDavid Brownell /* NOTE we *could* check if actually pending... */ 100788e75cc3SDavid Brownell w->pending = 0; 100888e75cc3SDavid Brownell 100988e75cc3SDavid Brownell return 0; 101088e75cc3SDavid Brownell } 101188e75cc3SDavid Brownell 101288e75cc3SDavid Brownell static int menelaus_set_alarm(struct device *dev, struct rtc_wkalrm *w) 101388e75cc3SDavid Brownell { 101488e75cc3SDavid Brownell int status; 101588e75cc3SDavid Brownell 101688e75cc3SDavid Brownell if (the_menelaus->client->irq <= 0 && w->enabled) 101788e75cc3SDavid Brownell return -ENODEV; 101888e75cc3SDavid Brownell 101988e75cc3SDavid Brownell /* clear previous alarm enable */ 102088e75cc3SDavid Brownell if (the_menelaus->rtc_control & RTC_CTRL_AL_EN) { 102188e75cc3SDavid Brownell the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN; 102288e75cc3SDavid Brownell status = menelaus_write_reg(MENELAUS_RTC_CTRL, 102388e75cc3SDavid Brownell the_menelaus->rtc_control); 102488e75cc3SDavid Brownell if (status < 0) 102588e75cc3SDavid Brownell return status; 102688e75cc3SDavid Brownell } 102788e75cc3SDavid Brownell 102888e75cc3SDavid Brownell /* write alarm registers */ 102988e75cc3SDavid Brownell status = time_to_menelaus(&w->time, MENELAUS_RTC_AL_SEC); 103088e75cc3SDavid Brownell if (status < 0) 103188e75cc3SDavid Brownell return status; 103288e75cc3SDavid Brownell 103388e75cc3SDavid Brownell /* enable alarm if requested */ 103488e75cc3SDavid Brownell if (w->enabled) { 103588e75cc3SDavid Brownell the_menelaus->rtc_control |= RTC_CTRL_AL_EN; 103688e75cc3SDavid Brownell status = menelaus_write_reg(MENELAUS_RTC_CTRL, 103788e75cc3SDavid Brownell the_menelaus->rtc_control); 103888e75cc3SDavid Brownell } 103988e75cc3SDavid Brownell 104088e75cc3SDavid Brownell return status; 104188e75cc3SDavid Brownell } 104288e75cc3SDavid Brownell 104388e75cc3SDavid Brownell #ifdef CONFIG_RTC_INTF_DEV 104488e75cc3SDavid Brownell 104588e75cc3SDavid Brownell static void menelaus_rtc_update_work(struct menelaus_chip *m) 104688e75cc3SDavid Brownell { 104788e75cc3SDavid Brownell /* report 1/sec update */ 104888e75cc3SDavid Brownell local_irq_disable(); 104988e75cc3SDavid Brownell rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_UF); 105088e75cc3SDavid Brownell local_irq_enable(); 105188e75cc3SDavid Brownell } 105288e75cc3SDavid Brownell 105388e75cc3SDavid Brownell static int menelaus_ioctl(struct device *dev, unsigned cmd, unsigned long arg) 105488e75cc3SDavid Brownell { 105588e75cc3SDavid Brownell int status; 105688e75cc3SDavid Brownell 105788e75cc3SDavid Brownell if (the_menelaus->client->irq <= 0) 105888e75cc3SDavid Brownell return -ENOIOCTLCMD; 105988e75cc3SDavid Brownell 106088e75cc3SDavid Brownell switch (cmd) { 106188e75cc3SDavid Brownell /* alarm IRQ */ 106288e75cc3SDavid Brownell case RTC_AIE_ON: 106388e75cc3SDavid Brownell if (the_menelaus->rtc_control & RTC_CTRL_AL_EN) 106488e75cc3SDavid Brownell return 0; 106588e75cc3SDavid Brownell the_menelaus->rtc_control |= RTC_CTRL_AL_EN; 106688e75cc3SDavid Brownell break; 106788e75cc3SDavid Brownell case RTC_AIE_OFF: 106888e75cc3SDavid Brownell if (!(the_menelaus->rtc_control & RTC_CTRL_AL_EN)) 106988e75cc3SDavid Brownell return 0; 107088e75cc3SDavid Brownell the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN; 107188e75cc3SDavid Brownell break; 107288e75cc3SDavid Brownell /* 1/second "update" IRQ */ 107388e75cc3SDavid Brownell case RTC_UIE_ON: 107488e75cc3SDavid Brownell if (the_menelaus->uie) 107588e75cc3SDavid Brownell return 0; 107688e75cc3SDavid Brownell status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ); 107788e75cc3SDavid Brownell status = menelaus_add_irq_work(MENELAUS_RTCTMR_IRQ, 107888e75cc3SDavid Brownell menelaus_rtc_update_work); 107988e75cc3SDavid Brownell if (status == 0) 108088e75cc3SDavid Brownell the_menelaus->uie = 1; 108188e75cc3SDavid Brownell return status; 108288e75cc3SDavid Brownell case RTC_UIE_OFF: 108388e75cc3SDavid Brownell if (!the_menelaus->uie) 108488e75cc3SDavid Brownell return 0; 108588e75cc3SDavid Brownell status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ); 108688e75cc3SDavid Brownell if (status == 0) 108788e75cc3SDavid Brownell the_menelaus->uie = 0; 108888e75cc3SDavid Brownell return status; 108988e75cc3SDavid Brownell default: 109088e75cc3SDavid Brownell return -ENOIOCTLCMD; 109188e75cc3SDavid Brownell } 109288e75cc3SDavid Brownell return menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control); 109388e75cc3SDavid Brownell } 109488e75cc3SDavid Brownell 109588e75cc3SDavid Brownell #else 109688e75cc3SDavid Brownell #define menelaus_ioctl NULL 109788e75cc3SDavid Brownell #endif 109888e75cc3SDavid Brownell 109988e75cc3SDavid Brownell /* REVISIT no compensation register support ... */ 110088e75cc3SDavid Brownell 110188e75cc3SDavid Brownell static const struct rtc_class_ops menelaus_rtc_ops = { 110288e75cc3SDavid Brownell .ioctl = menelaus_ioctl, 110388e75cc3SDavid Brownell .read_time = menelaus_read_time, 110488e75cc3SDavid Brownell .set_time = menelaus_set_time, 110588e75cc3SDavid Brownell .read_alarm = menelaus_read_alarm, 110688e75cc3SDavid Brownell .set_alarm = menelaus_set_alarm, 110788e75cc3SDavid Brownell }; 110888e75cc3SDavid Brownell 110988e75cc3SDavid Brownell static void menelaus_rtc_alarm_work(struct menelaus_chip *m) 111088e75cc3SDavid Brownell { 111188e75cc3SDavid Brownell /* report alarm */ 111288e75cc3SDavid Brownell local_irq_disable(); 111388e75cc3SDavid Brownell rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_AF); 111488e75cc3SDavid Brownell local_irq_enable(); 111588e75cc3SDavid Brownell 111688e75cc3SDavid Brownell /* then disable it; alarms are oneshot */ 111788e75cc3SDavid Brownell the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN; 111888e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control); 111988e75cc3SDavid Brownell } 112088e75cc3SDavid Brownell 112188e75cc3SDavid Brownell static inline void menelaus_rtc_init(struct menelaus_chip *m) 112288e75cc3SDavid Brownell { 112388e75cc3SDavid Brownell int alarm = (m->client->irq > 0); 112488e75cc3SDavid Brownell 112588e75cc3SDavid Brownell /* assume 32KDETEN pin is pulled high */ 112688e75cc3SDavid Brownell if (!(menelaus_read_reg(MENELAUS_OSC_CTRL) & 0x80)) { 112788e75cc3SDavid Brownell dev_dbg(&m->client->dev, "no 32k oscillator\n"); 112888e75cc3SDavid Brownell return; 112988e75cc3SDavid Brownell } 113088e75cc3SDavid Brownell 113188e75cc3SDavid Brownell /* support RTC alarm; it can issue wakeups */ 113288e75cc3SDavid Brownell if (alarm) { 113388e75cc3SDavid Brownell if (menelaus_add_irq_work(MENELAUS_RTCALM_IRQ, 113488e75cc3SDavid Brownell menelaus_rtc_alarm_work) < 0) { 113588e75cc3SDavid Brownell dev_err(&m->client->dev, "can't handle RTC alarm\n"); 113688e75cc3SDavid Brownell return; 113788e75cc3SDavid Brownell } 113888e75cc3SDavid Brownell device_init_wakeup(&m->client->dev, 1); 113988e75cc3SDavid Brownell } 114088e75cc3SDavid Brownell 114188e75cc3SDavid Brownell /* be sure RTC is enabled; allow 1/sec irqs; leave 12hr mode alone */ 114288e75cc3SDavid Brownell m->rtc_control = menelaus_read_reg(MENELAUS_RTC_CTRL); 114388e75cc3SDavid Brownell if (!(m->rtc_control & RTC_CTRL_RTC_EN) 114488e75cc3SDavid Brownell || (m->rtc_control & RTC_CTRL_AL_EN) 114588e75cc3SDavid Brownell || (m->rtc_control & RTC_CTRL_EVERY_MASK)) { 114688e75cc3SDavid Brownell if (!(m->rtc_control & RTC_CTRL_RTC_EN)) { 114788e75cc3SDavid Brownell dev_warn(&m->client->dev, "rtc clock needs setting\n"); 114888e75cc3SDavid Brownell m->rtc_control |= RTC_CTRL_RTC_EN; 114988e75cc3SDavid Brownell } 115088e75cc3SDavid Brownell m->rtc_control &= ~RTC_CTRL_EVERY_MASK; 115188e75cc3SDavid Brownell m->rtc_control &= ~RTC_CTRL_AL_EN; 115288e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_RTC_CTRL, m->rtc_control); 115388e75cc3SDavid Brownell } 115488e75cc3SDavid Brownell 115588e75cc3SDavid Brownell m->rtc = rtc_device_register(DRIVER_NAME, 115688e75cc3SDavid Brownell &m->client->dev, 115788e75cc3SDavid Brownell &menelaus_rtc_ops, THIS_MODULE); 115888e75cc3SDavid Brownell if (IS_ERR(m->rtc)) { 115988e75cc3SDavid Brownell if (alarm) { 116088e75cc3SDavid Brownell menelaus_remove_irq_work(MENELAUS_RTCALM_IRQ); 116188e75cc3SDavid Brownell device_init_wakeup(&m->client->dev, 0); 116288e75cc3SDavid Brownell } 116388e75cc3SDavid Brownell dev_err(&m->client->dev, "can't register RTC: %d\n", 116488e75cc3SDavid Brownell (int) PTR_ERR(m->rtc)); 116588e75cc3SDavid Brownell the_menelaus->rtc = NULL; 116688e75cc3SDavid Brownell } 116788e75cc3SDavid Brownell } 116888e75cc3SDavid Brownell 116988e75cc3SDavid Brownell #else 117088e75cc3SDavid Brownell 117188e75cc3SDavid Brownell static inline void menelaus_rtc_init(struct menelaus_chip *m) 117288e75cc3SDavid Brownell { 117388e75cc3SDavid Brownell /* nothing */ 117488e75cc3SDavid Brownell } 117588e75cc3SDavid Brownell 117688e75cc3SDavid Brownell #endif 117788e75cc3SDavid Brownell 117888e75cc3SDavid Brownell /*-----------------------------------------------------------------------*/ 117988e75cc3SDavid Brownell 118088e75cc3SDavid Brownell static struct i2c_driver menelaus_i2c_driver; 118188e75cc3SDavid Brownell 118288e75cc3SDavid Brownell static int menelaus_probe(struct i2c_client *client, 118388e75cc3SDavid Brownell const struct i2c_device_id *id) 118488e75cc3SDavid Brownell { 118588e75cc3SDavid Brownell struct menelaus_chip *menelaus; 1186*42a71ef9SJulia Lawall int rev = 0; 118788e75cc3SDavid Brownell int err = 0; 118888e75cc3SDavid Brownell struct menelaus_platform_data *menelaus_pdata = 1189334a41ceSJingoo Han dev_get_platdata(&client->dev); 119088e75cc3SDavid Brownell 119188e75cc3SDavid Brownell if (the_menelaus) { 119288e75cc3SDavid Brownell dev_dbg(&client->dev, "only one %s for now\n", 119388e75cc3SDavid Brownell DRIVER_NAME); 119488e75cc3SDavid Brownell return -ENODEV; 119588e75cc3SDavid Brownell } 119688e75cc3SDavid Brownell 11977a404311SJingoo Han menelaus = devm_kzalloc(&client->dev, sizeof(*menelaus), GFP_KERNEL); 119888e75cc3SDavid Brownell if (!menelaus) 119988e75cc3SDavid Brownell return -ENOMEM; 120088e75cc3SDavid Brownell 120188e75cc3SDavid Brownell i2c_set_clientdata(client, menelaus); 120288e75cc3SDavid Brownell 120388e75cc3SDavid Brownell the_menelaus = menelaus; 120488e75cc3SDavid Brownell menelaus->client = client; 120588e75cc3SDavid Brownell 120688e75cc3SDavid Brownell /* If a true probe check the device */ 120788e75cc3SDavid Brownell rev = menelaus_read_reg(MENELAUS_REV); 120888e75cc3SDavid Brownell if (rev < 0) { 120988e75cc3SDavid Brownell pr_err(DRIVER_NAME ": device not found"); 12107a404311SJingoo Han return -ENODEV; 121188e75cc3SDavid Brownell } 121288e75cc3SDavid Brownell 121388e75cc3SDavid Brownell /* Ack and disable all Menelaus interrupts */ 121488e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_INT_ACK1, 0xff); 121588e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_INT_ACK2, 0xff); 121688e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_INT_MASK1, 0xff); 121788e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_INT_MASK2, 0xff); 121888e75cc3SDavid Brownell menelaus->mask1 = 0xff; 121988e75cc3SDavid Brownell menelaus->mask2 = 0xff; 122088e75cc3SDavid Brownell 122188e75cc3SDavid Brownell /* Set output buffer strengths */ 122288e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_MCT_CTRL1, 0x73); 122388e75cc3SDavid Brownell 122488e75cc3SDavid Brownell if (client->irq > 0) { 1225f742b96eSYong Zhang err = request_irq(client->irq, menelaus_irq, 0, 122688e75cc3SDavid Brownell DRIVER_NAME, menelaus); 122788e75cc3SDavid Brownell if (err) { 122888e75cc3SDavid Brownell dev_dbg(&client->dev, "can't get IRQ %d, err %d\n", 122988e75cc3SDavid Brownell client->irq, err); 12307a404311SJingoo Han return err; 123188e75cc3SDavid Brownell } 123288e75cc3SDavid Brownell } 123388e75cc3SDavid Brownell 123488e75cc3SDavid Brownell mutex_init(&menelaus->lock); 123588e75cc3SDavid Brownell INIT_WORK(&menelaus->work, menelaus_work); 123688e75cc3SDavid Brownell 123788e75cc3SDavid Brownell pr_info("Menelaus rev %d.%d\n", rev >> 4, rev & 0x0f); 123888e75cc3SDavid Brownell 1239*42a71ef9SJulia Lawall err = menelaus_read_reg(MENELAUS_VCORE_CTRL1); 1240*42a71ef9SJulia Lawall if (err < 0) 12417a404311SJingoo Han goto fail; 1242*42a71ef9SJulia Lawall if (err & BIT(7)) 124388e75cc3SDavid Brownell menelaus->vcore_hw_mode = 1; 124488e75cc3SDavid Brownell else 124588e75cc3SDavid Brownell menelaus->vcore_hw_mode = 0; 124688e75cc3SDavid Brownell 124788e75cc3SDavid Brownell if (menelaus_pdata != NULL && menelaus_pdata->late_init != NULL) { 124888e75cc3SDavid Brownell err = menelaus_pdata->late_init(&client->dev); 124988e75cc3SDavid Brownell if (err < 0) 12507a404311SJingoo Han goto fail; 125188e75cc3SDavid Brownell } 125288e75cc3SDavid Brownell 125388e75cc3SDavid Brownell menelaus_rtc_init(menelaus); 125488e75cc3SDavid Brownell 125588e75cc3SDavid Brownell return 0; 12567a404311SJingoo Han fail: 125788e75cc3SDavid Brownell free_irq(client->irq, menelaus); 125843829731STejun Heo flush_work(&menelaus->work); 125988e75cc3SDavid Brownell return err; 126088e75cc3SDavid Brownell } 126188e75cc3SDavid Brownell 126288e75cc3SDavid Brownell static int __exit menelaus_remove(struct i2c_client *client) 126388e75cc3SDavid Brownell { 126488e75cc3SDavid Brownell struct menelaus_chip *menelaus = i2c_get_clientdata(client); 126588e75cc3SDavid Brownell 126688e75cc3SDavid Brownell free_irq(client->irq, menelaus); 126743829731STejun Heo flush_work(&menelaus->work); 126888e75cc3SDavid Brownell the_menelaus = NULL; 126988e75cc3SDavid Brownell return 0; 127088e75cc3SDavid Brownell } 127188e75cc3SDavid Brownell 127288e75cc3SDavid Brownell static const struct i2c_device_id menelaus_id[] = { 127388e75cc3SDavid Brownell { "menelaus", 0 }, 127488e75cc3SDavid Brownell { } 127588e75cc3SDavid Brownell }; 127688e75cc3SDavid Brownell MODULE_DEVICE_TABLE(i2c, menelaus_id); 127788e75cc3SDavid Brownell 127888e75cc3SDavid Brownell static struct i2c_driver menelaus_i2c_driver = { 127988e75cc3SDavid Brownell .driver = { 128088e75cc3SDavid Brownell .name = DRIVER_NAME, 128188e75cc3SDavid Brownell }, 128288e75cc3SDavid Brownell .probe = menelaus_probe, 128388e75cc3SDavid Brownell .remove = __exit_p(menelaus_remove), 128488e75cc3SDavid Brownell .id_table = menelaus_id, 128588e75cc3SDavid Brownell }; 128688e75cc3SDavid Brownell 12871d3c7f56SSachin Kamat module_i2c_driver(menelaus_i2c_driver); 128888e75cc3SDavid Brownell 128988e75cc3SDavid Brownell MODULE_AUTHOR("Texas Instruments, Inc. (and others)"); 129088e75cc3SDavid Brownell MODULE_DESCRIPTION("I2C interface for Menelaus."); 129188e75cc3SDavid Brownell MODULE_LICENSE("GPL"); 1292