summaryrefslogtreecommitdiffstats
path: root/fs/devpts
AgeCommit message (Expand)Author
2018-08-21devpts: Convert to new IDA APIMatthew Wilcox
2018-03-14devpts: comment devpts_mntget()Christian Brauner
2018-03-14devpts: resolve devpts bind-mountsChristian Brauner
2018-03-14devpts: hoist out check for DEVPTS_SUPER_MAGICChristian Brauner
2018-01-31devpts: fix error handling in devpts_mntget()Eric Biggers
2017-08-24pty: Repair TIOCGPTPEEREric W. Biederman
2017-08-23Revert "pty: fix the cached path of the pty slave file descriptor in the master"Linus Torvalds
2017-08-17pty: fix the cached path of the pty slave file descriptor in the masterLinus Torvalds
2016-10-10Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/vir...Linus Torvalds
2016-09-27fs: Replace CURRENT_TIME with current_time() for inode timestampsDeepa Dinamani
2016-09-23devpts: Change the owner of /dev/pts/ptmx to the mounter of /dev/ptsEric W. Biederman
2016-09-23devpts: Remove sync_filesystemsEric W. Biederman
2016-09-23devpts: Make devpts_kill_sb safe if fsi is NULLEric W. Biederman
2016-09-23devpts: Simplify devpts_mount by using mount_nodevEric W. Biederman
2016-09-23devpts: Move the creation of /dev/pts/ptmx into fill_superEric W. Biederman
2016-09-23devpts: Move parse_mount_options into fill_superEric W. Biederman
2016-09-03devpts: return NULL pts 'priv' entry for non-devpts nodesLinus Torvalds
2016-06-23userns: Remove the now unnecessary FS_USERNS_DEV_MOUNT flagEric W. Biederman
2016-06-05devpts: Make each mount of devpts an independent filesystem.Eric W. Biederman
2016-04-26devpts: more pty driver interface cleanupsLinus Torvalds
2016-04-18devpts: clean up interface to pty driversLinus Torvalds
2016-02-06pty: make sure super_block is still valid in final /dev/tty closeHerton R. Krzesinski
2016-01-22wrappers for ->i_mutex accessAl Viro
2015-06-30devpts: if initialization failed, don't crash when opening /dev/ptmxJosh Triplett
2015-04-15VFS: normal filesystems (and lustre): d_inode() annotationsDavid Howells
2014-06-06fs/devpts/inode.c: convert printk to pr_foo()Fabian Frederick
2014-03-13fs: push sync_filesystem() down to the file system's remount_fs()Theodore Ts'o
2013-11-13devpts: plug the memory leak in kill_sbIlija Hadzic
2013-01-26userns: Allow the userns root to mount of devptsEric W. Biederman
2012-10-22TTY: devpts, document devpts inode operationsJiri Slaby
2012-10-22TTY: devpts, do not set driver_dataJiri Slaby
2012-10-22TTY: devpts, return created inode from devpts_pty_newJiri Slaby
2012-10-22TTY: devpts, don't care about TTY in devpts_get_ttyJiri Slaby
2012-07-14VFS: Pass mount flags to sget()David Howells
2012-05-15userns: Convert devpts to use kuid/kgid where appropriateEric W. Biederman
2012-03-21Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/vir...Linus Torvalds
2012-03-20switch open-coded instances of d_make_root() to new helperAl Viro
2012-01-24tty: rework pty count limitingKonstantin Khlebnikov
2012-01-24tty: move pty count limiting into devptsKonstantin Khlebnikov
2012-01-08devpts: fix double-free on mount failureAl Viro
2012-01-06vfs: switch ->show_options() to struct dentry *Al Viro
2011-11-02filesystems: add set_nlink()Miklos Szeredi
2011-11-02filesystems: add missing nlink wrappersMiklos Szeredi
2011-03-22fs/devpts/inode.c: correctly check d_alloc_name() return code in devpts_pty_n...Andrey Vagin
2011-03-21fs: devpts_pty_new() return -ENOMEM if dentry allocation failedAndrey Vagin
2010-10-29convert get_sb_single() usersAl Viro
2010-05-21Simplify devpts_get_sb() failure exitsAl Viro
2010-03-30include cleanup: Update gfp.h and slab.h includes to prepare for breaking imp...Tejun Heo
2009-12-11devpts_get_tty() should validate inodeSukadev Bhattiprolu
2009-09-23Move magic numbers into magic.hNick Black
a id='n367' href='#n367'>367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
/*
 * Driver for Motorola PCAP2 as present in EZX phones
 *
 * Copyright (C) 2006 Harald Welte <laforge@openezx.org>
 * Copyright (C) 2009 Daniel Ribeiro <drwyrm@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mfd/ezx-pcap.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
#include <linux/slab.h>

#define PCAP_ADC_MAXQ		8
struct pcap_adc_request {
	u8 bank;
	u8 ch[2];
	u32 flags;
	void (*callback)(void *, u16[]);
	void *data;
};

struct pcap_adc_sync_request {
	u16 res[2];
	struct completion completion;
};

struct pcap_chip {
	struct spi_device *spi;

	/* IO */
	u32 buf;
	struct mutex io_mutex;

	/* IRQ */
	unsigned int irq_base;
	u32 msr;
	struct work_struct isr_work;
	struct work_struct msr_work;
	struct workqueue_struct *workqueue;

	/* ADC */
	struct pcap_adc_request *adc_queue[PCAP_ADC_MAXQ];
	u8 adc_head;
	u8 adc_tail;
	struct mutex adc_mutex;
};

