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> 43*5a0e3ad6STejun Heo #include <linux/slab.h> 4488e75cc3SDavid Brownell 4588e75cc3SDavid Brownell #include <asm/mach/irq.h> 4688e75cc3SDavid Brownell 4788e75cc3SDavid Brownell #include <mach/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 13188e75cc3SDavid Brownell static void menelaus_work(struct work_struct *_menelaus); 13288e75cc3SDavid Brownell 13388e75cc3SDavid Brownell struct menelaus_chip { 13488e75cc3SDavid Brownell struct mutex lock; 13588e75cc3SDavid Brownell struct i2c_client *client; 13688e75cc3SDavid Brownell struct work_struct work; 13788e75cc3SDavid Brownell #ifdef CONFIG_RTC_DRV_TWL92330 13888e75cc3SDavid Brownell struct rtc_device *rtc; 13988e75cc3SDavid Brownell u8 rtc_control; 14088e75cc3SDavid Brownell unsigned uie:1; 14188e75cc3SDavid Brownell #endif 14288e75cc3SDavid Brownell unsigned vcore_hw_mode:1; 14388e75cc3SDavid Brownell u8 mask1, mask2; 14488e75cc3SDavid Brownell void (*handlers[16])(struct menelaus_chip *); 14588e75cc3SDavid Brownell void (*mmc_callback)(void *data, u8 mask); 14688e75cc3SDavid Brownell void *mmc_callback_data; 14788e75cc3SDavid Brownell }; 14888e75cc3SDavid Brownell 14988e75cc3SDavid Brownell static struct menelaus_chip *the_menelaus; 15088e75cc3SDavid Brownell 15188e75cc3SDavid Brownell static int menelaus_write_reg(int reg, u8 value) 15288e75cc3SDavid Brownell { 15388e75cc3SDavid Brownell int val = i2c_smbus_write_byte_data(the_menelaus->client, reg, value); 15488e75cc3SDavid Brownell 15588e75cc3SDavid Brownell if (val < 0) { 15688e75cc3SDavid Brownell pr_err(DRIVER_NAME ": write error"); 15788e75cc3SDavid Brownell return val; 15888e75cc3SDavid Brownell } 15988e75cc3SDavid Brownell 16088e75cc3SDavid Brownell return 0; 16188e75cc3SDavid Brownell } 16288e75cc3SDavid Brownell 16388e75cc3SDavid Brownell static int menelaus_read_reg(int reg) 16488e75cc3SDavid Brownell { 16588e75cc3SDavid Brownell int val = i2c_smbus_read_byte_data(the_menelaus->client, reg); 16688e75cc3SDavid Brownell 16788e75cc3SDavid Brownell if (val < 0) 16888e75cc3SDavid Brownell pr_err(DRIVER_NAME ": read error"); 16988e75cc3SDavid Brownell 17088e75cc3SDavid Brownell return val; 17188e75cc3SDavid Brownell } 17288e75cc3SDavid Brownell 17388e75cc3SDavid Brownell static int menelaus_enable_irq(int irq) 17488e75cc3SDavid Brownell { 17588e75cc3SDavid Brownell if (irq > 7) { 17688e75cc3SDavid Brownell irq -= 8; 17788e75cc3SDavid Brownell the_menelaus->mask2 &= ~(1 << irq); 17888e75cc3SDavid Brownell return menelaus_write_reg(MENELAUS_INT_MASK2, 17988e75cc3SDavid Brownell the_menelaus->mask2); 18088e75cc3SDavid Brownell } else { 18188e75cc3SDavid Brownell the_menelaus->mask1 &= ~(1 << irq); 18288e75cc3SDavid Brownell return menelaus_write_reg(MENELAUS_INT_MASK1, 18388e75cc3SDavid Brownell the_menelaus->mask1); 18488e75cc3SDavid Brownell } 18588e75cc3SDavid Brownell } 18688e75cc3SDavid Brownell 18788e75cc3SDavid Brownell static int menelaus_disable_irq(int irq) 18888e75cc3SDavid Brownell { 18988e75cc3SDavid Brownell if (irq > 7) { 19088e75cc3SDavid Brownell irq -= 8; 19188e75cc3SDavid Brownell the_menelaus->mask2 |= (1 << irq); 19288e75cc3SDavid Brownell return menelaus_write_reg(MENELAUS_INT_MASK2, 19388e75cc3SDavid Brownell the_menelaus->mask2); 19488e75cc3SDavid Brownell } else { 19588e75cc3SDavid Brownell the_menelaus->mask1 |= (1 << irq); 19688e75cc3SDavid Brownell return menelaus_write_reg(MENELAUS_INT_MASK1, 19788e75cc3SDavid Brownell the_menelaus->mask1); 19888e75cc3SDavid Brownell } 19988e75cc3SDavid Brownell } 20088e75cc3SDavid Brownell 20188e75cc3SDavid Brownell static int menelaus_ack_irq(int irq) 20288e75cc3SDavid Brownell { 20388e75cc3SDavid Brownell if (irq > 7) 20488e75cc3SDavid Brownell return menelaus_write_reg(MENELAUS_INT_ACK2, 1 << (irq - 8)); 20588e75cc3SDavid Brownell else 20688e75cc3SDavid Brownell return menelaus_write_reg(MENELAUS_INT_ACK1, 1 << irq); 20788e75cc3SDavid Brownell } 20888e75cc3SDavid Brownell 20988e75cc3SDavid Brownell /* Adds a handler for an interrupt. Does not run in interrupt context */ 21088e75cc3SDavid Brownell static int menelaus_add_irq_work(int irq, 21188e75cc3SDavid Brownell void (*handler)(struct menelaus_chip *)) 21288e75cc3SDavid Brownell { 21388e75cc3SDavid Brownell int ret = 0; 21488e75cc3SDavid Brownell 21588e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 21688e75cc3SDavid Brownell the_menelaus->handlers[irq] = handler; 21788e75cc3SDavid Brownell ret = menelaus_enable_irq(irq); 21888e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 21988e75cc3SDavid Brownell 22088e75cc3SDavid Brownell return ret; 22188e75cc3SDavid Brownell } 22288e75cc3SDavid Brownell 22388e75cc3SDavid Brownell /* Removes handler for an interrupt */ 22488e75cc3SDavid Brownell static int menelaus_remove_irq_work(int irq) 22588e75cc3SDavid Brownell { 22688e75cc3SDavid Brownell int ret = 0; 22788e75cc3SDavid Brownell 22888e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 22988e75cc3SDavid Brownell ret = menelaus_disable_irq(irq); 23088e75cc3SDavid Brownell the_menelaus->handlers[irq] = NULL; 23188e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 23288e75cc3SDavid Brownell 23388e75cc3SDavid Brownell return ret; 23488e75cc3SDavid Brownell } 23588e75cc3SDavid Brownell 23688e75cc3SDavid Brownell /* 23788e75cc3SDavid Brownell * Gets scheduled when a card detect interrupt happens. Note that in some cases 23888e75cc3SDavid Brownell * this line is wired to card cover switch rather than the card detect switch 23988e75cc3SDavid Brownell * in each slot. In this case the cards are not seen by menelaus. 24088e75cc3SDavid Brownell * FIXME: Add handling for D1 too 24188e75cc3SDavid Brownell */ 24288e75cc3SDavid Brownell static void menelaus_mmc_cd_work(struct menelaus_chip *menelaus_hw) 24388e75cc3SDavid Brownell { 24488e75cc3SDavid Brownell int reg; 24588e75cc3SDavid Brownell unsigned char card_mask = 0; 24688e75cc3SDavid Brownell 24788e75cc3SDavid Brownell reg = menelaus_read_reg(MENELAUS_MCT_PIN_ST); 24888e75cc3SDavid Brownell if (reg < 0) 24988e75cc3SDavid Brownell return; 25088e75cc3SDavid Brownell 25188e75cc3SDavid Brownell if (!(reg & 0x1)) 25288e75cc3SDavid Brownell card_mask |= (1 << 0); 25388e75cc3SDavid Brownell 25488e75cc3SDavid Brownell if (!(reg & 0x2)) 25588e75cc3SDavid Brownell card_mask |= (1 << 1); 25688e75cc3SDavid Brownell 25788e75cc3SDavid Brownell if (menelaus_hw->mmc_callback) 25888e75cc3SDavid Brownell menelaus_hw->mmc_callback(menelaus_hw->mmc_callback_data, 25988e75cc3SDavid Brownell card_mask); 26088e75cc3SDavid Brownell } 26188e75cc3SDavid Brownell 26288e75cc3SDavid Brownell /* 26388e75cc3SDavid Brownell * Toggles the MMC slots between open-drain and push-pull mode. 26488e75cc3SDavid Brownell */ 26588e75cc3SDavid Brownell int menelaus_set_mmc_opendrain(int slot, int enable) 26688e75cc3SDavid Brownell { 26788e75cc3SDavid Brownell int ret, val; 26888e75cc3SDavid Brownell 26988e75cc3SDavid Brownell if (slot != 1 && slot != 2) 27088e75cc3SDavid Brownell return -EINVAL; 27188e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 27288e75cc3SDavid Brownell ret = menelaus_read_reg(MENELAUS_MCT_CTRL1); 27388e75cc3SDavid Brownell if (ret < 0) { 27488e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 27588e75cc3SDavid Brownell return ret; 27688e75cc3SDavid Brownell } 27788e75cc3SDavid Brownell val = ret; 27888e75cc3SDavid Brownell if (slot == 1) { 27988e75cc3SDavid Brownell if (enable) 28088e75cc3SDavid Brownell val |= 1 << 2; 28188e75cc3SDavid Brownell else 28288e75cc3SDavid Brownell val &= ~(1 << 2); 28388e75cc3SDavid Brownell } else { 28488e75cc3SDavid Brownell if (enable) 28588e75cc3SDavid Brownell val |= 1 << 3; 28688e75cc3SDavid Brownell else 28788e75cc3SDavid Brownell val &= ~(1 << 3); 28888e75cc3SDavid Brownell } 28988e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_MCT_CTRL1, val); 29088e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 29188e75cc3SDavid Brownell 29288e75cc3SDavid Brownell return ret; 29388e75cc3SDavid Brownell } 29488e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_mmc_opendrain); 29588e75cc3SDavid Brownell 29688e75cc3SDavid Brownell int menelaus_set_slot_sel(int enable) 29788e75cc3SDavid Brownell { 29888e75cc3SDavid Brownell int ret; 29988e75cc3SDavid Brownell 30088e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 30188e75cc3SDavid Brownell ret = menelaus_read_reg(MENELAUS_GPIO_CTRL); 30288e75cc3SDavid Brownell if (ret < 0) 30388e75cc3SDavid Brownell goto out; 30488e75cc3SDavid Brownell ret |= 0x02; 30588e75cc3SDavid Brownell if (enable) 30688e75cc3SDavid Brownell ret |= 1 << 5; 30788e75cc3SDavid Brownell else 30888e75cc3SDavid Brownell ret &= ~(1 << 5); 30988e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret); 31088e75cc3SDavid Brownell out: 31188e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 31288e75cc3SDavid Brownell return ret; 31388e75cc3SDavid Brownell } 31488e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_slot_sel); 31588e75cc3SDavid Brownell 31688e75cc3SDavid Brownell int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_en) 31788e75cc3SDavid Brownell { 31888e75cc3SDavid Brownell int ret, val; 31988e75cc3SDavid Brownell 32088e75cc3SDavid Brownell if (slot != 1 && slot != 2) 32188e75cc3SDavid Brownell return -EINVAL; 32288e75cc3SDavid Brownell if (power >= 3) 32388e75cc3SDavid Brownell return -EINVAL; 32488e75cc3SDavid Brownell 32588e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 32688e75cc3SDavid Brownell 32788e75cc3SDavid Brownell ret = menelaus_read_reg(MENELAUS_MCT_CTRL2); 32888e75cc3SDavid Brownell if (ret < 0) 32988e75cc3SDavid Brownell goto out; 33088e75cc3SDavid Brownell val = ret; 33188e75cc3SDavid Brownell if (slot == 1) { 33288e75cc3SDavid Brownell if (cd_en) 33388e75cc3SDavid Brownell val |= (1 << 4) | (1 << 6); 33488e75cc3SDavid Brownell else 33588e75cc3SDavid Brownell val &= ~((1 << 4) | (1 << 6)); 33688e75cc3SDavid Brownell } else { 33788e75cc3SDavid Brownell if (cd_en) 33888e75cc3SDavid Brownell val |= (1 << 5) | (1 << 7); 33988e75cc3SDavid Brownell else 34088e75cc3SDavid Brownell val &= ~((1 << 5) | (1 << 7)); 34188e75cc3SDavid Brownell } 34288e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, val); 34388e75cc3SDavid Brownell if (ret < 0) 34488e75cc3SDavid Brownell goto out; 34588e75cc3SDavid Brownell 34688e75cc3SDavid Brownell ret = menelaus_read_reg(MENELAUS_MCT_CTRL3); 34788e75cc3SDavid Brownell if (ret < 0) 34888e75cc3SDavid Brownell goto out; 34988e75cc3SDavid Brownell val = ret; 35088e75cc3SDavid Brownell if (slot == 1) { 35188e75cc3SDavid Brownell if (enable) 35288e75cc3SDavid Brownell val |= 1 << 0; 35388e75cc3SDavid Brownell else 35488e75cc3SDavid Brownell val &= ~(1 << 0); 35588e75cc3SDavid Brownell } else { 35688e75cc3SDavid Brownell int b; 35788e75cc3SDavid Brownell 35888e75cc3SDavid Brownell if (enable) 35988e75cc3SDavid Brownell ret |= 1 << 1; 36088e75cc3SDavid Brownell else 36188e75cc3SDavid Brownell ret &= ~(1 << 1); 36288e75cc3SDavid Brownell b = menelaus_read_reg(MENELAUS_MCT_CTRL2); 36388e75cc3SDavid Brownell b &= ~0x03; 36488e75cc3SDavid Brownell b |= power; 36588e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, b); 36688e75cc3SDavid Brownell if (ret < 0) 36788e75cc3SDavid Brownell goto out; 36888e75cc3SDavid Brownell } 36988e75cc3SDavid Brownell /* Disable autonomous shutdown */ 37088e75cc3SDavid Brownell val &= ~(0x03 << 2); 37188e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_MCT_CTRL3, val); 37288e75cc3SDavid Brownell out: 37388e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 37488e75cc3SDavid Brownell return ret; 37588e75cc3SDavid Brownell } 37688e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_mmc_slot); 37788e75cc3SDavid Brownell 37888e75cc3SDavid Brownell int menelaus_register_mmc_callback(void (*callback)(void *data, u8 card_mask), 37988e75cc3SDavid Brownell void *data) 38088e75cc3SDavid Brownell { 38188e75cc3SDavid Brownell int ret = 0; 38288e75cc3SDavid Brownell 38388e75cc3SDavid Brownell the_menelaus->mmc_callback_data = data; 38488e75cc3SDavid Brownell the_menelaus->mmc_callback = callback; 38588e75cc3SDavid Brownell ret = menelaus_add_irq_work(MENELAUS_MMC_S1CD_IRQ, 38688e75cc3SDavid Brownell menelaus_mmc_cd_work); 38788e75cc3SDavid Brownell if (ret < 0) 38888e75cc3SDavid Brownell return ret; 38988e75cc3SDavid Brownell ret = menelaus_add_irq_work(MENELAUS_MMC_S2CD_IRQ, 39088e75cc3SDavid Brownell menelaus_mmc_cd_work); 39188e75cc3SDavid Brownell if (ret < 0) 39288e75cc3SDavid Brownell return ret; 39388e75cc3SDavid Brownell ret = menelaus_add_irq_work(MENELAUS_MMC_S1D1_IRQ, 39488e75cc3SDavid Brownell menelaus_mmc_cd_work); 39588e75cc3SDavid Brownell if (ret < 0) 39688e75cc3SDavid Brownell return ret; 39788e75cc3SDavid Brownell ret = menelaus_add_irq_work(MENELAUS_MMC_S2D1_IRQ, 39888e75cc3SDavid Brownell menelaus_mmc_cd_work); 39988e75cc3SDavid Brownell 40088e75cc3SDavid Brownell return ret; 40188e75cc3SDavid Brownell } 40288e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_register_mmc_callback); 40388e75cc3SDavid Brownell 40488e75cc3SDavid Brownell void menelaus_unregister_mmc_callback(void) 40588e75cc3SDavid Brownell { 40688e75cc3SDavid Brownell menelaus_remove_irq_work(MENELAUS_MMC_S1CD_IRQ); 40788e75cc3SDavid Brownell menelaus_remove_irq_work(MENELAUS_MMC_S2CD_IRQ); 40888e75cc3SDavid Brownell menelaus_remove_irq_work(MENELAUS_MMC_S1D1_IRQ); 40988e75cc3SDavid Brownell menelaus_remove_irq_work(MENELAUS_MMC_S2D1_IRQ); 41088e75cc3SDavid Brownell 41188e75cc3SDavid Brownell the_menelaus->mmc_callback = NULL; 41288e75cc3SDavid Brownell the_menelaus->mmc_callback_data = 0; 41388e75cc3SDavid Brownell } 41488e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_unregister_mmc_callback); 41588e75cc3SDavid Brownell 41688e75cc3SDavid Brownell struct menelaus_vtg { 41788e75cc3SDavid Brownell const char *name; 41888e75cc3SDavid Brownell u8 vtg_reg; 41988e75cc3SDavid Brownell u8 vtg_shift; 42088e75cc3SDavid Brownell u8 vtg_bits; 42188e75cc3SDavid Brownell u8 mode_reg; 42288e75cc3SDavid Brownell }; 42388e75cc3SDavid Brownell 42488e75cc3SDavid Brownell struct menelaus_vtg_value { 42588e75cc3SDavid Brownell u16 vtg; 42688e75cc3SDavid Brownell u16 val; 42788e75cc3SDavid Brownell }; 42888e75cc3SDavid Brownell 42988e75cc3SDavid Brownell static int menelaus_set_voltage(const struct menelaus_vtg *vtg, int mV, 43088e75cc3SDavid Brownell int vtg_val, int mode) 43188e75cc3SDavid Brownell { 43288e75cc3SDavid Brownell int val, ret; 43388e75cc3SDavid Brownell struct i2c_client *c = the_menelaus->client; 43488e75cc3SDavid Brownell 43588e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 43688e75cc3SDavid Brownell if (vtg == 0) 43788e75cc3SDavid Brownell goto set_voltage; 43888e75cc3SDavid Brownell 43988e75cc3SDavid Brownell ret = menelaus_read_reg(vtg->vtg_reg); 44088e75cc3SDavid Brownell if (ret < 0) 44188e75cc3SDavid Brownell goto out; 44288e75cc3SDavid Brownell val = ret & ~(((1 << vtg->vtg_bits) - 1) << vtg->vtg_shift); 44388e75cc3SDavid Brownell val |= vtg_val << vtg->vtg_shift; 44488e75cc3SDavid Brownell 44588e75cc3SDavid Brownell dev_dbg(&c->dev, "Setting voltage '%s'" 44688e75cc3SDavid Brownell "to %d mV (reg 0x%02x, val 0x%02x)\n", 44788e75cc3SDavid Brownell vtg->name, mV, vtg->vtg_reg, val); 44888e75cc3SDavid Brownell 44988e75cc3SDavid Brownell ret = menelaus_write_reg(vtg->vtg_reg, val); 45088e75cc3SDavid Brownell if (ret < 0) 45188e75cc3SDavid Brownell goto out; 45288e75cc3SDavid Brownell set_voltage: 45388e75cc3SDavid Brownell ret = menelaus_write_reg(vtg->mode_reg, mode); 45488e75cc3SDavid Brownell out: 45588e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 45688e75cc3SDavid Brownell if (ret == 0) { 45788e75cc3SDavid Brownell /* Wait for voltage to stabilize */ 45888e75cc3SDavid Brownell msleep(1); 45988e75cc3SDavid Brownell } 46088e75cc3SDavid Brownell return ret; 46188e75cc3SDavid Brownell } 46288e75cc3SDavid Brownell 46388e75cc3SDavid Brownell static int menelaus_get_vtg_value(int vtg, const struct menelaus_vtg_value *tbl, 46488e75cc3SDavid Brownell int n) 46588e75cc3SDavid Brownell { 46688e75cc3SDavid Brownell int i; 46788e75cc3SDavid Brownell 46888e75cc3SDavid Brownell for (i = 0; i < n; i++, tbl++) 46988e75cc3SDavid Brownell if (tbl->vtg == vtg) 47088e75cc3SDavid Brownell return tbl->val; 47188e75cc3SDavid Brownell return -EINVAL; 47288e75cc3SDavid Brownell } 47388e75cc3SDavid Brownell 47488e75cc3SDavid Brownell /* 47588e75cc3SDavid Brownell * Vcore can be programmed in two ways: 47688e75cc3SDavid Brownell * SW-controlled: Required voltage is programmed into VCORE_CTRL1 47788e75cc3SDavid Brownell * HW-controlled: Required range (roof-floor) is programmed into VCORE_CTRL3 47888e75cc3SDavid Brownell * and VCORE_CTRL4 47988e75cc3SDavid Brownell * 48088e75cc3SDavid Brownell * Call correct 'set' function accordingly 48188e75cc3SDavid Brownell */ 48288e75cc3SDavid Brownell 48388e75cc3SDavid Brownell static const struct menelaus_vtg_value vcore_values[] = { 48488e75cc3SDavid Brownell { 1000, 0 }, 48588e75cc3SDavid Brownell { 1025, 1 }, 48688e75cc3SDavid Brownell { 1050, 2 }, 48788e75cc3SDavid Brownell { 1075, 3 }, 48888e75cc3SDavid Brownell { 1100, 4 }, 48988e75cc3SDavid Brownell { 1125, 5 }, 49088e75cc3SDavid Brownell { 1150, 6 }, 49188e75cc3SDavid Brownell { 1175, 7 }, 49288e75cc3SDavid Brownell { 1200, 8 }, 49388e75cc3SDavid Brownell { 1225, 9 }, 49488e75cc3SDavid Brownell { 1250, 10 }, 49588e75cc3SDavid Brownell { 1275, 11 }, 49688e75cc3SDavid Brownell { 1300, 12 }, 49788e75cc3SDavid Brownell { 1325, 13 }, 49888e75cc3SDavid Brownell { 1350, 14 }, 49988e75cc3SDavid Brownell { 1375, 15 }, 50088e75cc3SDavid Brownell { 1400, 16 }, 50188e75cc3SDavid Brownell { 1425, 17 }, 50288e75cc3SDavid Brownell { 1450, 18 }, 50388e75cc3SDavid Brownell }; 50488e75cc3SDavid Brownell 50588e75cc3SDavid Brownell int menelaus_set_vcore_sw(unsigned int mV) 50688e75cc3SDavid Brownell { 50788e75cc3SDavid Brownell int val, ret; 50888e75cc3SDavid Brownell struct i2c_client *c = the_menelaus->client; 50988e75cc3SDavid Brownell 51088e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vcore_values, 51188e75cc3SDavid Brownell ARRAY_SIZE(vcore_values)); 51288e75cc3SDavid Brownell if (val < 0) 51388e75cc3SDavid Brownell return -EINVAL; 51488e75cc3SDavid Brownell 51588e75cc3SDavid Brownell dev_dbg(&c->dev, "Setting VCORE to %d mV (val 0x%02x)\n", mV, val); 51688e75cc3SDavid Brownell 51788e75cc3SDavid Brownell /* Set SW mode and the voltage in one go. */ 51888e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 51988e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val); 52088e75cc3SDavid Brownell if (ret == 0) 52188e75cc3SDavid Brownell the_menelaus->vcore_hw_mode = 0; 52288e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 52388e75cc3SDavid Brownell msleep(1); 52488e75cc3SDavid Brownell 52588e75cc3SDavid Brownell return ret; 52688e75cc3SDavid Brownell } 52788e75cc3SDavid Brownell 52888e75cc3SDavid Brownell int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV) 52988e75cc3SDavid Brownell { 53088e75cc3SDavid Brownell int fval, rval, val, ret; 53188e75cc3SDavid Brownell struct i2c_client *c = the_menelaus->client; 53288e75cc3SDavid Brownell 53388e75cc3SDavid Brownell rval = menelaus_get_vtg_value(roof_mV, vcore_values, 53488e75cc3SDavid Brownell ARRAY_SIZE(vcore_values)); 53588e75cc3SDavid Brownell if (rval < 0) 53688e75cc3SDavid Brownell return -EINVAL; 53788e75cc3SDavid Brownell fval = menelaus_get_vtg_value(floor_mV, vcore_values, 53888e75cc3SDavid Brownell ARRAY_SIZE(vcore_values)); 53988e75cc3SDavid Brownell if (fval < 0) 54088e75cc3SDavid Brownell return -EINVAL; 54188e75cc3SDavid Brownell 54288e75cc3SDavid Brownell dev_dbg(&c->dev, "Setting VCORE FLOOR to %d mV and ROOF to %d mV\n", 54388e75cc3SDavid Brownell floor_mV, roof_mV); 54488e75cc3SDavid Brownell 54588e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 54688e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_VCORE_CTRL3, fval); 54788e75cc3SDavid Brownell if (ret < 0) 54888e75cc3SDavid Brownell goto out; 54988e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_VCORE_CTRL4, rval); 55088e75cc3SDavid Brownell if (ret < 0) 55188e75cc3SDavid Brownell goto out; 55288e75cc3SDavid Brownell if (!the_menelaus->vcore_hw_mode) { 55388e75cc3SDavid Brownell val = menelaus_read_reg(MENELAUS_VCORE_CTRL1); 55488e75cc3SDavid Brownell /* HW mode, turn OFF byte comparator */ 55588e75cc3SDavid Brownell val |= ((1 << 7) | (1 << 5)); 55688e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val); 55788e75cc3SDavid Brownell the_menelaus->vcore_hw_mode = 1; 55888e75cc3SDavid Brownell } 55988e75cc3SDavid Brownell msleep(1); 56088e75cc3SDavid Brownell out: 56188e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 56288e75cc3SDavid Brownell return ret; 56388e75cc3SDavid Brownell } 56488e75cc3SDavid Brownell 56588e75cc3SDavid Brownell static const struct menelaus_vtg vmem_vtg = { 56688e75cc3SDavid Brownell .name = "VMEM", 56788e75cc3SDavid Brownell .vtg_reg = MENELAUS_LDO_CTRL1, 56888e75cc3SDavid Brownell .vtg_shift = 0, 56988e75cc3SDavid Brownell .vtg_bits = 2, 57088e75cc3SDavid Brownell .mode_reg = MENELAUS_LDO_CTRL3, 57188e75cc3SDavid Brownell }; 57288e75cc3SDavid Brownell 57388e75cc3SDavid Brownell static const struct menelaus_vtg_value vmem_values[] = { 57488e75cc3SDavid Brownell { 1500, 0 }, 57588e75cc3SDavid Brownell { 1800, 1 }, 57688e75cc3SDavid Brownell { 1900, 2 }, 57788e75cc3SDavid Brownell { 2500, 3 }, 57888e75cc3SDavid Brownell }; 57988e75cc3SDavid Brownell 58088e75cc3SDavid Brownell int menelaus_set_vmem(unsigned int mV) 58188e75cc3SDavid Brownell { 58288e75cc3SDavid Brownell int val; 58388e75cc3SDavid Brownell 58488e75cc3SDavid Brownell if (mV == 0) 58588e75cc3SDavid Brownell return menelaus_set_voltage(&vmem_vtg, 0, 0, 0); 58688e75cc3SDavid Brownell 58788e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vmem_values, ARRAY_SIZE(vmem_values)); 58888e75cc3SDavid Brownell if (val < 0) 58988e75cc3SDavid Brownell return -EINVAL; 59088e75cc3SDavid Brownell return menelaus_set_voltage(&vmem_vtg, mV, val, 0x02); 59188e75cc3SDavid Brownell } 59288e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_vmem); 59388e75cc3SDavid Brownell 59488e75cc3SDavid Brownell static const struct menelaus_vtg vio_vtg = { 59588e75cc3SDavid Brownell .name = "VIO", 59688e75cc3SDavid Brownell .vtg_reg = MENELAUS_LDO_CTRL1, 59788e75cc3SDavid Brownell .vtg_shift = 2, 59888e75cc3SDavid Brownell .vtg_bits = 2, 59988e75cc3SDavid Brownell .mode_reg = MENELAUS_LDO_CTRL4, 60088e75cc3SDavid Brownell }; 60188e75cc3SDavid Brownell 60288e75cc3SDavid Brownell static const struct menelaus_vtg_value vio_values[] = { 60388e75cc3SDavid Brownell { 1500, 0 }, 60488e75cc3SDavid Brownell { 1800, 1 }, 60588e75cc3SDavid Brownell { 2500, 2 }, 60688e75cc3SDavid Brownell { 2800, 3 }, 60788e75cc3SDavid Brownell }; 60888e75cc3SDavid Brownell 60988e75cc3SDavid Brownell int menelaus_set_vio(unsigned int mV) 61088e75cc3SDavid Brownell { 61188e75cc3SDavid Brownell int val; 61288e75cc3SDavid Brownell 61388e75cc3SDavid Brownell if (mV == 0) 61488e75cc3SDavid Brownell return menelaus_set_voltage(&vio_vtg, 0, 0, 0); 61588e75cc3SDavid Brownell 61688e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vio_values, ARRAY_SIZE(vio_values)); 61788e75cc3SDavid Brownell if (val < 0) 61888e75cc3SDavid Brownell return -EINVAL; 61988e75cc3SDavid Brownell return menelaus_set_voltage(&vio_vtg, mV, val, 0x02); 62088e75cc3SDavid Brownell } 62188e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_vio); 62288e75cc3SDavid Brownell 62388e75cc3SDavid Brownell static const struct menelaus_vtg_value vdcdc_values[] = { 62488e75cc3SDavid Brownell { 1500, 0 }, 62588e75cc3SDavid Brownell { 1800, 1 }, 62688e75cc3SDavid Brownell { 2000, 2 }, 62788e75cc3SDavid Brownell { 2200, 3 }, 62888e75cc3SDavid Brownell { 2400, 4 }, 62988e75cc3SDavid Brownell { 2800, 5 }, 63088e75cc3SDavid Brownell { 3000, 6 }, 63188e75cc3SDavid Brownell { 3300, 7 }, 63288e75cc3SDavid Brownell }; 63388e75cc3SDavid Brownell 63488e75cc3SDavid Brownell static const struct menelaus_vtg vdcdc2_vtg = { 63588e75cc3SDavid Brownell .name = "VDCDC2", 63688e75cc3SDavid Brownell .vtg_reg = MENELAUS_DCDC_CTRL1, 63788e75cc3SDavid Brownell .vtg_shift = 0, 63888e75cc3SDavid Brownell .vtg_bits = 3, 63988e75cc3SDavid Brownell .mode_reg = MENELAUS_DCDC_CTRL2, 64088e75cc3SDavid Brownell }; 64188e75cc3SDavid Brownell 64288e75cc3SDavid Brownell static const struct menelaus_vtg vdcdc3_vtg = { 64388e75cc3SDavid Brownell .name = "VDCDC3", 64488e75cc3SDavid Brownell .vtg_reg = MENELAUS_DCDC_CTRL1, 64588e75cc3SDavid Brownell .vtg_shift = 3, 64688e75cc3SDavid Brownell .vtg_bits = 3, 64788e75cc3SDavid Brownell .mode_reg = MENELAUS_DCDC_CTRL3, 64888e75cc3SDavid Brownell }; 64988e75cc3SDavid Brownell 65088e75cc3SDavid Brownell int menelaus_set_vdcdc(int dcdc, unsigned int mV) 65188e75cc3SDavid Brownell { 65288e75cc3SDavid Brownell const struct menelaus_vtg *vtg; 65388e75cc3SDavid Brownell int val; 65488e75cc3SDavid Brownell 65588e75cc3SDavid Brownell if (dcdc != 2 && dcdc != 3) 65688e75cc3SDavid Brownell return -EINVAL; 65788e75cc3SDavid Brownell if (dcdc == 2) 65888e75cc3SDavid Brownell vtg = &vdcdc2_vtg; 65988e75cc3SDavid Brownell else 66088e75cc3SDavid Brownell vtg = &vdcdc3_vtg; 66188e75cc3SDavid Brownell 66288e75cc3SDavid Brownell if (mV == 0) 66388e75cc3SDavid Brownell return menelaus_set_voltage(vtg, 0, 0, 0); 66488e75cc3SDavid Brownell 66588e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vdcdc_values, 66688e75cc3SDavid Brownell ARRAY_SIZE(vdcdc_values)); 66788e75cc3SDavid Brownell if (val < 0) 66888e75cc3SDavid Brownell return -EINVAL; 66988e75cc3SDavid Brownell return menelaus_set_voltage(vtg, mV, val, 0x03); 67088e75cc3SDavid Brownell } 67188e75cc3SDavid Brownell 67288e75cc3SDavid Brownell static const struct menelaus_vtg_value vmmc_values[] = { 67388e75cc3SDavid Brownell { 1850, 0 }, 67488e75cc3SDavid Brownell { 2800, 1 }, 67588e75cc3SDavid Brownell { 3000, 2 }, 67688e75cc3SDavid Brownell { 3100, 3 }, 67788e75cc3SDavid Brownell }; 67888e75cc3SDavid Brownell 67988e75cc3SDavid Brownell static const struct menelaus_vtg vmmc_vtg = { 68088e75cc3SDavid Brownell .name = "VMMC", 68188e75cc3SDavid Brownell .vtg_reg = MENELAUS_LDO_CTRL1, 68288e75cc3SDavid Brownell .vtg_shift = 6, 68388e75cc3SDavid Brownell .vtg_bits = 2, 68488e75cc3SDavid Brownell .mode_reg = MENELAUS_LDO_CTRL7, 68588e75cc3SDavid Brownell }; 68688e75cc3SDavid Brownell 68788e75cc3SDavid Brownell int menelaus_set_vmmc(unsigned int mV) 68888e75cc3SDavid Brownell { 68988e75cc3SDavid Brownell int val; 69088e75cc3SDavid Brownell 69188e75cc3SDavid Brownell if (mV == 0) 69288e75cc3SDavid Brownell return menelaus_set_voltage(&vmmc_vtg, 0, 0, 0); 69388e75cc3SDavid Brownell 69488e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vmmc_values, ARRAY_SIZE(vmmc_values)); 69588e75cc3SDavid Brownell if (val < 0) 69688e75cc3SDavid Brownell return -EINVAL; 69788e75cc3SDavid Brownell return menelaus_set_voltage(&vmmc_vtg, mV, val, 0x02); 69888e75cc3SDavid Brownell } 69988e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_vmmc); 70088e75cc3SDavid Brownell 70188e75cc3SDavid Brownell 70288e75cc3SDavid Brownell static const struct menelaus_vtg_value vaux_values[] = { 70388e75cc3SDavid Brownell { 1500, 0 }, 70488e75cc3SDavid Brownell { 1800, 1 }, 70588e75cc3SDavid Brownell { 2500, 2 }, 70688e75cc3SDavid Brownell { 2800, 3 }, 70788e75cc3SDavid Brownell }; 70888e75cc3SDavid Brownell 70988e75cc3SDavid Brownell static const struct menelaus_vtg vaux_vtg = { 71088e75cc3SDavid Brownell .name = "VAUX", 71188e75cc3SDavid Brownell .vtg_reg = MENELAUS_LDO_CTRL1, 71288e75cc3SDavid Brownell .vtg_shift = 4, 71388e75cc3SDavid Brownell .vtg_bits = 2, 71488e75cc3SDavid Brownell .mode_reg = MENELAUS_LDO_CTRL6, 71588e75cc3SDavid Brownell }; 71688e75cc3SDavid Brownell 71788e75cc3SDavid Brownell int menelaus_set_vaux(unsigned int mV) 71888e75cc3SDavid Brownell { 71988e75cc3SDavid Brownell int val; 72088e75cc3SDavid Brownell 72188e75cc3SDavid Brownell if (mV == 0) 72288e75cc3SDavid Brownell return menelaus_set_voltage(&vaux_vtg, 0, 0, 0); 72388e75cc3SDavid Brownell 72488e75cc3SDavid Brownell val = menelaus_get_vtg_value(mV, vaux_values, ARRAY_SIZE(vaux_values)); 72588e75cc3SDavid Brownell if (val < 0) 72688e75cc3SDavid Brownell return -EINVAL; 72788e75cc3SDavid Brownell return menelaus_set_voltage(&vaux_vtg, mV, val, 0x02); 72888e75cc3SDavid Brownell } 72988e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_set_vaux); 73088e75cc3SDavid Brownell 73188e75cc3SDavid Brownell int menelaus_get_slot_pin_states(void) 73288e75cc3SDavid Brownell { 73388e75cc3SDavid Brownell return menelaus_read_reg(MENELAUS_MCT_PIN_ST); 73488e75cc3SDavid Brownell } 73588e75cc3SDavid Brownell EXPORT_SYMBOL(menelaus_get_slot_pin_states); 73688e75cc3SDavid Brownell 73788e75cc3SDavid Brownell int menelaus_set_regulator_sleep(int enable, u32 val) 73888e75cc3SDavid Brownell { 73988e75cc3SDavid Brownell int t, ret; 74088e75cc3SDavid Brownell struct i2c_client *c = the_menelaus->client; 74188e75cc3SDavid Brownell 74288e75cc3SDavid Brownell mutex_lock(&the_menelaus->lock); 74388e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_SLEEP_CTRL2, val); 74488e75cc3SDavid Brownell if (ret < 0) 74588e75cc3SDavid Brownell goto out; 74688e75cc3SDavid Brownell 74788e75cc3SDavid Brownell dev_dbg(&c->dev, "regulator sleep configuration: %02x\n", val); 74888e75cc3SDavid Brownell 74988e75cc3SDavid Brownell ret = menelaus_read_reg(MENELAUS_GPIO_CTRL); 75088e75cc3SDavid Brownell if (ret < 0) 75188e75cc3SDavid Brownell goto out; 75288e75cc3SDavid Brownell t = ((1 << 6) | 0x04); 75388e75cc3SDavid Brownell if (enable) 75488e75cc3SDavid Brownell ret |= t; 75588e75cc3SDavid Brownell else 75688e75cc3SDavid Brownell ret &= ~t; 75788e75cc3SDavid Brownell ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret); 75888e75cc3SDavid Brownell out: 75988e75cc3SDavid Brownell mutex_unlock(&the_menelaus->lock); 76088e75cc3SDavid Brownell return ret; 76188e75cc3SDavid Brownell } 76288e75cc3SDavid Brownell 76388e75cc3SDavid Brownell /*-----------------------------------------------------------------------*/ 76488e75cc3SDavid Brownell 76588e75cc3SDavid Brownell /* Handles Menelaus interrupts. Does not run in interrupt context */ 76688e75cc3SDavid Brownell static void menelaus_work(struct work_struct *_menelaus) 76788e75cc3SDavid Brownell { 76888e75cc3SDavid Brownell struct menelaus_chip *menelaus = 76988e75cc3SDavid Brownell container_of(_menelaus, struct menelaus_chip, work); 77088e75cc3SDavid Brownell void (*handler)(struct menelaus_chip *menelaus); 77188e75cc3SDavid Brownell 77288e75cc3SDavid Brownell while (1) { 77388e75cc3SDavid Brownell unsigned isr; 77488e75cc3SDavid Brownell 77588e75cc3SDavid Brownell isr = (menelaus_read_reg(MENELAUS_INT_STATUS2) 77688e75cc3SDavid Brownell & ~menelaus->mask2) << 8; 77788e75cc3SDavid Brownell isr |= menelaus_read_reg(MENELAUS_INT_STATUS1) 77888e75cc3SDavid Brownell & ~menelaus->mask1; 77988e75cc3SDavid Brownell if (!isr) 78088e75cc3SDavid Brownell break; 78188e75cc3SDavid Brownell 78288e75cc3SDavid Brownell while (isr) { 78388e75cc3SDavid Brownell int irq = fls(isr) - 1; 78488e75cc3SDavid Brownell isr &= ~(1 << irq); 78588e75cc3SDavid Brownell 78688e75cc3SDavid Brownell mutex_lock(&menelaus->lock); 78788e75cc3SDavid Brownell menelaus_disable_irq(irq); 78888e75cc3SDavid Brownell menelaus_ack_irq(irq); 78988e75cc3SDavid Brownell handler = menelaus->handlers[irq]; 79088e75cc3SDavid Brownell if (handler) 79188e75cc3SDavid Brownell handler(menelaus); 79288e75cc3SDavid Brownell menelaus_enable_irq(irq); 79388e75cc3SDavid Brownell mutex_unlock(&menelaus->lock); 79488e75cc3SDavid Brownell } 79588e75cc3SDavid Brownell } 79688e75cc3SDavid Brownell enable_irq(menelaus->client->irq); 79788e75cc3SDavid Brownell } 79888e75cc3SDavid Brownell 79988e75cc3SDavid Brownell /* 80088e75cc3SDavid Brownell * We cannot use I2C in interrupt context, so we just schedule work. 80188e75cc3SDavid Brownell */ 80288e75cc3SDavid Brownell static irqreturn_t menelaus_irq(int irq, void *_menelaus) 80388e75cc3SDavid Brownell { 80488e75cc3SDavid Brownell struct menelaus_chip *menelaus = _menelaus; 80588e75cc3SDavid Brownell 80688e75cc3SDavid Brownell disable_irq_nosync(irq); 80788e75cc3SDavid Brownell (void)schedule_work(&menelaus->work); 80888e75cc3SDavid Brownell 80988e75cc3SDavid Brownell return IRQ_HANDLED; 81088e75cc3SDavid Brownell } 81188e75cc3SDavid Brownell 81288e75cc3SDavid Brownell /*-----------------------------------------------------------------------*/ 81388e75cc3SDavid Brownell 81488e75cc3SDavid Brownell /* 81588e75cc3SDavid Brownell * The RTC needs to be set once, then it runs on backup battery power. 81688e75cc3SDavid Brownell * It supports alarms, including system wake alarms (from some modes); 81788e75cc3SDavid Brownell * and 1/second IRQs if requested. 81888e75cc3SDavid Brownell */ 81988e75cc3SDavid Brownell #ifdef CONFIG_RTC_DRV_TWL92330 82088e75cc3SDavid Brownell 82188e75cc3SDavid Brownell #define RTC_CTRL_RTC_EN (1 << 0) 82288e75cc3SDavid Brownell #define RTC_CTRL_AL_EN (1 << 1) 82388e75cc3SDavid Brownell #define RTC_CTRL_MODE12 (1 << 2) 82488e75cc3SDavid Brownell #define RTC_CTRL_EVERY_MASK (3 << 3) 82588e75cc3SDavid Brownell #define RTC_CTRL_EVERY_SEC (0 << 3) 82688e75cc3SDavid Brownell #define RTC_CTRL_EVERY_MIN (1 << 3) 82788e75cc3SDavid Brownell #define RTC_CTRL_EVERY_HR (2 << 3) 82888e75cc3SDavid Brownell #define RTC_CTRL_EVERY_DAY (3 << 3) 82988e75cc3SDavid Brownell 83088e75cc3SDavid Brownell #define RTC_UPDATE_EVERY 0x08 83188e75cc3SDavid Brownell 83288e75cc3SDavid Brownell #define RTC_HR_PM (1 << 7) 83388e75cc3SDavid Brownell 83488e75cc3SDavid Brownell static void menelaus_to_time(char *regs, struct rtc_time *t) 83588e75cc3SDavid Brownell { 83688e75cc3SDavid Brownell t->tm_sec = bcd2bin(regs[0]); 83788e75cc3SDavid Brownell t->tm_min = bcd2bin(regs[1]); 83888e75cc3SDavid Brownell if (the_menelaus->rtc_control & RTC_CTRL_MODE12) { 83988e75cc3SDavid Brownell t->tm_hour = bcd2bin(regs[2] & 0x1f) - 1; 84088e75cc3SDavid Brownell if (regs[2] & RTC_HR_PM) 84188e75cc3SDavid Brownell t->tm_hour += 12; 84288e75cc3SDavid Brownell } else 84388e75cc3SDavid Brownell t->tm_hour = bcd2bin(regs[2] & 0x3f); 84488e75cc3SDavid Brownell t->tm_mday = bcd2bin(regs[3]); 84588e75cc3SDavid Brownell t->tm_mon = bcd2bin(regs[4]) - 1; 84688e75cc3SDavid Brownell t->tm_year = bcd2bin(regs[5]) + 100; 84788e75cc3SDavid Brownell } 84888e75cc3SDavid Brownell 84988e75cc3SDavid Brownell static int time_to_menelaus(struct rtc_time *t, int regnum) 85088e75cc3SDavid Brownell { 85188e75cc3SDavid Brownell int hour, status; 85288e75cc3SDavid Brownell 85388e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, bin2bcd(t->tm_sec)); 85488e75cc3SDavid Brownell if (status < 0) 85588e75cc3SDavid Brownell goto fail; 85688e75cc3SDavid Brownell 85788e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, bin2bcd(t->tm_min)); 85888e75cc3SDavid Brownell if (status < 0) 85988e75cc3SDavid Brownell goto fail; 86088e75cc3SDavid Brownell 86188e75cc3SDavid Brownell if (the_menelaus->rtc_control & RTC_CTRL_MODE12) { 86288e75cc3SDavid Brownell hour = t->tm_hour + 1; 86388e75cc3SDavid Brownell if (hour > 12) 86488e75cc3SDavid Brownell hour = RTC_HR_PM | bin2bcd(hour - 12); 86588e75cc3SDavid Brownell else 86688e75cc3SDavid Brownell hour = bin2bcd(hour); 86788e75cc3SDavid Brownell } else 86888e75cc3SDavid Brownell hour = bin2bcd(t->tm_hour); 86988e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, hour); 87088e75cc3SDavid Brownell if (status < 0) 87188e75cc3SDavid Brownell goto fail; 87288e75cc3SDavid Brownell 87388e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, bin2bcd(t->tm_mday)); 87488e75cc3SDavid Brownell if (status < 0) 87588e75cc3SDavid Brownell goto fail; 87688e75cc3SDavid Brownell 87788e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, bin2bcd(t->tm_mon + 1)); 87888e75cc3SDavid Brownell if (status < 0) 87988e75cc3SDavid Brownell goto fail; 88088e75cc3SDavid Brownell 88188e75cc3SDavid Brownell status = menelaus_write_reg(regnum++, bin2bcd(t->tm_year - 100)); 88288e75cc3SDavid Brownell if (status < 0) 88388e75cc3SDavid Brownell goto fail; 88488e75cc3SDavid Brownell 88588e75cc3SDavid Brownell return 0; 88688e75cc3SDavid Brownell fail: 88788e75cc3SDavid Brownell dev_err(&the_menelaus->client->dev, "rtc write reg %02x, err %d\n", 88888e75cc3SDavid Brownell --regnum, status); 88988e75cc3SDavid Brownell return status; 89088e75cc3SDavid Brownell } 89188e75cc3SDavid Brownell 89288e75cc3SDavid Brownell static int menelaus_read_time(struct device *dev, struct rtc_time *t) 89388e75cc3SDavid Brownell { 89488e75cc3SDavid Brownell struct i2c_msg msg[2]; 89588e75cc3SDavid Brownell char regs[7]; 89688e75cc3SDavid Brownell int status; 89788e75cc3SDavid Brownell 89888e75cc3SDavid Brownell /* block read date and time registers */ 89988e75cc3SDavid Brownell regs[0] = MENELAUS_RTC_SEC; 90088e75cc3SDavid Brownell 90188e75cc3SDavid Brownell msg[0].addr = MENELAUS_I2C_ADDRESS; 90288e75cc3SDavid Brownell msg[0].flags = 0; 90388e75cc3SDavid Brownell msg[0].len = 1; 90488e75cc3SDavid Brownell msg[0].buf = regs; 90588e75cc3SDavid Brownell 90688e75cc3SDavid Brownell msg[1].addr = MENELAUS_I2C_ADDRESS; 90788e75cc3SDavid Brownell msg[1].flags = I2C_M_RD; 90888e75cc3SDavid Brownell msg[1].len = sizeof(regs); 90988e75cc3SDavid Brownell msg[1].buf = regs; 91088e75cc3SDavid Brownell 91188e75cc3SDavid Brownell status = i2c_transfer(the_menelaus->client->adapter, msg, 2); 91288e75cc3SDavid Brownell if (status != 2) { 91388e75cc3SDavid Brownell dev_err(dev, "%s error %d\n", "read", status); 91488e75cc3SDavid Brownell return -EIO; 91588e75cc3SDavid Brownell } 91688e75cc3SDavid Brownell 91788e75cc3SDavid Brownell menelaus_to_time(regs, t); 91888e75cc3SDavid Brownell t->tm_wday = bcd2bin(regs[6]); 91988e75cc3SDavid Brownell 92088e75cc3SDavid Brownell return 0; 92188e75cc3SDavid Brownell } 92288e75cc3SDavid Brownell 92388e75cc3SDavid Brownell static int menelaus_set_time(struct device *dev, struct rtc_time *t) 92488e75cc3SDavid Brownell { 92588e75cc3SDavid Brownell int status; 92688e75cc3SDavid Brownell 92788e75cc3SDavid Brownell /* write date and time registers */ 92888e75cc3SDavid Brownell status = time_to_menelaus(t, MENELAUS_RTC_SEC); 92988e75cc3SDavid Brownell if (status < 0) 93088e75cc3SDavid Brownell return status; 93188e75cc3SDavid Brownell status = menelaus_write_reg(MENELAUS_RTC_WKDAY, bin2bcd(t->tm_wday)); 93288e75cc3SDavid Brownell if (status < 0) { 93388e75cc3SDavid Brownell dev_err(&the_menelaus->client->dev, "rtc write reg %02x " 93488e75cc3SDavid Brownell "err %d\n", MENELAUS_RTC_WKDAY, status); 93588e75cc3SDavid Brownell return status; 93688e75cc3SDavid Brownell } 93788e75cc3SDavid Brownell 93888e75cc3SDavid Brownell /* now commit the write */ 93988e75cc3SDavid Brownell status = menelaus_write_reg(MENELAUS_RTC_UPDATE, RTC_UPDATE_EVERY); 94088e75cc3SDavid Brownell if (status < 0) 94188e75cc3SDavid Brownell dev_err(&the_menelaus->client->dev, "rtc commit time, err %d\n", 94288e75cc3SDavid Brownell status); 94388e75cc3SDavid Brownell 94488e75cc3SDavid Brownell return 0; 94588e75cc3SDavid Brownell } 94688e75cc3SDavid Brownell 94788e75cc3SDavid Brownell static int menelaus_read_alarm(struct device *dev, struct rtc_wkalrm *w) 94888e75cc3SDavid Brownell { 94988e75cc3SDavid Brownell struct i2c_msg msg[2]; 95088e75cc3SDavid Brownell char regs[6]; 95188e75cc3SDavid Brownell int status; 95288e75cc3SDavid Brownell 95388e75cc3SDavid Brownell /* block read alarm registers */ 95488e75cc3SDavid Brownell regs[0] = MENELAUS_RTC_AL_SEC; 95588e75cc3SDavid Brownell 95688e75cc3SDavid Brownell msg[0].addr = MENELAUS_I2C_ADDRESS; 95788e75cc3SDavid Brownell msg[0].flags = 0; 95888e75cc3SDavid Brownell msg[0].len = 1; 95988e75cc3SDavid Brownell msg[0].buf = regs; 96088e75cc3SDavid Brownell 96188e75cc3SDavid Brownell msg[1].addr = MENELAUS_I2C_ADDRESS; 96288e75cc3SDavid Brownell msg[1].flags = I2C_M_RD; 96388e75cc3SDavid Brownell msg[1].len = sizeof(regs); 96488e75cc3SDavid Brownell msg[1].buf = regs; 96588e75cc3SDavid Brownell 96688e75cc3SDavid Brownell status = i2c_transfer(the_menelaus->client->adapter, msg, 2); 96788e75cc3SDavid Brownell if (status != 2) { 96888e75cc3SDavid Brownell dev_err(dev, "%s error %d\n", "alarm read", status); 96988e75cc3SDavid Brownell return -EIO; 97088e75cc3SDavid Brownell } 97188e75cc3SDavid Brownell 97288e75cc3SDavid Brownell menelaus_to_time(regs, &w->time); 97388e75cc3SDavid Brownell 97488e75cc3SDavid Brownell w->enabled = !!(the_menelaus->rtc_control & RTC_CTRL_AL_EN); 97588e75cc3SDavid Brownell 97688e75cc3SDavid Brownell /* NOTE we *could* check if actually pending... */ 97788e75cc3SDavid Brownell w->pending = 0; 97888e75cc3SDavid Brownell 97988e75cc3SDavid Brownell return 0; 98088e75cc3SDavid Brownell } 98188e75cc3SDavid Brownell 98288e75cc3SDavid Brownell static int menelaus_set_alarm(struct device *dev, struct rtc_wkalrm *w) 98388e75cc3SDavid Brownell { 98488e75cc3SDavid Brownell int status; 98588e75cc3SDavid Brownell 98688e75cc3SDavid Brownell if (the_menelaus->client->irq <= 0 && w->enabled) 98788e75cc3SDavid Brownell return -ENODEV; 98888e75cc3SDavid Brownell 98988e75cc3SDavid Brownell /* clear previous alarm enable */ 99088e75cc3SDavid Brownell if (the_menelaus->rtc_control & RTC_CTRL_AL_EN) { 99188e75cc3SDavid Brownell the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN; 99288e75cc3SDavid Brownell status = menelaus_write_reg(MENELAUS_RTC_CTRL, 99388e75cc3SDavid Brownell the_menelaus->rtc_control); 99488e75cc3SDavid Brownell if (status < 0) 99588e75cc3SDavid Brownell return status; 99688e75cc3SDavid Brownell } 99788e75cc3SDavid Brownell 99888e75cc3SDavid Brownell /* write alarm registers */ 99988e75cc3SDavid Brownell status = time_to_menelaus(&w->time, MENELAUS_RTC_AL_SEC); 100088e75cc3SDavid Brownell if (status < 0) 100188e75cc3SDavid Brownell return status; 100288e75cc3SDavid Brownell 100388e75cc3SDavid Brownell /* enable alarm if requested */ 100488e75cc3SDavid Brownell if (w->enabled) { 100588e75cc3SDavid Brownell the_menelaus->rtc_control |= RTC_CTRL_AL_EN; 100688e75cc3SDavid Brownell status = menelaus_write_reg(MENELAUS_RTC_CTRL, 100788e75cc3SDavid Brownell the_menelaus->rtc_control); 100888e75cc3SDavid Brownell } 100988e75cc3SDavid Brownell 101088e75cc3SDavid Brownell return status; 101188e75cc3SDavid Brownell } 101288e75cc3SDavid Brownell 101388e75cc3SDavid Brownell #ifdef CONFIG_RTC_INTF_DEV 101488e75cc3SDavid Brownell 101588e75cc3SDavid Brownell static void menelaus_rtc_update_work(struct menelaus_chip *m) 101688e75cc3SDavid Brownell { 101788e75cc3SDavid Brownell /* report 1/sec update */ 101888e75cc3SDavid Brownell local_irq_disable(); 101988e75cc3SDavid Brownell rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_UF); 102088e75cc3SDavid Brownell local_irq_enable(); 102188e75cc3SDavid Brownell } 102288e75cc3SDavid Brownell 102388e75cc3SDavid Brownell static int menelaus_ioctl(struct device *dev, unsigned cmd, unsigned long arg) 102488e75cc3SDavid Brownell { 102588e75cc3SDavid Brownell int status; 102688e75cc3SDavid Brownell 102788e75cc3SDavid Brownell if (the_menelaus->client->irq <= 0) 102888e75cc3SDavid Brownell return -ENOIOCTLCMD; 102988e75cc3SDavid Brownell 103088e75cc3SDavid Brownell switch (cmd) { 103188e75cc3SDavid Brownell /* alarm IRQ */ 103288e75cc3SDavid Brownell case RTC_AIE_ON: 103388e75cc3SDavid Brownell if (the_menelaus->rtc_control & RTC_CTRL_AL_EN) 103488e75cc3SDavid Brownell return 0; 103588e75cc3SDavid Brownell the_menelaus->rtc_control |= RTC_CTRL_AL_EN; 103688e75cc3SDavid Brownell break; 103788e75cc3SDavid Brownell case RTC_AIE_OFF: 103888e75cc3SDavid Brownell if (!(the_menelaus->rtc_control & RTC_CTRL_AL_EN)) 103988e75cc3SDavid Brownell return 0; 104088e75cc3SDavid Brownell the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN; 104188e75cc3SDavid Brownell break; 104288e75cc3SDavid Brownell /* 1/second "update" IRQ */ 104388e75cc3SDavid Brownell case RTC_UIE_ON: 104488e75cc3SDavid Brownell if (the_menelaus->uie) 104588e75cc3SDavid Brownell return 0; 104688e75cc3SDavid Brownell status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ); 104788e75cc3SDavid Brownell status = menelaus_add_irq_work(MENELAUS_RTCTMR_IRQ, 104888e75cc3SDavid Brownell menelaus_rtc_update_work); 104988e75cc3SDavid Brownell if (status == 0) 105088e75cc3SDavid Brownell the_menelaus->uie = 1; 105188e75cc3SDavid Brownell return status; 105288e75cc3SDavid Brownell case RTC_UIE_OFF: 105388e75cc3SDavid Brownell if (!the_menelaus->uie) 105488e75cc3SDavid Brownell return 0; 105588e75cc3SDavid Brownell status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ); 105688e75cc3SDavid Brownell if (status == 0) 105788e75cc3SDavid Brownell the_menelaus->uie = 0; 105888e75cc3SDavid Brownell return status; 105988e75cc3SDavid Brownell default: 106088e75cc3SDavid Brownell return -ENOIOCTLCMD; 106188e75cc3SDavid Brownell } 106288e75cc3SDavid Brownell return menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control); 106388e75cc3SDavid Brownell } 106488e75cc3SDavid Brownell 106588e75cc3SDavid Brownell #else 106688e75cc3SDavid Brownell #define menelaus_ioctl NULL 106788e75cc3SDavid Brownell #endif 106888e75cc3SDavid Brownell 106988e75cc3SDavid Brownell /* REVISIT no compensation register support ... */ 107088e75cc3SDavid Brownell 107188e75cc3SDavid Brownell static const struct rtc_class_ops menelaus_rtc_ops = { 107288e75cc3SDavid Brownell .ioctl = menelaus_ioctl, 107388e75cc3SDavid Brownell .read_time = menelaus_read_time, 107488e75cc3SDavid Brownell .set_time = menelaus_set_time, 107588e75cc3SDavid Brownell .read_alarm = menelaus_read_alarm, 107688e75cc3SDavid Brownell .set_alarm = menelaus_set_alarm, 107788e75cc3SDavid Brownell }; 107888e75cc3SDavid Brownell 107988e75cc3SDavid Brownell static void menelaus_rtc_alarm_work(struct menelaus_chip *m) 108088e75cc3SDavid Brownell { 108188e75cc3SDavid Brownell /* report alarm */ 108288e75cc3SDavid Brownell local_irq_disable(); 108388e75cc3SDavid Brownell rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_AF); 108488e75cc3SDavid Brownell local_irq_enable(); 108588e75cc3SDavid Brownell 108688e75cc3SDavid Brownell /* then disable it; alarms are oneshot */ 108788e75cc3SDavid Brownell the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN; 108888e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control); 108988e75cc3SDavid Brownell } 109088e75cc3SDavid Brownell 109188e75cc3SDavid Brownell static inline void menelaus_rtc_init(struct menelaus_chip *m) 109288e75cc3SDavid Brownell { 109388e75cc3SDavid Brownell int alarm = (m->client->irq > 0); 109488e75cc3SDavid Brownell 109588e75cc3SDavid Brownell /* assume 32KDETEN pin is pulled high */ 109688e75cc3SDavid Brownell if (!(menelaus_read_reg(MENELAUS_OSC_CTRL) & 0x80)) { 109788e75cc3SDavid Brownell dev_dbg(&m->client->dev, "no 32k oscillator\n"); 109888e75cc3SDavid Brownell return; 109988e75cc3SDavid Brownell } 110088e75cc3SDavid Brownell 110188e75cc3SDavid Brownell /* support RTC alarm; it can issue wakeups */ 110288e75cc3SDavid Brownell if (alarm) { 110388e75cc3SDavid Brownell if (menelaus_add_irq_work(MENELAUS_RTCALM_IRQ, 110488e75cc3SDavid Brownell menelaus_rtc_alarm_work) < 0) { 110588e75cc3SDavid Brownell dev_err(&m->client->dev, "can't handle RTC alarm\n"); 110688e75cc3SDavid Brownell return; 110788e75cc3SDavid Brownell } 110888e75cc3SDavid Brownell device_init_wakeup(&m->client->dev, 1); 110988e75cc3SDavid Brownell } 111088e75cc3SDavid Brownell 111188e75cc3SDavid Brownell /* be sure RTC is enabled; allow 1/sec irqs; leave 12hr mode alone */ 111288e75cc3SDavid Brownell m->rtc_control = menelaus_read_reg(MENELAUS_RTC_CTRL); 111388e75cc3SDavid Brownell if (!(m->rtc_control & RTC_CTRL_RTC_EN) 111488e75cc3SDavid Brownell || (m->rtc_control & RTC_CTRL_AL_EN) 111588e75cc3SDavid Brownell || (m->rtc_control & RTC_CTRL_EVERY_MASK)) { 111688e75cc3SDavid Brownell if (!(m->rtc_control & RTC_CTRL_RTC_EN)) { 111788e75cc3SDavid Brownell dev_warn(&m->client->dev, "rtc clock needs setting\n"); 111888e75cc3SDavid Brownell m->rtc_control |= RTC_CTRL_RTC_EN; 111988e75cc3SDavid Brownell } 112088e75cc3SDavid Brownell m->rtc_control &= ~RTC_CTRL_EVERY_MASK; 112188e75cc3SDavid Brownell m->rtc_control &= ~RTC_CTRL_AL_EN; 112288e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_RTC_CTRL, m->rtc_control); 112388e75cc3SDavid Brownell } 112488e75cc3SDavid Brownell 112588e75cc3SDavid Brownell m->rtc = rtc_device_register(DRIVER_NAME, 112688e75cc3SDavid Brownell &m->client->dev, 112788e75cc3SDavid Brownell &menelaus_rtc_ops, THIS_MODULE); 112888e75cc3SDavid Brownell if (IS_ERR(m->rtc)) { 112988e75cc3SDavid Brownell if (alarm) { 113088e75cc3SDavid Brownell menelaus_remove_irq_work(MENELAUS_RTCALM_IRQ); 113188e75cc3SDavid Brownell device_init_wakeup(&m->client->dev, 0); 113288e75cc3SDavid Brownell } 113388e75cc3SDavid Brownell dev_err(&m->client->dev, "can't register RTC: %d\n", 113488e75cc3SDavid Brownell (int) PTR_ERR(m->rtc)); 113588e75cc3SDavid Brownell the_menelaus->rtc = NULL; 113688e75cc3SDavid Brownell } 113788e75cc3SDavid Brownell } 113888e75cc3SDavid Brownell 113988e75cc3SDavid Brownell #else 114088e75cc3SDavid Brownell 114188e75cc3SDavid Brownell static inline void menelaus_rtc_init(struct menelaus_chip *m) 114288e75cc3SDavid Brownell { 114388e75cc3SDavid Brownell /* nothing */ 114488e75cc3SDavid Brownell } 114588e75cc3SDavid Brownell 114688e75cc3SDavid Brownell #endif 114788e75cc3SDavid Brownell 114888e75cc3SDavid Brownell /*-----------------------------------------------------------------------*/ 114988e75cc3SDavid Brownell 115088e75cc3SDavid Brownell static struct i2c_driver menelaus_i2c_driver; 115188e75cc3SDavid Brownell 115288e75cc3SDavid Brownell static int menelaus_probe(struct i2c_client *client, 115388e75cc3SDavid Brownell const struct i2c_device_id *id) 115488e75cc3SDavid Brownell { 115588e75cc3SDavid Brownell struct menelaus_chip *menelaus; 115688e75cc3SDavid Brownell int rev = 0, val; 115788e75cc3SDavid Brownell int err = 0; 115888e75cc3SDavid Brownell struct menelaus_platform_data *menelaus_pdata = 115988e75cc3SDavid Brownell client->dev.platform_data; 116088e75cc3SDavid Brownell 116188e75cc3SDavid Brownell if (the_menelaus) { 116288e75cc3SDavid Brownell dev_dbg(&client->dev, "only one %s for now\n", 116388e75cc3SDavid Brownell DRIVER_NAME); 116488e75cc3SDavid Brownell return -ENODEV; 116588e75cc3SDavid Brownell } 116688e75cc3SDavid Brownell 116788e75cc3SDavid Brownell menelaus = kzalloc(sizeof *menelaus, GFP_KERNEL); 116888e75cc3SDavid Brownell if (!menelaus) 116988e75cc3SDavid Brownell return -ENOMEM; 117088e75cc3SDavid Brownell 117188e75cc3SDavid Brownell i2c_set_clientdata(client, menelaus); 117288e75cc3SDavid Brownell 117388e75cc3SDavid Brownell the_menelaus = menelaus; 117488e75cc3SDavid Brownell menelaus->client = client; 117588e75cc3SDavid Brownell 117688e75cc3SDavid Brownell /* If a true probe check the device */ 117788e75cc3SDavid Brownell rev = menelaus_read_reg(MENELAUS_REV); 117888e75cc3SDavid Brownell if (rev < 0) { 117988e75cc3SDavid Brownell pr_err(DRIVER_NAME ": device not found"); 118088e75cc3SDavid Brownell err = -ENODEV; 118188e75cc3SDavid Brownell goto fail1; 118288e75cc3SDavid Brownell } 118388e75cc3SDavid Brownell 118488e75cc3SDavid Brownell /* Ack and disable all Menelaus interrupts */ 118588e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_INT_ACK1, 0xff); 118688e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_INT_ACK2, 0xff); 118788e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_INT_MASK1, 0xff); 118888e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_INT_MASK2, 0xff); 118988e75cc3SDavid Brownell menelaus->mask1 = 0xff; 119088e75cc3SDavid Brownell menelaus->mask2 = 0xff; 119188e75cc3SDavid Brownell 119288e75cc3SDavid Brownell /* Set output buffer strengths */ 119388e75cc3SDavid Brownell menelaus_write_reg(MENELAUS_MCT_CTRL1, 0x73); 119488e75cc3SDavid Brownell 119588e75cc3SDavid Brownell if (client->irq > 0) { 119688e75cc3SDavid Brownell err = request_irq(client->irq, menelaus_irq, IRQF_DISABLED, 119788e75cc3SDavid Brownell DRIVER_NAME, menelaus); 119888e75cc3SDavid Brownell if (err) { 119988e75cc3SDavid Brownell dev_dbg(&client->dev, "can't get IRQ %d, err %d\n", 120088e75cc3SDavid Brownell client->irq, err); 120188e75cc3SDavid Brownell goto fail1; 120288e75cc3SDavid Brownell } 120388e75cc3SDavid Brownell } 120488e75cc3SDavid Brownell 120588e75cc3SDavid Brownell mutex_init(&menelaus->lock); 120688e75cc3SDavid Brownell INIT_WORK(&menelaus->work, menelaus_work); 120788e75cc3SDavid Brownell 120888e75cc3SDavid Brownell pr_info("Menelaus rev %d.%d\n", rev >> 4, rev & 0x0f); 120988e75cc3SDavid Brownell 121088e75cc3SDavid Brownell val = menelaus_read_reg(MENELAUS_VCORE_CTRL1); 121188e75cc3SDavid Brownell if (val < 0) 121288e75cc3SDavid Brownell goto fail2; 121388e75cc3SDavid Brownell if (val & (1 << 7)) 121488e75cc3SDavid Brownell menelaus->vcore_hw_mode = 1; 121588e75cc3SDavid Brownell else 121688e75cc3SDavid Brownell menelaus->vcore_hw_mode = 0; 121788e75cc3SDavid Brownell 121888e75cc3SDavid Brownell if (menelaus_pdata != NULL && menelaus_pdata->late_init != NULL) { 121988e75cc3SDavid Brownell err = menelaus_pdata->late_init(&client->dev); 122088e75cc3SDavid Brownell if (err < 0) 122188e75cc3SDavid Brownell goto fail2; 122288e75cc3SDavid Brownell } 122388e75cc3SDavid Brownell 122488e75cc3SDavid Brownell menelaus_rtc_init(menelaus); 122588e75cc3SDavid Brownell 122688e75cc3SDavid Brownell return 0; 122788e75cc3SDavid Brownell fail2: 122888e75cc3SDavid Brownell free_irq(client->irq, menelaus); 122988e75cc3SDavid Brownell flush_scheduled_work(); 123088e75cc3SDavid Brownell fail1: 123188e75cc3SDavid Brownell kfree(menelaus); 123288e75cc3SDavid Brownell return err; 123388e75cc3SDavid Brownell } 123488e75cc3SDavid Brownell 123588e75cc3SDavid Brownell static int __exit menelaus_remove(struct i2c_client *client) 123688e75cc3SDavid Brownell { 123788e75cc3SDavid Brownell struct menelaus_chip *menelaus = i2c_get_clientdata(client); 123888e75cc3SDavid Brownell 123988e75cc3SDavid Brownell free_irq(client->irq, menelaus); 124088e75cc3SDavid Brownell kfree(menelaus); 124188e75cc3SDavid Brownell i2c_set_clientdata(client, NULL); 124288e75cc3SDavid Brownell the_menelaus = NULL; 124388e75cc3SDavid Brownell return 0; 124488e75cc3SDavid Brownell } 124588e75cc3SDavid Brownell 124688e75cc3SDavid Brownell static const struct i2c_device_id menelaus_id[] = { 124788e75cc3SDavid Brownell { "menelaus", 0 }, 124888e75cc3SDavid Brownell { } 124988e75cc3SDavid Brownell }; 125088e75cc3SDavid Brownell MODULE_DEVICE_TABLE(i2c, menelaus_id); 125188e75cc3SDavid Brownell 125288e75cc3SDavid Brownell static struct i2c_driver menelaus_i2c_driver = { 125388e75cc3SDavid Brownell .driver = { 125488e75cc3SDavid Brownell .name = DRIVER_NAME, 125588e75cc3SDavid Brownell }, 125688e75cc3SDavid Brownell .probe = menelaus_probe, 125788e75cc3SDavid Brownell .remove = __exit_p(menelaus_remove), 125888e75cc3SDavid Brownell .id_table = menelaus_id, 125988e75cc3SDavid Brownell }; 126088e75cc3SDavid Brownell 126188e75cc3SDavid Brownell static int __init menelaus_init(void) 126288e75cc3SDavid Brownell { 126388e75cc3SDavid Brownell int res; 126488e75cc3SDavid Brownell 126588e75cc3SDavid Brownell res = i2c_add_driver(&menelaus_i2c_driver); 126688e75cc3SDavid Brownell if (res < 0) { 126788e75cc3SDavid Brownell pr_err(DRIVER_NAME ": driver registration failed\n"); 126888e75cc3SDavid Brownell return res; 126988e75cc3SDavid Brownell } 127088e75cc3SDavid Brownell 127188e75cc3SDavid Brownell return 0; 127288e75cc3SDavid Brownell } 127388e75cc3SDavid Brownell 127488e75cc3SDavid Brownell static void __exit menelaus_exit(void) 127588e75cc3SDavid Brownell { 127688e75cc3SDavid Brownell i2c_del_driver(&menelaus_i2c_driver); 127788e75cc3SDavid Brownell 127888e75cc3SDavid Brownell /* FIXME: Shutdown menelaus parts that can be shut down */ 127988e75cc3SDavid Brownell } 128088e75cc3SDavid Brownell 128188e75cc3SDavid Brownell MODULE_AUTHOR("Texas Instruments, Inc. (and others)"); 128288e75cc3SDavid Brownell MODULE_DESCRIPTION("I2C interface for Menelaus."); 128388e75cc3SDavid Brownell MODULE_LICENSE("GPL"); 128488e75cc3SDavid Brownell 128588e75cc3SDavid Brownell module_init(menelaus_init); 128688e75cc3SDavid Brownell module_exit(menelaus_exit); 1287