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