menelaus.c

来自「linux 内核源代码」· C语言 代码 · 共 1,279 行 · 第 1/2 页

C
1,279
字号
static const struct menelaus_vtg vdcdc3_vtg = {	.name = "VDCDC3",	.vtg_reg = MENELAUS_DCDC_CTRL1,	.vtg_shift = 3,	.vtg_bits = 3,	.mode_reg = MENELAUS_DCDC_CTRL3,};int menelaus_set_vdcdc(int dcdc, unsigned int mV){	const struct menelaus_vtg *vtg;	int val;	if (dcdc != 2 && dcdc != 3)		return -EINVAL;	if (dcdc == 2)		vtg = &vdcdc2_vtg;	else		vtg = &vdcdc3_vtg;	if (mV == 0)		return menelaus_set_voltage(vtg, 0, 0, 0);	val = menelaus_get_vtg_value(mV, vdcdc_values,				     ARRAY_SIZE(vdcdc_values));	if (val < 0)		return -EINVAL;	return menelaus_set_voltage(vtg, mV, val, 0x03);}static const struct menelaus_vtg_value vmmc_values[] = {	{ 1850, 0 },	{ 2800, 1 },	{ 3000, 2 },	{ 3100, 3 },};static const struct menelaus_vtg vmmc_vtg = {	.name = "VMMC",	.vtg_reg = MENELAUS_LDO_CTRL1,	.vtg_shift = 6,	.vtg_bits = 2,	.mode_reg = MENELAUS_LDO_CTRL7,};int menelaus_set_vmmc(unsigned int mV){	int val;	if (mV == 0)		return menelaus_set_voltage(&vmmc_vtg, 0, 0, 0);	val = menelaus_get_vtg_value(mV, vmmc_values, ARRAY_SIZE(vmmc_values));	if (val < 0)		return -EINVAL;	return menelaus_set_voltage(&vmmc_vtg, mV, val, 0x02);}EXPORT_SYMBOL(menelaus_set_vmmc);static const struct menelaus_vtg_value vaux_values[] = {	{ 1500, 0 },	{ 1800, 1 },	{ 2500, 2 },	{ 2800, 3 },};static const struct menelaus_vtg vaux_vtg = {	.name = "VAUX",	.vtg_reg = MENELAUS_LDO_CTRL1,	.vtg_shift = 4,	.vtg_bits = 2,	.mode_reg = MENELAUS_LDO_CTRL6,};int menelaus_set_vaux(unsigned int mV){	int val;	if (mV == 0)		return menelaus_set_voltage(&vaux_vtg, 0, 0, 0);	val = menelaus_get_vtg_value(mV, vaux_values, ARRAY_SIZE(vaux_values));	if (val < 0)		return -EINVAL;	return menelaus_set_voltage(&vaux_vtg, mV, val, 0x02);}EXPORT_SYMBOL(menelaus_set_vaux);int menelaus_get_slot_pin_states(void){	return menelaus_read_reg(MENELAUS_MCT_PIN_ST);}EXPORT_SYMBOL(menelaus_get_slot_pin_states);int menelaus_set_regulator_sleep(int enable, u32 val){	int t, ret;	struct i2c_client *c = the_menelaus->client;	mutex_lock(&the_menelaus->lock);	ret = menelaus_write_reg(MENELAUS_SLEEP_CTRL2, val);	if (ret < 0)		goto out;	dev_dbg(&c->dev, "regulator sleep configuration: %02x\n", val);	ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);	if (ret < 0)		goto out;	t = ((1 << 6) | 0x04);	if (enable)		ret |= t;	else		ret &= ~t;	ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);out:	mutex_unlock(&the_menelaus->lock);	return ret;}/*-----------------------------------------------------------------------*//* Handles Menelaus interrupts. Does not run in interrupt context */static void menelaus_work(struct work_struct *_menelaus){	struct menelaus_chip *menelaus =			container_of(_menelaus, struct menelaus_chip, work);	void (*handler)(struct menelaus_chip *menelaus);	while (1) {		unsigned isr;		isr = (menelaus_read_reg(MENELAUS_INT_STATUS2)				& ~menelaus->mask2) << 8;		isr |= menelaus_read_reg(MENELAUS_INT_STATUS1)				& ~menelaus->mask1;		if (!isr)			break;		while (isr) {			int irq = fls(isr) - 1;			isr &= ~(1 << irq);			mutex_lock(&menelaus->lock);			menelaus_disable_irq(irq);			menelaus_ack_irq(irq);			handler = menelaus->handlers[irq];			if (handler)				handler(menelaus);			menelaus_enable_irq(irq);			mutex_unlock(&menelaus->lock);		}	}	enable_irq(menelaus->client->irq);}/* * 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;	disable_irq_nosync(irq);	(void)schedule_work(&menelaus->work);	return IRQ_HANDLED;}/*-----------------------------------------------------------------------*//* * 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#define RTC_CTRL_RTC_EN		(1 << 0)#define RTC_CTRL_AL_EN		(1 << 1)#define RTC_CTRL_MODE12		(1 << 2)#define RTC_CTRL_EVERY_MASK	(3 << 3)#define RTC_CTRL_EVERY_SEC	(0 << 3)#define RTC_CTRL_EVERY_MIN	(1 << 3)#define RTC_CTRL_EVERY_HR	(2 << 3)#define RTC_CTRL_EVERY_DAY	(3 << 3)#define RTC_UPDATE_EVERY	0x08#define RTC_HR_PM		(1 << 7)static void menelaus_to_time(char *regs, struct rtc_time *t){	t->tm_sec = BCD2BIN(regs[0]);	t->tm_min = BCD2BIN(regs[1]);	if (the_menelaus->rtc_control & RTC_CTRL_MODE12) {		t->tm_hour = BCD2BIN(regs[2] & 0x1f) - 1;		if (regs[2] & RTC_HR_PM)			t->tm_hour += 12;	} else		t->tm_hour = BCD2BIN(regs[2] & 0x3f);	t->tm_mday = BCD2BIN(regs[3]);	t->tm_mon = BCD2BIN(regs[4]) - 1;	t->tm_year = BCD2BIN(regs[5]) + 100;}static int time_to_menelaus(struct rtc_time *t, int regnum){	int	hour, status;	status = menelaus_write_reg(regnum++, BIN2BCD(t->tm_sec));	if (status < 0)		goto fail;	status = menelaus_write_reg(regnum++, BIN2BCD(t->tm_min));	if (status < 0)		goto fail;	if (the_menelaus->rtc_control & RTC_CTRL_MODE12) {		hour = t->tm_hour + 1;		if (hour > 12)			hour = RTC_HR_PM | BIN2BCD(hour - 12);		else			hour = BIN2BCD(hour);	} else		hour = BIN2BCD(t->tm_hour);	status = menelaus_write_reg(regnum++, hour);	if (status < 0)		goto fail;	status = menelaus_write_reg(regnum++, BIN2BCD(t->tm_mday));	if (status < 0)		goto fail;	status = menelaus_write_reg(regnum++, BIN2BCD(t->tm_mon + 1));	if (status < 0)		goto fail;	status = menelaus_write_reg(regnum++, BIN2BCD(t->tm_year - 100));	if (status < 0)		goto fail;	return 0;fail:	dev_err(&the_menelaus->client->dev, "rtc write reg %02x, err %d\n",			--regnum, status);	return status;}static int menelaus_read_time(struct device *dev, struct rtc_time *t){	struct i2c_msg	msg[2];	char		regs[7];	int		status;	/* block read date and time registers */	regs[0] = MENELAUS_RTC_SEC;	msg[0].addr = MENELAUS_I2C_ADDRESS;	msg[0].flags = 0;	msg[0].len = 1;	msg[0].buf = regs;	msg[1].addr = MENELAUS_I2C_ADDRESS;	msg[1].flags = I2C_M_RD;	msg[1].len = sizeof(regs);	msg[1].buf = regs;	status = i2c_transfer(the_menelaus->client->adapter, msg, 2);	if (status != 2) {		dev_err(dev, "%s error %d\n", "read", status);		return -EIO;	}	menelaus_to_time(regs, t);	t->tm_wday = BCD2BIN(regs[6]);	return 0;}static int 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);	return 0;}static int menelaus_read_alarm(struct device *dev, struct rtc_wkalrm *w){	struct i2c_msg	msg[2];	char		regs[6];	int		status;	/* block read alarm registers */	regs[0] = MENELAUS_RTC_AL_SEC;	msg[0].addr = MENELAUS_I2C_ADDRESS;	msg[0].flags = 0;	msg[0].len = 1;	msg[0].buf = regs;	msg[1].addr = MENELAUS_I2C_ADDRESS;	msg[1].flags = I2C_M_RD;	msg[1].len = sizeof(regs);	msg[1].buf = regs;	status = i2c_transfer(the_menelaus->client->adapter, msg, 2);	if (status != 2) {		dev_err(dev, "%s error %d\n", "alarm read", status);		return -EIO;	}	menelaus_to_time(regs, &w->time);	w->enabled = !!(the_menelaus->rtc_control & RTC_CTRL_AL_EN);	/* NOTE we *could* check if actually pending... */	w->pending = 0;	return 0;}static int menelaus_set_alarm(struct device *dev, struct rtc_wkalrm *w){	int		status;	if (the_menelaus->client->irq <= 0 && w->enabled)		return -ENODEV;	/* clear previous alarm enable */	if (the_menelaus->rtc_control & RTC_CTRL_AL_EN) {		the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;		status = menelaus_write_reg(MENELAUS_RTC_CTRL,				the_menelaus->rtc_control);		if (status < 0)			return status;	}	/* write alarm registers */	status = time_to_menelaus(&w->time, MENELAUS_RTC_AL_SEC);	if (status < 0)		return status;	/* enable alarm if requested */	if (w->enabled) {		the_menelaus->rtc_control |= RTC_CTRL_AL_EN;		status = menelaus_write_reg(MENELAUS_RTC_CTRL,				the_menelaus->rtc_control);	}	return status;}#ifdef CONFIG_RTC_INTF_DEVstatic void menelaus_rtc_update_work(struct menelaus_chip *m){	/* report 1/sec update */	local_irq_disable();	rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_UF);	local_irq_enable();}static int menelaus_ioctl(struct device *dev, unsigned cmd, unsigned long arg){	int	status;	if (the_menelaus->client->irq <= 0)		return -ENOIOCTLCMD;	switch (cmd) {	/* alarm IRQ */	case RTC_AIE_ON:		if (the_menelaus->rtc_control & RTC_CTRL_AL_EN)			return 0;		the_menelaus->rtc_control |= RTC_CTRL_AL_EN;		break;	case RTC_AIE_OFF:		if (!(the_menelaus->rtc_control & RTC_CTRL_AL_EN))			return 0;		the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;		break;	/* 1/second "update" IRQ */	case RTC_UIE_ON:		if (the_menelaus->uie)			return 0;		status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ);		status = menelaus_add_irq_work(MENELAUS_RTCTMR_IRQ,				menelaus_rtc_update_work);		if (status == 0)			the_menelaus->uie = 1;		return status;	case RTC_UIE_OFF:		if (!the_menelaus->uie)			return 0;		status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ);		if (status == 0)			the_menelaus->uie = 0;		return status;	default:		return -ENOIOCTLCMD;	}	return menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control);}#else#define menelaus_ioctl	NULL#endif/* REVISIT no compensation register support ... */static const struct rtc_class_ops menelaus_rtc_ops = {	.ioctl			= menelaus_ioctl,	.read_time		= menelaus_read_time,	.set_time		= menelaus_set_time,	.read_alarm		= menelaus_read_alarm,	.set_alarm		= menelaus_set_alarm,};static void menelaus_rtc_alarm_work(struct menelaus_chip *m){	/* report alarm */	local_irq_disable();	rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_AF);	local_irq_enable();	/* then disable it; alarms are oneshot */	the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;	menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control);}static inline void menelaus_rtc_init(struct menelaus_chip *m){	int	alarm = (m->client->irq > 0);	/* assume 32KDETEN pin is pulled high */	if (!(menelaus_read_reg(MENELAUS_OSC_CTRL) & 0x80)) {		dev_dbg(&m->client->dev, "no 32k oscillator\n");		return;	}	/* 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);	}	/* be sure RTC is enabled; allow 1/sec irqs; leave 12hr mode alone */	m->rtc_control = menelaus_read_reg(MENELAUS_RTC_CTRL);	if (!(m->rtc_control & RTC_CTRL_RTC_EN)			|| (m->rtc_control & RTC_CTRL_AL_EN)			|| (m->rtc_control & RTC_CTRL_EVERY_MASK)) {		if (!(m->rtc_control & RTC_CTRL_RTC_EN)) {			dev_warn(&m->client->dev, "rtc clock needs setting\n");			m->rtc_control |= RTC_CTRL_RTC_EN;		}		m->rtc_control &= ~RTC_CTRL_EVERY_MASK;		m->rtc_control &= ~RTC_CTRL_AL_EN;		menelaus_write_reg(MENELAUS_RTC_CTRL, m->rtc_control);	}	m->rtc = rtc_device_register(DRIVER_NAME,			&m->client->dev,			&menelaus_rtc_ops, THIS_MODULE);	if (IS_ERR(m->rtc)) {		if (alarm) {			menelaus_remove_irq_work(MENELAUS_RTCALM_IRQ);			device_init_wakeup(&m->client->dev, 0);		}		dev_err(&m->client->dev, "can't register RTC: %d\n",				(int) PTR_ERR(m->rtc));		the_menelaus->rtc = NULL;	}}#elsestatic inline void menelaus_rtc_init(struct menelaus_chip *m){	/* nothing */}#endif/*-----------------------------------------------------------------------*/static struct i2c_driver menelaus_i2c_driver;static int menelaus_probe(struct i2c_client *client){	struct menelaus_chip	*menelaus;	int			rev = 0, val;	int			err = 0;	struct menelaus_platform_data *menelaus_pdata =					client->dev.platform_data;	if (the_menelaus) {		dev_dbg(&client->dev, "only one %s for now\n",				DRIVER_NAME);		return -ENODEV;	}	menelaus = kzalloc(sizeof *menelaus, GFP_KERNEL);	if (!menelaus)		return -ENOMEM;	i2c_set_clientdata(client, menelaus);	the_menelaus = menelaus;	menelaus->client = client;	/* If a true probe check the device */	rev = menelaus_read_reg(MENELAUS_REV);	if (rev < 0) {		pr_err(DRIVER_NAME ": device not found");		err = -ENODEV;		goto fail1;	}	/* Ack and disable all Menelaus interrupts */	menelaus_write_reg(MENELAUS_INT_ACK1, 0xff);	menelaus_write_reg(MENELAUS_INT_ACK2, 0xff);	menelaus_write_reg(MENELAUS_INT_MASK1, 0xff);	menelaus_write_reg(MENELAUS_INT_MASK2, 0xff);	menelaus->mask1 = 0xff;	menelaus->mask2 = 0xff;	/* Set output buffer strengths */	menelaus_write_reg(MENELAUS_MCT_CTRL1, 0x73);	if (client->irq > 0) {		err = request_irq(client->irq, menelaus_irq, IRQF_DISABLED,				  DRIVER_NAME, menelaus);		if (err) {			dev_dbg(&client->dev,  "can't get IRQ %d, err %d\n",					client->irq, err);			goto fail1;		}	}	mutex_init(&menelaus->lock);	INIT_WORK(&menelaus->work, menelaus_work);	pr_info("Menelaus rev %d.%d\n", rev >> 4, rev & 0x0f);	val = menelaus_read_reg(MENELAUS_VCORE_CTRL1);	if (val < 0)		goto fail2;	if (val & (1 << 7))		menelaus->vcore_hw_mode = 1;	else		menelaus->vcore_hw_mode = 0;	if (menelaus_pdata != NULL && menelaus_pdata->late_init != NULL) {		err = menelaus_pdata->late_init(&client->dev);		if (err < 0)			goto fail2;	}	menelaus_rtc_init(menelaus);	return 0;fail2:	free_irq(client->irq, menelaus);	flush_scheduled_work();fail1:	kfree(menelaus);	return err;}static int __exit menelaus_remove(struct i2c_client *client){	struct menelaus_chip	*menelaus = i2c_get_clientdata(client);	free_irq(client->irq, menelaus);	kfree(menelaus);	i2c_set_clientdata(client, NULL);	the_menelaus = NULL;	return 0;}static struct i2c_driver menelaus_i2c_driver = {	.driver = {		.name		= DRIVER_NAME,	},	.probe		= menelaus_probe,	.remove		= __exit_p(menelaus_remove),};static int __init menelaus_init(void){	int res;	res = i2c_add_driver(&menelaus_i2c_driver);	if (res < 0) {		pr_err(DRIVER_NAME ": driver registration failed\n");		return res;	}	return 0;}static void __exit menelaus_exit(void){	i2c_del_driver(&menelaus_i2c_driver);	/* FIXME: Shutdown menelaus parts that can be shut down */}MODULE_AUTHOR("Texas Instruments, Inc. (and others)");MODULE_DESCRIPTION("I2C interface for Menelaus.");MODULE_LICENSE("GPL");module_init(menelaus_init);module_exit(menelaus_exit);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?