/* IO */
static int ezx_pcap_putget(struct pcap_chip *pcap, u32 *data)
{
	struct spi_transfer t;
	struct spi_message m;
	int status;

	memset(&t, 0, sizeof(t));
	spi_message_init(&m);
	t.len = sizeof(u32);
	spi_message_add_tail(&t, &m);

	pcap->buf = *data;
	t.tx_buf = (u8 *) &pcap->buf;
	t.rx_buf = (u8 *) &pcap->buf;
	status = spi_sync(pcap->spi, &m);

	if (status == 0)
		*data = pcap->buf;

	return status;
}

int ezx_pcap_write(struct pcap_chip *pcap, u8 reg_num, u32 value)
{
	int ret;

	mutex_lock(&pcap->io_mutex);
	value &= PCAP_REGISTER_VALUE_MASK;
	value |= PCAP_REGISTER_WRITE_OP_BIT
		| (reg_num << PCAP_REGISTER_ADDRESS_SHIFT);
	ret = ezx_pcap_putget(pcap, &value);
	mutex_unlock(&pcap->io_mutex);

	return ret;
}
EXPORT_SYMBOL_GPL(ezx_pcap_write);

int ezx_pcap_read(struct pcap_chip *pcap, u8 reg_num, u32 *value)
{
	int ret;

	mutex_lock(&pcap->io_mutex);
	*value = PCAP_REGISTER_READ_OP_BIT
		| (reg_num << PCAP_REGISTER_ADDRESS_SHIFT);

	ret = ezx_pcap_putget(pcap, value);
	mutex_unlock(&pcap->io_mutex);

	return ret;
}
EXPORT_SYMBOL_GPL(ezx_pcap_read);

int ezx_pcap_set_bits(struct pcap_chip *pcap, u8 reg_num, u32 mask, u32 val)
{
	int ret;
	u32 tmp = PCAP_REGISTER_READ_OP_BIT |
		(reg_num << PCAP_REGISTER_ADDRESS_SHIFT);

	mutex_lock(&pcap->io_mutex);
	ret = ezx_pcap_putget(pcap, &tmp);
	if (ret)
		goto out_unlock;

	tmp &= (PCAP_REGISTER_VALUE_MASK & ~mask);
	tmp |= (val & mask) | PCAP_REGISTER_WRITE_OP_BIT |
		(reg_num << PCAP_REGISTER_ADDRESS_SHIFT);

	ret = ezx_pcap_putget(pcap, &tmp);
out_unlock:
	mutex_unlock(&pcap->io_mutex);

	return ret;
}
EXPORT_SYMBOL_GPL(ezx_pcap_set_bits);

