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> 4488e75cc3SDavid Brownell 4588e75cc3SDavid Brownell #include <asm/mach/irq.h> 4688e75cc3SDavid Brownell 471bc857f7SRussell King #include <asm/gpio.h> 48ce491cf8STony Lindgren #include <plat/menelaus.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; 44588e75cc3SDavid Brownell the_menelaus->mmc_callback_data = 0; 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 if (vtg == 0) 47088e75cc3SDavid Brownell goto set_voltage; 47188e75cc3SDavid Brownell 47288e75cc3SDavid Brownell ret = menelaus_read_reg(vtg->vtg_reg); 47388e75cc3SDavid Brownell if (ret < 0) 47488e75cc3SDavid Brownell goto out; 47588e75cc3SDavid Brownell val = ret & ~(((1 << vtg->vtg_bits) - 1) << vtg->vtg_shift); 47688e75cc3SDavid Brownell val |= vtg_val << vtg->vtg_shift; 47788e75cc3SDavid Brownell 47888e75cc3SDavid Brownell dev_dbg(&c->dev, "Setting voltage '%s'" 47988e75cc3SDavid Brownell "to %d mV (reg 0x%02x, val 0x%02x)\n", 48088e75cc3SDavid Brownell vtg->name, mV, vtg->vtg_reg, val); 48188e75cc3SDavid Brownell 48288e75cc3SDavid Brownell ret = menelaus_write_reg(vtg->vtg_reg, val); 48388e75cc3SDavid Brownell if (ret < 0) 48488e75cc3SDavid Brownell goto out; 48588e75cc3SDavid Brownell set_voltage: 48688e75cc3SDavid Brownell ret = menelaus_write_reg(vtg->mode_reg, mode); 48788e75cc3SDavid Brownell out: 48888e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 48988e75cc3SDavid Brownell if (ret == 0) { 49088e75cc3SDavid Brownell /* Wait for voltage to stabilize */ 49188e75cc3SDavid Brownell msleep(1); 49288e75cc3SDavid Brownell } 49388e75cc3SDavid Brownell return ret; 49488e75cc3SDavid Brownell } 49588e75cc3SDavid Brownell 49688e75cc3SDavid Brownell static int menelaus_get_vtg_value(int vtg, const struct menelaus_vtg_value *tbl, 49788e75cc3SDavid Brownell int n) 49888e75cc3SDavid Brownell { 49988e75cc3SDavid Brownell int i; 50088e75cc3SDavid Brownell 50188e75cc3SDavid Brownell for (i = 0; i < n; i++, tbl++) 50288e75cc3SDavid Brownell if (tbl->vtg == vtg) 50388e75cc3SDavid Brownell return tbl->val; 50488e75cc3SDavid Brownell return -EINVAL; 50588e75cc3SDavid Brownell } 50688e75cc3SDavid Brownell 50788e75cc3SDavid Brownell /* 50888e75cc3SDavid Brownell * Vcore can be programmed in two ways: 50988e75cc3SDavid Brownell * SW-controlled: Required voltage is programmed into VCORE_CTRL1 51088e75cc3SDavid Brownell * HW-controlled: Required range (roof-floor) is programmed into VCORE_CTRL3 51188e75cc3SDavid Brownell * and VCORE_CTRL4 51288e75cc3SDavid Brownell * 51388e75cc3SDavid Brownell * Call correct 'set' function accordingly 51488e75cc3SDavid Brownell */ 51588e75cc3SDavid Brownell 51688e75cc3SDavid Brownell static const struct menelaus_vtg_value vcore_values[] = { 51788e75cc3SDavid Brownell { 1000, 0 }, 51888e75cc3SDavid Brownell { 1025, 1 }, 51988e75cc3SDavid Brownell { 1050, 2 }, 52088e75cc3SDavid Brownell { 1075, 3 }, 52188e75cc3SDavid Brownell { 1100, 4 }, 52288e75cc3SDavid Brownell { 1125, 5 }, 52388e75cc3SDavid Brownell { 1150, 6 }, 52488e75cc3SDavid Brownell { 1175, 7 }, 52588e75cc3SDavid Brownell { 1200, 8 }, 52688e75cc3SDavid Brownell { 1225, 9 }, 52788e75cc3SDavid Brownell { 1250, 10 }, 52888e75cc3SDavid Brownell { 1275, 11 }, 52988e75cc3SDavid Brownell { 1300, 12 }, 53088e75cc3SDavid Brownell { 1325, 13 }, 53188e75cc3SDavid Brownell { 1350, 14 }, 53288e75cc3SDavid Brownell { 1375, 15 }, 53388e75cc3SDavid Brownell { 1400, 16 }, 53488e75cc3SDavid Brownell { 1425, 17 }, 53588e75cc3SDavid Brownell { 1450, 18 }, 53688e75cc3SDavid Brownell }; 53788e75cc3SDavid Brownell 53888e75cc3SDavid Brownell int menelaus_set_vcore_sw(unsigned int mV) 53988e75cc3SDavid Brownell { 54088e75cc3SDavid Brownell int val, ret; 54188e75cc3SDavid Brownell struct i2c_client *c = the_menelaus->client; 54288e75cc3SDavid Brownell 54388e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vcore_values, 54488e75cc3SDavid Brownell ARRAY_SIZE(vcore_values)); 54588e75cc3SDavid Brownell if (val < 0) 54688e75cc3SDavid Brownell return -EINVAL; 54788e75cc3SDavid Brownell 54888e75cc3SDavid Brownell dev_dbg(&c->dev, "Setting VCORE to %d mV (val 0x%02x)\n", mV, val); 54988e75cc3SDavid Brownell 55088e75cc3SDavid Brownell /* Set SW mode and the voltage in one go. */ 55188e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 55288e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val); 55388e75cc3SDavid Brownell if (ret == 0) 55488e75cc3SDavid Brownell the_menelaus->vcore_hw_mode = 0; 55588e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 55688e75cc3SDavid Brownell msleep(1); 55788e75cc3SDavid Brownell 55888e75cc3SDavid Brownell return ret; 55988e75cc3SDavid Brownell } 56088e75cc3SDavid Brownell 56188e75cc3SDavid Brownell int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV) 56288e75cc3SDavid Brownell { 56388e75cc3SDavid Brownell int fval, rval, val, ret; 56488e75cc3SDavid Brownell struct i2c_client *c = the_menelaus->client; 56588e75cc3SDavid Brownell 56688e75cc3SDavid Brownell rval = menelaus_get_vtg_value(roof_mV, vcore_values, 56788e75cc3SDavid Brownell ARRAY_SIZE(vcore_values)); 56888e75cc3SDavid Brownell if (rval < 0) 56988e75cc3SDavid Brownell return -EINVAL; 57088e75cc3SDavid Brownell fval = menelaus_get_vtg_value(floor_mV, vcore_values, 57188e75cc3SDavid Brownell ARRAY_SIZE(vcore_values)); 57288e75cc3SDavid Brownell if (fval < 0) 57388e75cc3SDavid Brownell return -EINVAL; 57488e75cc3SDavid Brownell 57588e75cc3SDavid Brownell dev_dbg(&c->dev, "Setting VCORE FLOOR to %d mV and ROOF to %d mV\n", 57688e75cc3SDavid Brownell floor_mV, roof_mV); 57788e75cc3SDavid Brownell 57888e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 57988e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_VCORE_CTRL3, fval); 58088e75cc3SDavid Brownell if (ret < 0) 58188e75cc3SDavid Brownell goto out; 58288e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_VCORE_CTRL4, rval); 58388e75cc3SDavid Brownell if (ret < 0) 58488e75cc3SDavid Brownell goto out; 58588e75cc3SDavid Brownell if (!the_menelaus->vcore_hw_mode) { 58688e75cc3SDavid Brownell val = menelaus_read_reg(MENELAUS_VCORE_CTRL1); 58788e75cc3SDavid Brownell /* HW mode, turn OFF byte comparator */ 5881c888e2eSJarkko Nikula val |= (VCORE_CTRL1_HW_NSW | VCORE_CTRL1_BYP_COMP); 58988e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val); 59088e75cc3SDavid Brownell the_menelaus->vcore_hw_mode = 1; 59188e75cc3SDavid Brownell } 59288e75cc3SDavid Brownell msleep(1); 59388e75cc3SDavid Brownell out: 59488e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 59588e75cc3SDavid Brownell return ret; 59688e75cc3SDavid Brownell } 59788e75cc3SDavid Brownell 59888e75cc3SDavid Brownell static const struct menelaus_vtg vmem_vtg = { 59988e75cc3SDavid Brownell .name = "VMEM", 60088e75cc3SDavid Brownell .vtg_reg = MENELAUS_LDO_CTRL1, 60188e75cc3SDavid Brownell .vtg_shift = 0, 60288e75cc3SDavid Brownell .vtg_bits = 2, 60388e75cc3SDavid Brownell .mode_reg = MENELAUS_LDO_CTRL3, 60488e75cc3SDavid Brownell }; 60588e75cc3SDavid Brownell 60688e75cc3SDavid Brownell static const struct menelaus_vtg_value vmem_values[] = { 60788e75cc3SDavid Brownell { 1500, 0 }, 60888e75cc3SDavid Brownell { 1800, 1 }, 60988e75cc3SDavid Brownell { 1900, 2 }, 61088e75cc3SDavid Brownell { 2500, 3 }, 61188e75cc3SDavid Brownell }; 61288e75cc3SDavid Brownell 61388e75cc3SDavid Brownell int menelaus_set_vmem(unsigned int mV) 61488e75cc3SDavid Brownell { 61588e75cc3SDavid Brownell int val; 61688e75cc3SDavid Brownell 61788e75cc3SDavid Brownell if (mV == 0) 61888e75cc3SDavid Brownell return menelaus_set_voltage(&vmem_vtg, 0, 0, 0); 61988e75cc3SDavid Brownell 62088e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vmem_values, ARRAY_SIZE(vmem_values)); 62188e75cc3SDavid Brownell if (val < 0) 62288e75cc3SDavid Brownell return -EINVAL; 62388e75cc3SDavid Brownell return menelaus_set_voltage(&vmem_vtg, mV, val, 0x02); 62488e75cc3SDavid Brownell } 62588e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_vmem); 62688e75cc3SDavid Brownell 62788e75cc3SDavid Brownell static const struct menelaus_vtg vio_vtg = { 62888e75cc3SDavid Brownell .name = "VIO", 62988e75cc3SDavid Brownell .vtg_reg = MENELAUS_LDO_CTRL1, 63088e75cc3SDavid Brownell .vtg_shift = 2, 63188e75cc3SDavid Brownell .vtg_bits = 2, 63288e75cc3SDavid Brownell .mode_reg = MENELAUS_LDO_CTRL4, 63388e75cc3SDavid Brownell }; 63488e75cc3SDavid Brownell 63588e75cc3SDavid Brownell static const struct menelaus_vtg_value vio_values[] = { 63688e75cc3SDavid Brownell { 1500, 0 }, 63788e75cc3SDavid Brownell { 1800, 1 }, 63888e75cc3SDavid Brownell { 2500, 2 }, 63988e75cc3SDavid Brownell { 2800, 3 }, 64088e75cc3SDavid Brownell }; 64188e75cc3SDavid Brownell 64288e75cc3SDavid Brownell int menelaus_set_vio(unsigned int mV) 64388e75cc3SDavid Brownell { 64488e75cc3SDavid Brownell int val; 64588e75cc3SDavid Brownell 64688e75cc3SDavid Brownell if (mV == 0) 64788e75cc3SDavid Brownell return menelaus_set_voltage(&vio_vtg, 0, 0, 0); 64888e75cc3SDavid Brownell 64988e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vio_values, ARRAY_SIZE(vio_values)); 65088e75cc3SDavid Brownell if (val < 0) 65188e75cc3SDavid Brownell return -EINVAL; 65288e75cc3SDavid Brownell return menelaus_set_voltage(&vio_vtg, mV, val, 0x02); 65388e75cc3SDavid Brownell } 65488e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_vio); 65588e75cc3SDavid Brownell 65688e75cc3SDavid Brownell static const struct menelaus_vtg_value vdcdc_values[] = { 65788e75cc3SDavid Brownell { 1500, 0 }, 65888e75cc3SDavid Brownell { 1800, 1 }, 65988e75cc3SDavid Brownell { 2000, 2 }, 66088e75cc3SDavid Brownell { 2200, 3 }, 66188e75cc3SDavid Brownell { 2400, 4 }, 66288e75cc3SDavid Brownell { 2800, 5 }, 66388e75cc3SDavid Brownell { 3000, 6 }, 66488e75cc3SDavid Brownell { 3300, 7 }, 66588e75cc3SDavid Brownell }; 66688e75cc3SDavid Brownell 66788e75cc3SDavid Brownell static const struct menelaus_vtg vdcdc2_vtg = { 66888e75cc3SDavid Brownell .name = "VDCDC2", 66988e75cc3SDavid Brownell .vtg_reg = MENELAUS_DCDC_CTRL1, 67088e75cc3SDavid Brownell .vtg_shift = 0, 67188e75cc3SDavid Brownell .vtg_bits = 3, 67288e75cc3SDavid Brownell .mode_reg = MENELAUS_DCDC_CTRL2, 67388e75cc3SDavid Brownell }; 67488e75cc3SDavid Brownell 67588e75cc3SDavid Brownell static const struct menelaus_vtg vdcdc3_vtg = { 67688e75cc3SDavid Brownell .name = "VDCDC3", 67788e75cc3SDavid Brownell .vtg_reg = MENELAUS_DCDC_CTRL1, 67888e75cc3SDavid Brownell .vtg_shift = 3, 67988e75cc3SDavid Brownell .vtg_bits = 3, 68088e75cc3SDavid Brownell .mode_reg = MENELAUS_DCDC_CTRL3, 68188e75cc3SDavid Brownell }; 68288e75cc3SDavid Brownell 68388e75cc3SDavid Brownell int menelaus_set_vdcdc(int dcdc, unsigned int mV) 68488e75cc3SDavid Brownell { 68588e75cc3SDavid Brownell const struct menelaus_vtg *vtg; 68688e75cc3SDavid Brownell int val; 68788e75cc3SDavid Brownell 68888e75cc3SDavid Brownell if (dcdc != 2 && dcdc != 3) 68988e75cc3SDavid Brownell return -EINVAL; 69088e75cc3SDavid Brownell if (dcdc == 2) 69188e75cc3SDavid Brownell vtg = &vdcdc2_vtg; 69288e75cc3SDavid Brownell else 69388e75cc3SDavid Brownell vtg = &vdcdc3_vtg; 69488e75cc3SDavid Brownell 69588e75cc3SDavid Brownell if (mV == 0) 69688e75cc3SDavid Brownell return menelaus_set_voltage(vtg, 0, 0, 0); 69788e75cc3SDavid Brownell 69888e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vdcdc_values, 69988e75cc3SDavid Brownell ARRAY_SIZE(vdcdc_values)); 70088e75cc3SDavid Brownell if (val < 0) 70188e75cc3SDavid Brownell return -EINVAL; 70288e75cc3SDavid Brownell return menelaus_set_voltage(vtg, mV, val, 0x03); 70388e75cc3SDavid Brownell } 70488e75cc3SDavid Brownell 70588e75cc3SDavid Brownell static const struct menelaus_vtg_value vmmc_values[] = { 70688e75cc3SDavid Brownell { 1850, 0 }, 70788e75cc3SDavid Brownell { 2800, 1 }, 70888e75cc3SDavid Brownell { 3000, 2 }, 70988e75cc3SDavid Brownell { 3100, 3 }, 71088e75cc3SDavid Brownell }; 71188e75cc3SDavid Brownell 71288e75cc3SDavid Brownell static const struct menelaus_vtg vmmc_vtg = { 71388e75cc3SDavid Brownell .name = "VMMC", 71488e75cc3SDavid Brownell .vtg_reg = MENELAUS_LDO_CTRL1, 71588e75cc3SDavid Brownell .vtg_shift = 6, 71688e75cc3SDavid Brownell .vtg_bits = 2, 71788e75cc3SDavid Brownell .mode_reg = MENELAUS_LDO_CTRL7, 71888e75cc3SDavid Brownell }; 71988e75cc3SDavid Brownell 72088e75cc3SDavid Brownell int menelaus_set_vmmc(unsigned int mV) 72188e75cc3SDavid Brownell { 72288e75cc3SDavid Brownell int val; 72388e75cc3SDavid Brownell 72488e75cc3SDavid Brownell if (mV == 0) 72588e75cc3SDavid Brownell return menelaus_set_voltage(&vmmc_vtg, 0, 0, 0); 72688e75cc3SDavid Brownell 72788e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vmmc_values, ARRAY_SIZE(vmmc_values)); 72888e75cc3SDavid Brownell if (val < 0) 72988e75cc3SDavid Brownell return -EINVAL; 73088e75cc3SDavid Brownell return menelaus_set_voltage(&vmmc_vtg, mV, val, 0x02); 73188e75cc3SDavid Brownell } 73288e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_vmmc); 73388e75cc3SDavid Brownell 73488e75cc3SDavid Brownell 73588e75cc3SDavid Brownell static const struct menelaus_vtg_value vaux_values[] = { 73688e75cc3SDavid Brownell { 1500, 0 }, 73788e75cc3SDavid Brownell { 1800, 1 }, 73888e75cc3SDavid Brownell { 2500, 2 }, 73988e75cc3SDavid Brownell { 2800, 3 }, 74088e75cc3SDavid Brownell }; 74188e75cc3SDavid Brownell 74288e75cc3SDavid Brownell static const struct menelaus_vtg vaux_vtg = { 74388e75cc3SDavid Brownell .name = "VAUX", 74488e75cc3SDavid Brownell .vtg_reg = MENELAUS_LDO_CTRL1, 74588e75cc3SDavid Brownell .vtg_shift = 4, 74688e75cc3SDavid Brownell .vtg_bits = 2, 74788e75cc3SDavid Brownell .mode_reg = MENELAUS_LDO_CTRL6, 74888e75cc3SDavid Brownell }; 74988e75cc3SDavid Brownell 75088e75cc3SDavid Brownell int menelaus_set_vaux(unsigned int mV) 75188e75cc3SDavid Brownell { 75288e75cc3SDavid Brownell int val; 75388e75cc3SDavid Brownell 75488e75cc3SDavid Brownell if (mV == 0) 75588e75cc3SDavid Brownell return menelaus_set_voltage(&vaux_vtg, 0, 0, 0); 75688e75cc3SDavid Brownell 75788e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vaux_values, ARRAY_SIZE(vaux_values)); 75888e75cc3SDavid Brownell if (val < 0) 75988e75cc3SDavid Brownell return -EINVAL; 76088e75cc3SDavid Brownell return menelaus_set_voltage(&vaux_vtg, mV, val, 0x02); 76188e75cc3SDavid Brownell } 76288e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_vaux); 76388e75cc3SDavid Brownell 76488e75cc3SDavid Brownell int menelaus_get_slot_pin_states(void) 76588e75cc3SDavid Brownell { 76688e75cc3SDavid Brownell return menelaus_read_reg(MENELAUS_MCT_PIN_ST); 76788e75cc3SDavid Brownell } 76888e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_get_slot_pin_states); 76988e75cc3SDavid Brownell 77088e75cc3SDavid Brownell int menelaus_set_regulator_sleep(int enable, u32 val) 77188e75cc3SDavid Brownell { 77288e75cc3SDavid Brownell int t, ret; 77388e75cc3SDavid Brownell struct i2c_client *c = the_menelaus->client; 77488e75cc3SDavid Brownell 77588e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 77688e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_SLEEP_CTRL2, val); 77788e75cc3SDavid Brownell if (ret < 0) 77888e75cc3SDavid Brownell goto out; 77988e75cc3SDavid Brownell 78088e75cc3SDavid Brownell dev_dbg(&c->dev, "regulator sleep configuration: %02x\n", val); 78188e75cc3SDavid Brownell 78288e75cc3SDavid Brownell ret = menelaus_read_reg(MENELAUS_GPIO_CTRL); 78388e75cc3SDavid Brownell if (ret < 0) 78488e75cc3SDavid Brownell goto out; 7851c888e2eSJarkko Nikula t = (GPIO_CTRL_SLPCTLEN | GPIO3_DIR_INPUT); 78688e75cc3SDavid Brownell if (enable) 78788e75cc3SDavid Brownell ret |= t; 78888e75cc3SDavid Brownell else 78988e75cc3SDavid Brownell ret &= ~t; 79088e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret); 79188e75cc3SDavid Brownell out: 79288e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 79388e75cc3SDavid Brownell return ret; 79488e75cc3SDavid Brownell } 79588e75cc3SDavid Brownell 79688e75cc3SDavid Brownell /*-----------------------------------------------------------------------*/ 79788e75cc3SDavid Brownell 79888e75cc3SDavid Brownell /* Handles Menelaus interrupts. Does not run in interrupt context */ 79988e75cc3SDavid Brownell static void menelaus_work(struct work_struct *_menelaus) 80088e75cc3SDavid Brownell { 80188e75cc3SDavid Brownell struct menelaus_chip *menelaus = 80288e75cc3SDavid Brownell container_of(_menelaus, struct menelaus_chip, work); 80388e75cc3SDavid Brownell void (*handler)(struct menelaus_chip *menelaus); 80488e75cc3SDavid Brownell 80588e75cc3SDavid Brownell while (1) { 80688e75cc3SDavid Brownell unsigned isr; 80788e75cc3SDavid Brownell 80888e75cc3SDavid Brownell isr = (menelaus_read_reg(MENELAUS_INT_STATUS2) 80988e75cc3SDavid Brownell & ~menelaus->mask2) << 8; 81088e75cc3SDavid Brownell isr |= menelaus_read_reg(MENELAUS_INT_STATUS1) 81188e75cc3SDavid Brownell & ~menelaus->mask1; 81288e75cc3SDavid Brownell if (!isr) 81388e75cc3SDavid Brownell break; 81488e75cc3SDavid Brownell 81588e75cc3SDavid Brownell while (isr) { 81688e75cc3SDavid Brownell int irq = fls(isr) - 1; 81788e75cc3SDavid Brownell isr &= ~(1 << irq); 81888e75cc3SDavid Brownell 81988e75cc3SDavid Brownell mutex_lock(&menelaus->lock); 82088e75cc3SDavid Brownell menelaus_disable_irq(irq); 82188e75cc3SDavid Brownell menelaus_ack_irq(irq); 82288e75cc3SDavid Brownell handler = menelaus->handlers[irq]; 82388e75cc3SDavid Brownell if (handler) 82488e75cc3SDavid Brownell handler(menelaus); 82588e75cc3SDavid Brownell menelaus_enable_irq(irq); 82688e75cc3SDavid Brownell mutex_unlock(&menelaus->lock); 82788e75cc3SDavid Brownell } 82888e75cc3SDavid Brownell } 82988e75cc3SDavid Brownell enable_irq(menelaus->client->irq); 83088e75cc3SDavid Brownell } 83188e75cc3SDavid Brownell 83288e75cc3SDavid Brownell /* 83388e75cc3SDavid Brownell * We cannot use I2C in interrupt context, so we just schedule work. 83488e75cc3SDavid Brownell */ 83588e75cc3SDavid Brownell static irqreturn_t menelaus_irq(int irq, void *_menelaus) 83688e75cc3SDavid Brownell { 83788e75cc3SDavid Brownell struct menelaus_chip *menelaus = _menelaus; 83888e75cc3SDavid Brownell 83988e75cc3SDavid Brownell disable_irq_nosync(irq); 84088e75cc3SDavid Brownell (void)schedule_work(&menelaus->work); 84188e75cc3SDavid Brownell 84288e75cc3SDavid Brownell return IRQ_HANDLED; 84388e75cc3SDavid Brownell } 84488e75cc3SDavid Brownell 84588e75cc3SDavid Brownell /*-----------------------------------------------------------------------*/ 84688e75cc3SDavid Brownell 84788e75cc3SDavid Brownell /* 84888e75cc3SDavid Brownell * The RTC needs to be set once, then it runs on backup battery power. 84988e75cc3SDavid Brownell * It supports alarms, including system wake alarms (from some modes); 85088e75cc3SDavid Brownell * and 1/second IRQs if requested. 85188e75cc3SDavid Brownell */ 85288e75cc3SDavid Brownell #ifdef CONFIG_RTC_DRV_TWL92330 85388e75cc3SDavid Brownell 85488e75cc3SDavid Brownell #define RTC_CTRL_RTC_EN (1 << 0) 85588e75cc3SDavid Brownell #define RTC_CTRL_AL_EN (1 << 1) 85688e75cc3SDavid Brownell #define RTC_CTRL_MODE12 (1 << 2) 85788e75cc3SDavid Brownell #define RTC_CTRL_EVERY_MASK (3 << 3) 85888e75cc3SDavid Brownell #define RTC_CTRL_EVERY_SEC (0 << 3) 85988e75cc3SDavid Brownell #define RTC_CTRL_EVERY_MIN (1 << 3) 86088e75cc3SDavid Brownell #define RTC_CTRL_EVERY_HR (2 << 3) 86188e75cc3SDavid Brownell #define RTC_CTRL_EVERY_DAY (3 << 3) 86288e75cc3SDavid Brownell 86388e75cc3SDavid Brownell #define RTC_UPDATE_EVERY 0x08 86488e75cc3SDavid Brownell 86588e75cc3SDavid Brownell #define RTC_HR_PM (1 << 7) 86688e75cc3SDavid Brownell 86788e75cc3SDavid Brownell static void menelaus_to_time(char *regs, struct rtc_time *t) 86888e75cc3SDavid Brownell { 86988e75cc3SDavid Brownell t->tm_sec = bcd2bin(regs[0]); 87088e75cc3SDavid Brownell t->tm_min = bcd2bin(regs[1]); 87188e75cc3SDavid Brownell if (the_menelaus->rtc_control & RTC_CTRL_MODE12) { 87288e75cc3SDavid Brownell t->tm_hour = bcd2bin(regs[2] & 0x1f) - 1; 87388e75cc3SDavid Brownell if (regs[2] & RTC_HR_PM) 87488e75cc3SDavid Brownell t->tm_hour += 12; 87588e75cc3SDavid Brownell } else 87688e75cc3SDavid Brownell t->tm_hour = bcd2bin(regs[2] & 0x3f); 87788e75cc3SDavid Brownell t->tm_mday = bcd2bin(regs[3]); 87888e75cc3SDavid Brownell t->tm_mon = bcd2bin(regs[4]) - 1; 87988e75cc3SDavid Brownell t->tm_year = bcd2bin(regs[5]) + 100; 88088e75cc3SDavid Brownell } 88188e75cc3SDavid Brownell 88288e75cc3SDavid Brownell static int time_to_menelaus(struct rtc_time *t, int regnum) 88388e75cc3SDavid Brownell { 88488e75cc3SDavid Brownell int hour, status; 88588e75cc3SDavid Brownell 88688e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, bin2bcd(t->tm_sec)); 88788e75cc3SDavid Brownell if (status < 0) 88888e75cc3SDavid Brownell goto fail; 88988e75cc3SDavid Brownell 89088e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, bin2bcd(t->tm_min)); 89188e75cc3SDavid Brownell if (status < 0) 89288e75cc3SDavid Brownell goto fail; 89388e75cc3SDavid Brownell 89488e75cc3SDavid Brownell if (the_menelaus->rtc_control & RTC_CTRL_MODE12) { 89588e75cc3SDavid Brownell hour = t->tm_hour + 1; 89688e75cc3SDavid Brownell if (hour > 12) 89788e75cc3SDavid Brownell hour = RTC_HR_PM | bin2bcd(hour - 12); 89888e75cc3SDavid Brownell else 89988e75cc3SDavid Brownell hour = bin2bcd(hour); 90088e75cc3SDavid Brownell } else 90188e75cc3SDavid Brownell hour = bin2bcd(t->tm_hour); 90288e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, hour); 90388e75cc3SDavid Brownell if (status < 0) 90488e75cc3SDavid Brownell goto fail; 90588e75cc3SDavid Brownell 90688e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, bin2bcd(t->tm_mday)); 90788e75cc3SDavid Brownell if (status < 0) 90888e75cc3SDavid Brownell goto fail; 90988e75cc3SDavid Brownell 91088e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, bin2bcd(t->tm_mon + 1)); 91188e75cc3SDavid Brownell if (status < 0) 91288e75cc3SDavid Brownell goto fail; 91388e75cc3SDavid Brownell 91488e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, bin2bcd(t->tm_year - 100)); 91588e75cc3SDavid Brownell if (status < 0) 91688e75cc3SDavid Brownell goto fail; 91788e75cc3SDavid Brownell 91888e75cc3SDavid Brownell return 0; 91988e75cc3SDavid Brownell fail: 92088e75cc3SDavid Brownell dev_err(&the_menelaus->client->dev, "rtc write reg %02x, err %d\n", 92188e75cc3SDavid Brownell --regnum, status); 92288e75cc3SDavid Brownell return status; 92388e75cc3SDavid Brownell } 92488e75cc3SDavid Brownell 92588e75cc3SDavid Brownell static int menelaus_read_time(struct device *dev, struct rtc_time *t) 92688e75cc3SDavid Brownell { 92788e75cc3SDavid Brownell struct i2c_msg msg[2]; 92888e75cc3SDavid Brownell char regs[7]; 92988e75cc3SDavid Brownell int status; 93088e75cc3SDavid Brownell 93188e75cc3SDavid Brownell /* block read date and time registers */ 93288e75cc3SDavid Brownell regs[0] = MENELAUS_RTC_SEC; 93388e75cc3SDavid Brownell 93488e75cc3SDavid Brownell msg[0].addr = MENELAUS_I2C_ADDRESS; 93588e75cc3SDavid Brownell msg[0].flags = 0; 93688e75cc3SDavid Brownell msg[0].len = 1; 93788e75cc3SDavid Brownell msg[0].buf = regs; 93888e75cc3SDavid Brownell 93988e75cc3SDavid Brownell msg[1].addr = MENELAUS_I2C_ADDRESS; 94088e75cc3SDavid Brownell msg[1].flags = I2C_M_RD; 94188e75cc3SDavid Brownell msg[1].len = sizeof(regs); 94288e75cc3SDavid Brownell msg[1].buf = regs; 94388e75cc3SDavid Brownell 94488e75cc3SDavid Brownell status = i2c_transfer(the_menelaus->client->adapter, msg, 2); 94588e75cc3SDavid Brownell if (status != 2) { 94688e75cc3SDavid Brownell dev_err(dev, "%s error %d\n", "read", status); 94788e75cc3SDavid Brownell return -EIO; 94888e75cc3SDavid Brownell } 94988e75cc3SDavid Brownell 95088e75cc3SDavid Brownell menelaus_to_time(regs, t); 95188e75cc3SDavid Brownell t->tm_wday = bcd2bin(regs[6]); 95288e75cc3SDavid Brownell 95388e75cc3SDavid Brownell return 0; 95488e75cc3SDavid Brownell } 95588e75cc3SDavid Brownell 95688e75cc3SDavid Brownell static int menelaus_set_time(struct device *dev, struct rtc_time *t) 95788e75cc3SDavid Brownell { 95888e75cc3SDavid Brownell int status; 95988e75cc3SDavid Brownell 96088e75cc3SDavid Brownell /* write date and time registers */ 96188e75cc3SDavid Brownell status = time_to_menelaus(t, MENELAUS_RTC_SEC); 96288e75cc3SDavid Brownell if (status < 0) 96388e75cc3SDavid Brownell return status; 96488e75cc3SDavid Brownell status = menelaus_write_reg(MENELAUS_RTC_WKDAY, bin2bcd(t->tm_wday)); 96588e75cc3SDavid Brownell if (status < 0) { 96688e75cc3SDavid Brownell dev_err(&the_menelaus->client->dev, "rtc write reg %02x " 96788e75cc3SDavid Brownell "err %d\n", MENELAUS_RTC_WKDAY, status); 96888e75cc3SDavid Brownell return status; 96988e75cc3SDavid Brownell } 97088e75cc3SDavid Brownell 97188e75cc3SDavid Brownell /* now commit the write */ 97288e75cc3SDavid Brownell status = menelaus_write_reg(MENELAUS_RTC_UPDATE, RTC_UPDATE_EVERY); 97388e75cc3SDavid Brownell if (status < 0) 97488e75cc3SDavid Brownell dev_err(&the_menelaus->client->dev, "rtc commit time, err %d\n", 97588e75cc3SDavid Brownell status); 97688e75cc3SDavid Brownell 97788e75cc3SDavid Brownell return 0; 97888e75cc3SDavid Brownell } 97988e75cc3SDavid Brownell 98088e75cc3SDavid Brownell static int menelaus_read_alarm(struct device *dev, struct rtc_wkalrm *w) 98188e75cc3SDavid Brownell { 98288e75cc3SDavid Brownell struct i2c_msg msg[2]; 98388e75cc3SDavid Brownell char regs[6]; 98488e75cc3SDavid Brownell int status; 98588e75cc3SDavid Brownell 98688e75cc3SDavid Brownell /* block read alarm registers */ 98788e75cc3SDavid Brownell regs[0] = MENELAUS_RTC_AL_SEC; 98888e75cc3SDavid Brownell 98988e75cc3SDavid Brownell msg[0].addr = MENELAUS_I2C_ADDRESS; 99088e75cc3SDavid Brownell msg[0].flags = 0; 99188e75cc3SDavid Brownell msg[0].len = 1; 99288e75cc3SDavid Brownell msg[0].buf = regs; 99388e75cc3SDavid Brownell 99488e75cc3SDavid Brownell msg[1].addr = MENELAUS_I2C_ADDRESS; 99588e75cc3SDavid Brownell msg[1].flags = I2C_M_RD; 99688e75cc3SDavid Brownell msg[1].len = sizeof(regs); 99788e75cc3SDavid Brownell msg[1].buf = regs; 99888e75cc3SDavid Brownell 99988e75cc3SDavid Brownell status = i2c_transfer(the_menelaus->client->adapter, msg, 2); 100088e75cc3SDavid Brownell if (status != 2) { 100188e75cc3SDavid Brownell dev_err(dev, "%s error %d\n", "alarm read", status); 100288e75cc3SDavid Brownell return -EIO; 100388e75cc3SDavid Brownell } 100488e75cc3SDavid Brownell 100588e75cc3SDavid Brownell menelaus_to_time(regs, &w->time); 100688e75cc3SDavid Brownell 100788e75cc3SDavid Brownell w->enabled = !!(the_menelaus->rtc_control & RTC_CTRL_AL_EN); 100888e75cc3SDavid Brownell 100988e75cc3SDavid Brownell /* NOTE we *could* check if actually pending... */ 101088e75cc3SDavid Brownell w->pending = 0; 101188e75cc3SDavid Brownell 101288e75cc3SDavid Brownell return 0; 101388e75cc3SDavid Brownell } 101488e75cc3SDavid Brownell 101588e75cc3SDavid Brownell static int menelaus_set_alarm(struct device *dev, struct rtc_wkalrm *w) 101688e75cc3SDavid Brownell { 101788e75cc3SDavid Brownell int status; 101888e75cc3SDavid Brownell 101988e75cc3SDavid Brownell if (the_menelaus->client->irq <= 0 && w->enabled) 102088e75cc3SDavid Brownell return -ENODEV; 102188e75cc3SDavid Brownell 102288e75cc3SDavid Brownell /* clear previous alarm enable */ 102388e75cc3SDavid Brownell if (the_menelaus->rtc_control & RTC_CTRL_AL_EN) { 102488e75cc3SDavid Brownell the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN; 102588e75cc3SDavid Brownell status = menelaus_write_reg(MENELAUS_RTC_CTRL, 102688e75cc3SDavid Brownell the_menelaus->rtc_control); 102788e75cc3SDavid Brownell if (status < 0) 102888e75cc3SDavid Brownell return status; 102988e75cc3SDavid Brownell } 103088e75cc3SDavid Brownell 103188e75cc3SDavid Brownell /* write alarm registers */ 103288e75cc3SDavid Brownell status = time_to_menelaus(&w->time, MENELAUS_RTC_AL_SEC); 103388e75cc3SDavid Brownell if (status < 0) 103488e75cc3SDavid Brownell return status; 103588e75cc3SDavid Brownell 103688e75cc3SDavid Brownell /* enable alarm if requested */ 103788e75cc3SDavid Brownell if (w->enabled) { 103888e75cc3SDavid Brownell the_menelaus->rtc_control |= RTC_CTRL_AL_EN; 103988e75cc3SDavid Brownell status = menelaus_write_reg(MENELAUS_RTC_CTRL, 104088e75cc3SDavid Brownell the_menelaus->rtc_control); 104188e75cc3SDavid Brownell } 104288e75cc3SDavid Brownell 104388e75cc3SDavid Brownell return status; 104488e75cc3SDavid Brownell } 104588e75cc3SDavid Brownell 104688e75cc3SDavid Brownell #ifdef CONFIG_RTC_INTF_DEV 104788e75cc3SDavid Brownell 104888e75cc3SDavid Brownell static void menelaus_rtc_update_work(struct menelaus_chip *m) 104988e75cc3SDavid Brownell { 105088e75cc3SDavid Brownell /* report 1/sec update */ 105188e75cc3SDavid Brownell local_irq_disable(); 105288e75cc3SDavid Brownell rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_UF); 105388e75cc3SDavid Brownell local_irq_enable(); 105488e75cc3SDavid Brownell } 105588e75cc3SDavid Brownell 105688e75cc3SDavid Brownell static int menelaus_ioctl(struct device *dev, unsigned cmd, unsigned long arg) 105788e75cc3SDavid Brownell { 105888e75cc3SDavid Brownell int status; 105988e75cc3SDavid Brownell 106088e75cc3SDavid Brownell if (the_menelaus->client->irq <= 0) 106188e75cc3SDavid Brownell return -ENOIOCTLCMD; 106288e75cc3SDavid Brownell 106388e75cc3SDavid Brownell switch (cmd) { 106488e75cc3SDavid Brownell /* alarm IRQ */ 106588e75cc3SDavid Brownell case RTC_AIE_ON: 106688e75cc3SDavid Brownell if (the_menelaus->rtc_control & RTC_CTRL_AL_EN) 106788e75cc3SDavid Brownell return 0; 106888e75cc3SDavid Brownell the_menelaus->rtc_control |= RTC_CTRL_AL_EN; 106988e75cc3SDavid Brownell break; 107088e75cc3SDavid Brownell case RTC_AIE_OFF: 107188e75cc3SDavid Brownell if (!(the_menelaus->rtc_control & RTC_CTRL_AL_EN)) 107288e75cc3SDavid Brownell return 0; 107388e75cc3SDavid Brownell the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN; 107488e75cc3SDavid Brownell break; 107588e75cc3SDavid Brownell /* 1/second "update" IRQ */ 107688e75cc3SDavid Brownell case RTC_UIE_ON: 107788e75cc3SDavid Brownell if (the_menelaus->uie) 107888e75cc3SDavid Brownell return 0; 107988e75cc3SDavid Brownell status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ); 108088e75cc3SDavid Brownell status = menelaus_add_irq_work(MENELAUS_RTCTMR_IRQ, 108188e75cc3SDavid Brownell menelaus_rtc_update_work); 108288e75cc3SDavid Brownell if (status == 0) 108388e75cc3SDavid Brownell the_menelaus->uie = 1; 108488e75cc3SDavid Brownell return status; 108588e75cc3SDavid Brownell case RTC_UIE_OFF: 108688e75cc3SDavid Brownell if (!the_menelaus->uie) 108788e75cc3SDavid Brownell return 0; 108888e75cc3SDavid Brownell status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ); 108988e75cc3SDavid Brownell if (status == 0) 109088e75cc3SDavid Brownell the_menelaus->uie = 0; 109188e75cc3SDavid Brownell return status; 109288e75cc3SDavid Brownell default: 109388e75cc3SDavid Brownell return -ENOIOCTLCMD; 109488e75cc3SDavid Brownell } 109588e75cc3SDavid Brownell return menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control); 109688e75cc3SDavid Brownell } 109788e75cc3SDavid Brownell 109888e75cc3SDavid Brownell #else 109988e75cc3SDavid Brownell #define menelaus_ioctl NULL 110088e75cc3SDavid Brownell #endif 110188e75cc3SDavid Brownell 110288e75cc3SDavid Brownell /* REVISIT no compensation register support ... */ 110388e75cc3SDavid Brownell 110488e75cc3SDavid Brownell static const struct rtc_class_ops menelaus_rtc_ops = { 110588e75cc3SDavid Brownell .ioctl = menelaus_ioctl, 110688e75cc3SDavid Brownell .read_time = menelaus_read_time, 110788e75cc3SDavid Brownell .set_time = menelaus_set_time, 110888e75cc3SDavid Brownell .read_alarm = menelaus_read_alarm, 110988e75cc3SDavid Brownell .set_alarm = menelaus_set_alarm, 111088e75cc3SDavid Brownell }; 111188e75cc3SDavid Brownell 111288e75cc3SDavid Brownell static void menelaus_rtc_alarm_work(struct menelaus_chip *m) 111388e75cc3SDavid Brownell { 111488e75cc3SDavid Brownell /* report alarm */ 111588e75cc3SDavid Brownell local_irq_disable(); 111688e75cc3SDavid Brownell rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_AF); 111788e75cc3SDavid Brownell local_irq_enable(); 111888e75cc3SDavid Brownell 111988e75cc3SDavid Brownell /* then disable it; alarms are oneshot */ 112088e75cc3SDavid Brownell the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN; 112188e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control); 112288e75cc3SDavid Brownell } 112388e75cc3SDavid Brownell 112488e75cc3SDavid Brownell static inline void menelaus_rtc_init(struct menelaus_chip *m) 112588e75cc3SDavid Brownell { 112688e75cc3SDavid Brownell int alarm = (m->client->irq > 0); 112788e75cc3SDavid Brownell 112888e75cc3SDavid Brownell /* assume 32KDETEN pin is pulled high */ 112988e75cc3SDavid Brownell if (!(menelaus_read_reg(MENELAUS_OSC_CTRL) & 0x80)) { 113088e75cc3SDavid Brownell dev_dbg(&m->client->dev, "no 32k oscillator\n"); 113188e75cc3SDavid Brownell return; 113288e75cc3SDavid Brownell } 113388e75cc3SDavid Brownell 113488e75cc3SDavid Brownell /* support RTC alarm; it can issue wakeups */ 113588e75cc3SDavid Brownell if (alarm) { 113688e75cc3SDavid Brownell if (menelaus_add_irq_work(MENELAUS_RTCALM_IRQ, 113788e75cc3SDavid Brownell menelaus_rtc_alarm_work) < 0) { 113888e75cc3SDavid Brownell dev_err(&m->client->dev, "can't handle RTC alarm\n"); 113988e75cc3SDavid Brownell return; 114088e75cc3SDavid Brownell } 114188e75cc3SDavid Brownell device_init_wakeup(&m->client->dev, 1); 114288e75cc3SDavid Brownell } 114388e75cc3SDavid Brownell 114488e75cc3SDavid Brownell /* be sure RTC is enabled; allow 1/sec irqs; leave 12hr mode alone */ 114588e75cc3SDavid Brownell m->rtc_control = menelaus_read_reg(MENELAUS_RTC_CTRL); 114688e75cc3SDavid Brownell if (!(m->rtc_control & RTC_CTRL_RTC_EN) 114788e75cc3SDavid Brownell || (m->rtc_control & RTC_CTRL_AL_EN) 114888e75cc3SDavid Brownell || (m->rtc_control & RTC_CTRL_EVERY_MASK)) { 114988e75cc3SDavid Brownell if (!(m->rtc_control & RTC_CTRL_RTC_EN)) { 115088e75cc3SDavid Brownell dev_warn(&m->client->dev, "rtc clock needs setting\n"); 115188e75cc3SDavid Brownell m->rtc_control |= RTC_CTRL_RTC_EN; 115288e75cc3SDavid Brownell } 115388e75cc3SDavid Brownell m->rtc_control &= ~RTC_CTRL_EVERY_MASK; 115488e75cc3SDavid Brownell m->rtc_control &= ~RTC_CTRL_AL_EN; 115588e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_RTC_CTRL, m->rtc_control); 115688e75cc3SDavid Brownell } 115788e75cc3SDavid Brownell 115888e75cc3SDavid Brownell m->rtc = rtc_device_register(DRIVER_NAME, 115988e75cc3SDavid Brownell &m->client->dev, 116088e75cc3SDavid Brownell &menelaus_rtc_ops, THIS_MODULE); 116188e75cc3SDavid Brownell if (IS_ERR(m->rtc)) { 116288e75cc3SDavid Brownell if (alarm) { 116388e75cc3SDavid Brownell menelaus_remove_irq_work(MENELAUS_RTCALM_IRQ); 116488e75cc3SDavid Brownell device_init_wakeup(&m->client->dev, 0); 116588e75cc3SDavid Brownell } 116688e75cc3SDavid Brownell dev_err(&m->client->dev, "can't register RTC: %d\n", 116788e75cc3SDavid Brownell (int) PTR_ERR(m->rtc)); 116888e75cc3SDavid Brownell the_menelaus->rtc = NULL; 116988e75cc3SDavid Brownell } 117088e75cc3SDavid Brownell } 117188e75cc3SDavid Brownell 117288e75cc3SDavid Brownell #else 117388e75cc3SDavid Brownell 117488e75cc3SDavid Brownell static inline void menelaus_rtc_init(struct menelaus_chip *m) 117588e75cc3SDavid Brownell { 117688e75cc3SDavid Brownell /* nothing */ 117788e75cc3SDavid Brownell } 117888e75cc3SDavid Brownell 117988e75cc3SDavid Brownell #endif 118088e75cc3SDavid Brownell 118188e75cc3SDavid Brownell /*-----------------------------------------------------------------------*/ 118288e75cc3SDavid Brownell 118388e75cc3SDavid Brownell static struct i2c_driver menelaus_i2c_driver; 118488e75cc3SDavid Brownell 118588e75cc3SDavid Brownell static int menelaus_probe(struct i2c_client *client, 118688e75cc3SDavid Brownell const struct i2c_device_id *id) 118788e75cc3SDavid Brownell { 118888e75cc3SDavid Brownell struct menelaus_chip *menelaus; 118988e75cc3SDavid Brownell int rev = 0, val; 119088e75cc3SDavid Brownell int err = 0; 119188e75cc3SDavid Brownell struct menelaus_platform_data *menelaus_pdata = 119288e75cc3SDavid Brownell client->dev.platform_data; 119388e75cc3SDavid Brownell 119488e75cc3SDavid Brownell if (the_menelaus) { 119588e75cc3SDavid Brownell dev_dbg(&client->dev, "only one %s for now\n", 119688e75cc3SDavid Brownell DRIVER_NAME); 119788e75cc3SDavid Brownell return -ENODEV; 119888e75cc3SDavid Brownell } 119988e75cc3SDavid Brownell 120088e75cc3SDavid Brownell menelaus = kzalloc(sizeof *menelaus, GFP_KERNEL); 120188e75cc3SDavid Brownell if (!menelaus) 120288e75cc3SDavid Brownell return -ENOMEM; 120388e75cc3SDavid Brownell 120488e75cc3SDavid Brownell i2c_set_clientdata(client, menelaus); 120588e75cc3SDavid Brownell 120688e75cc3SDavid Brownell the_menelaus = menelaus; 120788e75cc3SDavid Brownell menelaus->client = client; 120888e75cc3SDavid Brownell 120988e75cc3SDavid Brownell /* If a true probe check the device */ 121088e75cc3SDavid Brownell rev = menelaus_read_reg(MENELAUS_REV); 121188e75cc3SDavid Brownell if (rev < 0) { 121288e75cc3SDavid Brownell pr_err(DRIVER_NAME ": device not found"); 121388e75cc3SDavid Brownell err = -ENODEV; 121488e75cc3SDavid Brownell goto fail1; 121588e75cc3SDavid Brownell } 121688e75cc3SDavid Brownell 121788e75cc3SDavid Brownell /* Ack and disable all Menelaus interrupts */ 121888e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_INT_ACK1, 0xff); 121988e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_INT_ACK2, 0xff); 122088e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_INT_MASK1, 0xff); 122188e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_INT_MASK2, 0xff); 122288e75cc3SDavid Brownell menelaus->mask1 = 0xff; 122388e75cc3SDavid Brownell menelaus->mask2 = 0xff; 122488e75cc3SDavid Brownell 122588e75cc3SDavid Brownell /* Set output buffer strengths */ 122688e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_MCT_CTRL1, 0x73); 122788e75cc3SDavid Brownell 122888e75cc3SDavid Brownell if (client->irq > 0) { 1229f742b96eSYong Zhang err = request_irq(client->irq, menelaus_irq, 0, 123088e75cc3SDavid Brownell DRIVER_NAME, menelaus); 123188e75cc3SDavid Brownell if (err) { 123288e75cc3SDavid Brownell dev_dbg(&client->dev, "can't get IRQ %d, err %d\n", 123388e75cc3SDavid Brownell client->irq, err); 123488e75cc3SDavid Brownell goto fail1; 123588e75cc3SDavid Brownell } 123688e75cc3SDavid Brownell } 123788e75cc3SDavid Brownell 123888e75cc3SDavid Brownell mutex_init(&menelaus->lock); 123988e75cc3SDavid Brownell INIT_WORK(&menelaus->work, menelaus_work); 124088e75cc3SDavid Brownell 124188e75cc3SDavid Brownell pr_info("Menelaus rev %d.%d\n", rev >> 4, rev & 0x0f); 124288e75cc3SDavid Brownell 124388e75cc3SDavid Brownell val = menelaus_read_reg(MENELAUS_VCORE_CTRL1); 124488e75cc3SDavid Brownell if (val < 0) 124588e75cc3SDavid Brownell goto fail2; 124688e75cc3SDavid Brownell if (val & (1 << 7)) 124788e75cc3SDavid Brownell menelaus->vcore_hw_mode = 1; 124888e75cc3SDavid Brownell else 124988e75cc3SDavid Brownell menelaus->vcore_hw_mode = 0; 125088e75cc3SDavid Brownell 125188e75cc3SDavid Brownell if (menelaus_pdata != NULL && menelaus_pdata->late_init != NULL) { 125288e75cc3SDavid Brownell err = menelaus_pdata->late_init(&client->dev); 125388e75cc3SDavid Brownell if (err < 0) 125488e75cc3SDavid Brownell goto fail2; 125588e75cc3SDavid Brownell } 125688e75cc3SDavid Brownell 125788e75cc3SDavid Brownell menelaus_rtc_init(menelaus); 125888e75cc3SDavid Brownell 125988e75cc3SDavid Brownell return 0; 126088e75cc3SDavid Brownell fail2: 126188e75cc3SDavid Brownell free_irq(client->irq, menelaus); 1262*43829731STejun Heo flush_work(&menelaus->work); 126388e75cc3SDavid Brownell fail1: 126488e75cc3SDavid Brownell kfree(menelaus); 126588e75cc3SDavid Brownell return err; 126688e75cc3SDavid Brownell } 126788e75cc3SDavid Brownell 126888e75cc3SDavid Brownell static int __exit menelaus_remove(struct i2c_client *client) 126988e75cc3SDavid Brownell { 127088e75cc3SDavid Brownell struct menelaus_chip *menelaus = i2c_get_clientdata(client); 127188e75cc3SDavid Brownell 127288e75cc3SDavid Brownell free_irq(client->irq, menelaus); 1273*43829731STejun Heo flush_work(&menelaus->work); 1274f322d5f0SWolfram Sang kfree(menelaus); 127588e75cc3SDavid Brownell the_menelaus = NULL; 127688e75cc3SDavid Brownell return 0; 127788e75cc3SDavid Brownell } 127888e75cc3SDavid Brownell 127988e75cc3SDavid Brownell static const struct i2c_device_id menelaus_id[] = { 128088e75cc3SDavid Brownell { "menelaus", 0 }, 128188e75cc3SDavid Brownell { } 128288e75cc3SDavid Brownell }; 128388e75cc3SDavid Brownell MODULE_DEVICE_TABLE(i2c, menelaus_id); 128488e75cc3SDavid Brownell 128588e75cc3SDavid Brownell static struct i2c_driver menelaus_i2c_driver = { 128688e75cc3SDavid Brownell .driver = { 128788e75cc3SDavid Brownell .name = DRIVER_NAME, 128888e75cc3SDavid Brownell }, 128988e75cc3SDavid Brownell .probe = menelaus_probe, 129088e75cc3SDavid Brownell .remove = __exit_p(menelaus_remove), 129188e75cc3SDavid Brownell .id_table = menelaus_id, 129288e75cc3SDavid Brownell }; 129388e75cc3SDavid Brownell 129488e75cc3SDavid Brownell static int __init menelaus_init(void) 129588e75cc3SDavid Brownell { 129688e75cc3SDavid Brownell int res; 129788e75cc3SDavid Brownell 129888e75cc3SDavid Brownell res = i2c_add_driver(&menelaus_i2c_driver); 129988e75cc3SDavid Brownell if (res < 0) { 130088e75cc3SDavid Brownell pr_err(DRIVER_NAME ": driver registration failed\n"); 130188e75cc3SDavid Brownell return res; 130288e75cc3SDavid Brownell } 130388e75cc3SDavid Brownell 130488e75cc3SDavid Brownell return 0; 130588e75cc3SDavid Brownell } 130688e75cc3SDavid Brownell 130788e75cc3SDavid Brownell static void __exit menelaus_exit(void) 130888e75cc3SDavid Brownell { 130988e75cc3SDavid Brownell i2c_del_driver(&menelaus_i2c_driver); 131088e75cc3SDavid Brownell 131188e75cc3SDavid Brownell /* FIXME: Shutdown menelaus parts that can be shut down */ 131288e75cc3SDavid Brownell } 131388e75cc3SDavid Brownell 131488e75cc3SDavid Brownell MODULE_AUTHOR("Texas Instruments, Inc. (and others)"); 131588e75cc3SDavid Brownell MODULE_DESCRIPTION("I2C interface for Menelaus."); 131688e75cc3SDavid Brownell MODULE_LICENSE("GPL"); 131788e75cc3SDavid Brownell 131888e75cc3SDavid Brownell module_init(menelaus_init); 131988e75cc3SDavid Brownell module_exit(menelaus_exit); 1320