// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2004 Texas Instruments, Inc. * * Some parts based tps65010.c: * Copyright (C) 2004 Texas Instruments and * Copyright (C) 2004-2005 David Brownell * * Some parts based on tlv320aic24.c: * Copyright (C) by Kai Svahn <kai.svahn@nokia.com> * * Changes for interrupt handling and clean-up by * Tony Lindgren <tony@atomide.com> and Imre Deak <imre.deak@nokia.com> * Cleanup and generalized support for voltage setting by * Juha Yrjola * Added support for controlling VCORE and regulator sleep states, * Amit Kucheria <amit.kucheria@nokia.com> * Copyright (C) 2005, 2006 Nokia Corporation
*/
/* Adds a handler for an interrupt. Does not run in interrupt context */ staticint menelaus_add_irq_work(int irq, void (*handler)(struct menelaus_chip *))
{ int ret = 0;
mutex_lock(&the_menelaus->lock);
the_menelaus->handlers[irq] = handler;
ret = menelaus_enable_irq(irq);
mutex_unlock(&the_menelaus->lock);
return ret;
}
/* Removes handler for an interrupt */ staticint menelaus_remove_irq_work(int irq)
{ int ret = 0;
mutex_lock(&the_menelaus->lock);
ret = menelaus_disable_irq(irq);
the_menelaus->handlers[irq] = NULL;
mutex_unlock(&the_menelaus->lock);
return ret;
}
/* * Gets scheduled when a card detect interrupt happens. Note that in some cases * this line is wired to card cover switch rather than the card detect switch * in each slot. In this case the cards are not seen by menelaus. * FIXME: Add handling for D1 too
*/ staticvoid menelaus_mmc_cd_work(struct menelaus_chip *menelaus_hw)
{ int reg; unsignedchar card_mask = 0;
reg = menelaus_read_reg(MENELAUS_MCT_PIN_ST); if (reg < 0) return;
if (!(reg & 0x1))
card_mask |= MCT_PIN_ST_S1_CD_ST;
if (!(reg & 0x2))
card_mask |= MCT_PIN_ST_S2_CD_ST;
if (menelaus_hw->mmc_callback)
menelaus_hw->mmc_callback(menelaus_hw->mmc_callback_data,
card_mask);
}
/* * Toggles the MMC slots between open-drain and push-pull mode.
*/ int menelaus_set_mmc_opendrain(int slot, int enable)
{ int ret, val;
if (slot != 1 && slot != 2) return -EINVAL;
mutex_lock(&the_menelaus->lock);
ret = menelaus_read_reg(MENELAUS_MCT_CTRL1); if (ret < 0) {
mutex_unlock(&the_menelaus->lock); return ret;
}
val = ret; if (slot == 1) { if (enable)
val |= MCT_CTRL1_S1_CMD_OD; else
val &= ~MCT_CTRL1_S1_CMD_OD;
} else { if (enable)
val |= MCT_CTRL1_S2_CMD_OD; else
val &= ~MCT_CTRL1_S2_CMD_OD;
}
ret = menelaus_write_reg(MENELAUS_MCT_CTRL1, val);
mutex_unlock(&the_menelaus->lock);
mutex_lock(&the_menelaus->lock);
ret = menelaus_read_reg(MENELAUS_GPIO_CTRL); if (ret < 0) goto out;
ret |= GPIO2_DIR_INPUT; if (enable)
ret |= GPIO_CTRL_SLOTSELEN; else
ret &= ~GPIO_CTRL_SLOTSELEN;
ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);
out:
mutex_unlock(&the_menelaus->lock); return ret;
}
EXPORT_SYMBOL(menelaus_set_slot_sel);
int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_en)
{ int ret, val;
if (slot != 1 && slot != 2) return -EINVAL; if (power >= 3) return -EINVAL;
mutex_lock(&the_menelaus->lock);
ret = menelaus_read_reg(MENELAUS_MCT_CTRL2); if (ret < 0) goto out;
val = ret; if (slot == 1) { if (cd_en)
val |= MCT_CTRL2_S1CD_BUFEN | MCT_CTRL2_S1CD_DBEN; else
val &= ~(MCT_CTRL2_S1CD_BUFEN | MCT_CTRL2_S1CD_DBEN);
} else { if (cd_en)
val |= MCT_CTRL2_S2CD_BUFEN | MCT_CTRL2_S2CD_BEN; else
val &= ~(MCT_CTRL2_S2CD_BUFEN | MCT_CTRL2_S2CD_BEN);
}
ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, val); if (ret < 0) goto out;
ret = menelaus_read_reg(MENELAUS_MCT_CTRL3); if (ret < 0) goto out;
val = ret; if (slot == 1) { if (enable)
val |= MCT_CTRL3_SLOT1_EN; else
val &= ~MCT_CTRL3_SLOT1_EN;
} else { int b;
if (enable)
val |= MCT_CTRL3_SLOT2_EN; else
val &= ~MCT_CTRL3_SLOT2_EN;
b = menelaus_read_reg(MENELAUS_MCT_CTRL2);
b &= ~(MCT_CTRL2_VS2_SEL_D0 | MCT_CTRL2_VS2_SEL_D1);
b |= power;
ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, b); if (ret < 0) goto out;
} /* Disable autonomous shutdown */
val &= ~(MCT_CTRL3_S1_AUTO_EN | MCT_CTRL3_S2_AUTO_EN);
ret = menelaus_write_reg(MENELAUS_MCT_CTRL3, val);
out:
mutex_unlock(&the_menelaus->lock); return ret;
}
EXPORT_SYMBOL(menelaus_set_mmc_slot);
int menelaus_register_mmc_callback(void (*callback)(void *data, u8 card_mask), void *data)
{ int ret = 0;
the_menelaus->mmc_callback_data = data;
the_menelaus->mmc_callback = callback;
ret = menelaus_add_irq_work(MENELAUS_MMC_S1CD_IRQ,
menelaus_mmc_cd_work); if (ret < 0) return ret;
ret = menelaus_add_irq_work(MENELAUS_MMC_S2CD_IRQ,
menelaus_mmc_cd_work); if (ret < 0) return ret;
ret = menelaus_add_irq_work(MENELAUS_MMC_S1D1_IRQ,
menelaus_mmc_cd_work); if (ret < 0) return ret;
ret = menelaus_add_irq_work(MENELAUS_MMC_S2D1_IRQ,
menelaus_mmc_cd_work);
staticint menelaus_set_voltage(conststruct menelaus_vtg *vtg, int mV, int vtg_val, int mode)
{ int val, ret; struct i2c_client *c = the_menelaus->client;
mutex_lock(&the_menelaus->lock);
ret = menelaus_read_reg(vtg->vtg_reg); if (ret < 0) goto out;
val = ret & ~(((1 << vtg->vtg_bits) - 1) << vtg->vtg_shift);
val |= vtg_val << vtg->vtg_shift;
dev_dbg(&c->dev, "Setting voltage '%s'" "to %d mV (reg 0x%02x, val 0x%02x)\n",
vtg->name, mV, vtg->vtg_reg, val);
ret = menelaus_write_reg(vtg->vtg_reg, val); if (ret < 0) goto out;
ret = menelaus_write_reg(vtg->mode_reg, mode);
out:
mutex_unlock(&the_menelaus->lock); if (ret == 0) { /* Wait for voltage to stabilize */
msleep(1);
} return ret;
}
staticint menelaus_get_vtg_value(int vtg, conststruct menelaus_vtg_value *tbl, int n)
{ int i;
for (i = 0; i < n; i++, tbl++) if (tbl->vtg == vtg) return tbl->val; return -EINVAL;
}
/* * Vcore can be programmed in two ways: * SW-controlled: Required voltage is programmed into VCORE_CTRL1 * HW-controlled: Required range (roof-floor) is programmed into VCORE_CTRL3 * and VCORE_CTRL4 * * Call correct 'set' function accordingly
*/
ret = menelaus_read_reg(MENELAUS_GPIO_CTRL); if (ret < 0) goto out;
t = (GPIO_CTRL_SLPCTLEN | GPIO3_DIR_INPUT); if (enable)
ret |= t; else
ret &= ~t;
ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);
out:
mutex_unlock(&the_menelaus->lock); return ret;
}
/* * We cannot use I2C in interrupt context, so we just schedule work.
*/ static irqreturn_t menelaus_irq(int irq, void *_menelaus)
{ struct menelaus_chip *menelaus = _menelaus;
/* * The RTC needs to be set once, then it runs on backup battery power. * It supports alarms, including system wake alarms (from some modes); * and 1/second IRQs if requested.
*/ #ifdef CONFIG_RTC_DRV_TWL92330
staticint menelaus_set_time(struct device *dev, struct rtc_time *t)
{ int status;
/* write date and time registers */
status = time_to_menelaus(t, MENELAUS_RTC_SEC); if (status < 0) return status;
status = menelaus_write_reg(MENELAUS_RTC_WKDAY, bin2bcd(t->tm_wday)); if (status < 0) {
dev_err(&the_menelaus->client->dev, "rtc write reg %02x " "err %d\n", MENELAUS_RTC_WKDAY, status); return status;
}
/* now commit the write */
status = menelaus_write_reg(MENELAUS_RTC_UPDATE, RTC_UPDATE_EVERY); if (status < 0)
dev_err(&the_menelaus->client->dev, "rtc commit time, err %d\n",
status);
/* then disable it; alarms are oneshot */
the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control);
}
staticinlinevoid menelaus_rtc_init(struct menelaus_chip *m)
{ int alarm = (m->client->irq > 0); int err;
/* assume 32KDETEN pin is pulled high */ if (!(menelaus_read_reg(MENELAUS_OSC_CTRL) & 0x80)) {
dev_dbg(&m->client->dev, "no 32k oscillator\n"); return;
}
m->rtc = devm_rtc_allocate_device(&m->client->dev); if (IS_ERR(m->rtc)) return;
m->rtc->ops = &menelaus_rtc_ops;
/* support RTC alarm; it can issue wakeups */ if (alarm) { if (menelaus_add_irq_work(MENELAUS_RTCALM_IRQ,
menelaus_rtc_alarm_work) < 0) {
dev_err(&m->client->dev, "can't handle RTC alarm\n"); return;
}
device_init_wakeup(&m->client->dev, 1);
}
/* If a true probe check the device */
rev = menelaus_read_reg(MENELAUS_REV); if (rev < 0) {
pr_err(DRIVER_NAME ": device not found"); return -ENODEV;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.