/* IRQ */
int irq_to_pcap(struct pcap_chip *pcap, int irq)
{
	return irq - pcap->irq_base;
}
EXPORT_SYMBOL_GPL(irq_to_pcap);

int pcap_to_irq(struct pcap_chip *pcap, int irq)
{
	return pcap->irq_base + irq;
}
EXPORT_SYMBOL_GPL(pcap_to_irq);

static void pcap_mask_irq(struct irq_data *d)
{
	struct pcap_chip *pcap = irq_data_get_irq_chip_data(d);

	pcap->msr |= 1 << irq_to_pcap(pcap, d->irq);
	queue_work(pcap->workqueue, &pcap->msr_work);
}

static void pcap_unmask_irq(struct irq_data *d)
{
	struct pcap_chip *pcap = irq_data_get_irq_chip_data(d);

	pcap->msr &= ~(1 << irq_to_pcap(pcap, d->irq));
	queue_work(pcap->workqueue, &pcap->msr_work);
}

static struct irq_chip pcap_irq_chip = {
	.name		= "pcap",
	.irq_disable	= pcap_mask_irq,
	.irq_mask	= pcap_mask_irq,
	.irq_unmask	= pcap_unmask_irq,
};

static void pcap_msr_work(struct work_struct *work)
{
	struct pcap_chip *pcap = container_of(work, struct pcap_chip, msr_work);

	ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr);
}

static void pcap_isr_work(struct work_struct *work)
{
	struct pcap_chip *pcap = container_of(work, struct pcap_chip, isr_work);
	struct pcap_platform_data *pdata = dev_get_platdata(&pcap->spi->dev);
	u32 msr, isr, int_sel, service;
	int irq;

	do {
		ezx_pcap_read(pcap, PCAP_REG_MSR, &msr);
		ezx_pcap_read(pcap, PCAP_REG_ISR, &isr);

		/* We can't service/ack irqs that are assigned to port 2 */
		if (!(pdata->config & PCAP_SECOND_PORT)) {
			ezx_pcap_read(pcap, PCAP_REG_INT_SEL, &int_sel);
			isr &= ~int_sel;
		}

		ezx_pcap_write(pcap, PCAP_REG_MSR, isr | msr);
		ezx_pcap_write(pcap, PCAP_REG_ISR, isr);

		local_irq_disable();
		service = isr & ~msr;
		for (irq = pcap->irq_base; service; service >>= 1, irq++) {
			if (service & 1)
				generic_handle_irq(irq);
		}
		local_irq_enable();
		ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr);
	} while (gpio_get_value(pdata->gpio));
}

static void pcap_irq_handler(struct irq_desc *desc)
{
	struct pcap_chip *pcap = irq_desc_get_handler_data(desc);

	desc->irq_data.chip->irq_ack(&desc->irq_data);
	queue_work(pcap->workqueue, &pcap->isr_work);
}

/* ADC */
void pcap_set_ts_bits(struct pcap_chip *pcap, u32 bits)
{
	u32 tmp;

	mutex_lock(&pcap->adc_mutex);
	ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp);
	tmp &= ~(PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR);
	tmp |= bits & (PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR);
	ezx_pcap_write(pcap, PCAP_REG_ADC, tmp);
	mutex_unlock(&pcap->adc_mutex);
}
EXPORT_SYMBOL_GPL(pcap_set_ts_bits);

static void pcap_disable_adc(struct pcap_chip *pcap)
{
	u32 tmp;

	ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp);
	tmp &= ~(PCAP_ADC_ADEN|PCAP_ADC_BATT_I_ADC|PCAP_ADC_BATT_I_POLARITY);
	ezx_pcap_write