Re: [Patch 06/10] mfd: PCAP driver for the Motorola EZX GSM mobile phones

From: pHilipp Zabel
Date: Tue Jul 08 2008 - 17:19:36 EST


On Tue, Jul 8, 2008 at 10:32 PM, Stefan Schmidt
<stefan@xxxxxxxxxxxxxxxxxx> wrote:
> Hello.
>
> On Tue, 2008-07-08 at 11:01, Daniel Ribeiro wrote:
>> Eric Miao escreveu:
>> > The irq2pcap[] array looks horrible to me. It's actually a sparse array.
>> > Isn't there a nice 1:1 mapping using a formular??
>> >
>> > Besides, the IRQ numbering scheme has now changed to a more generic way,
>> > I suggest to pull from Russell's latest git tree and rebase the IRQ
>> > part.
>>
>> Will do as you suggested and get rid of the arrays.
>>
>> > The following block of code:
>> >
>> > + if (pcap_data->cs >= 0) {
>> > + if (machine_is_ezx_a780() || machine_is_ezx_e680())
>> > + gpio_direction_output(pcap_data->cs, 1);
>> > + else
>> > + gpio_direction_output(pcap_data->cs, 0);
>> > + }
>> >
>> > has 3 occurrences in the driver (2 in ezx_ssp_pcap_putget, 1 in ezx_pcap_probe)
>> > which is good reason to fold this into the platform data.
>> >
>> > Well, if the above is done in platform data, I guess you won't mind another bit
>> > flag (e.g. PCAP_REDIRECT_IRQ or something alike) added in platform data, either
>>
>> Moved to platform data. What should PCAP_REDIRECT_IRQ flag do?
>
> Daniel fixed all of Philipps comments besides the voltage framework. From Eric
> comments we updated to the new IRQ part and moved to platform data.
>
> Open items we are still working on:
> o Voltage framework. Is this needed for the first merge

No way, it's not even in mainline yet.
I just wanted to point out that this might be interesting in the future.

> or can we switch to it with a later patch?
> o Get rid of the arrays
> o Eric, what do you like the PCAP_REDIRECT_IRQ flag to do?
>
> Besides the left items we updated and tested the patch below:
>
>
> Subject: [@num@/@total@] mfd: PCAP driver for the Motorola EZX GSM mobile phones
> To: sameo@xxxxxxxxxxxxxx
> Cc: philipp.zabel@xxxxxxxxx, linux-kernel@xxxxxxxxxxxxxxx
>
> The PCAP Asic as present on EZX phones is a multi function device with voltage
> regulators, irq expander, touch screen controller and audio codec.
> It is connected to the processor via SPI, this driver provides read/write
> functions to its registers and a irq demultiplexer.
>
> Signed-off-by: Daniel Ribeiro <drwyrm@xxxxxxxxx>
>
> PATCH FOLLOWS
> KernelVersion: 2.6-arm-git pxa branch
>
> Index: linux-2.6-arm/drivers/mfd/ezx-pcap.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6-arm/drivers/mfd/ezx-pcap.c
> @@ -0,0 +1,372 @@
> +/* Driver for Motorola PCAP2 as present in EZX phones
> + *
> + * This is both a SPI device driver for PCAP itself, as well as
> + * an IRQ demultiplexer for handling PCAP generated events such as
> + * headphone jack sense by downstream drivers.
> + *
> + * Copyright (C) 2006 Harald Welte <laforge@xxxxxxxxxxx>
> + * Copyright (C) 2007-2008 Daniel Ribeiro <drwyrm@xxxxxxxxx>
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel_stat.h>
> +#include <linux/proc_fs.h>
> +#include <linux/mfd/ezx-pcap.h>
> +#include <linux/delay.h>
> +#include <linux/gpio.h>
> +
> +#include <asm/hardware.h>
> +#include <asm/mach-types.h>
> +
> +#include <asm/arch/ssp.h>
> +#include <asm/arch/pxa-regs.h>
> +#include <asm/arch/regs-ssp.h>
> +#include <asm/arch/mfp-pxa27x.h>
> +#include <asm/arch/irqs.h>
> +#include <asm/mach/irq.h>
> +
> +static DEFINE_SPINLOCK(ezx_ssp_lock);
> +static struct ssp_dev ezx_ssp_dev;
> +static struct ssp_state ezx_ssp_state;
> +static struct pcap_platform_data *pcap_data;
> +static int pcap_irq;
> +
> +static u_int32_t ezx_ssp_pcap_putget(u_int32_t data)
> +{
> + unsigned long flag;
> + u_int32_t ret = 0;
> +
> + spin_lock_irqsave(&ezx_ssp_lock, flag);
> + if (pcap_data->cs >= 0) {
> + if (pcap_data->config & CS_INVERTED)
> + gpio_set_value(pcap_data->cs, 0);
> + else
> + gpio_set_value(pcap_data->cs, 1);
> + }
> +
> + ssp_write_word(&ezx_ssp_dev, data);
> + ssp_read_word(&ezx_ssp_dev, &ret);
> +
> + if (pcap_data->cs >= 0) {
> + if (pcap_data->config & CS_INVERTED)
> + gpio_set_value(pcap_data->cs, 1);
> + else
> + gpio_set_value(pcap_data->cs, 0);
> + }
> +
> + spin_unlock_irqrestore(&ezx_ssp_lock, flag);
> +
> + return ret;
> +}
> +
> +void ezx_pcap_write(u_int8_t reg_num, u_int32_t value)
> +{
> + value &= PCAP_REGISTER_VALUE_MASK;
> + value |= PCAP_REGISTER_WRITE_OP_BIT
> + | (reg_num<<PCAP_REGISTER_ADDRESS_SHIFT);
> +
> + ezx_ssp_pcap_putget(value);
> +}
> +EXPORT_SYMBOL_GPL(ezx_pcap_write);
> +
> +void ezx_pcap_read(u_int8_t reg_num, u_int32_t *value)
> +{
> + u_int32_t frame = PCAP_REGISTER_READ_OP_BIT
> + | (reg_num<<PCAP_REGISTER_ADDRESS_SHIFT);
> +
> + *value = ezx_ssp_pcap_putget(frame);
> +}
> +EXPORT_SYMBOL_GPL(ezx_pcap_read);
> +
> +void ezx_pcap_set_sw(u_int8_t sw, u_int8_t what, u_int8_t val)
> +{
> + u_int32_t tmp;
> +
> + ezx_pcap_read(PCAP_REG_LOWPWR, &tmp);
> + tmp &= ~(0xf << (sw + what));
> + tmp |= ((val & 0xf) << (sw + what));
> + ezx_pcap_write(PCAP_REG_LOWPWR, tmp);
> +}
> +EXPORT_SYMBOL_GPL(ezx_pcap_set_sw);
> +
> +static u_int8_t vaux_table[][8] = {
> + /* EN INDEX MASK STBY LOWPWR */
> + [VAUX1] = { 1, 2, 0x3, 22, 23, },
> + [VAUX2] = { 4, 5, 0x3, 0, 1, },
> + [VAUX3] = { 7, 8, 0xf, 2, 3, },
> + [VAUX4] = { 12, 13, 0x3, 4, 5, },
> + [VSIM] = { 17, 18, 0x1, 0xff, 6, },
> + [VSIM2] = { 16, 0xff, 0x0, 0xff, 7, },
> + [VVIB] = { 19, 20, 0x3, 0xff, 0xff, },
> + [VC] = { 0xff, 0xff, 0x0, 24, 0xff, },
> +};
> +
> +int ezx_pcap_set_vaux(u_int8_t vaux, u_int8_t what, u_int8_t val)
> +{
> + u_int8_t reg, shift, mask;
> + u_int32_t tmp;
> +
> + switch (what) {
> + case VAUX_EN:
> + reg = PCAP_REG_AUXVREG;
> + shift = vaux_table[vaux][VAUX_EN];
> + mask = 0x1;
> + break;
> + case VAUX_VAL:
> + reg = PCAP_REG_AUXVREG;
> + shift = vaux_table[vaux][VAUX_VAL];
> + mask = vaux_table[vaux][VAUX_MASK];
> + break;
> + case VAUX_STBY:
> + if (vaux == VAUX1) /* exception */
> + reg = PCAP_REG_AUXVREG;
> + else
> + reg = PCAP_REG_LOWPWR;
> + shift = vaux_table[vaux][VAUX_STBY];
> + mask = 0x1;
> + break;
> + case VAUX_LOWPWR:
> + if (vaux == VAUX1)
> + reg = PCAP_REG_AUXVREG;
> + else
> + reg = PCAP_REG_LOWPWR;
> + shift = vaux_table[vaux][VAUX_LOWPWR];
> + mask = 0x1;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + /* invalid setting */
> + if (shift == 0xff || val > mask)
> + return -EINVAL;
> +
> + ezx_pcap_read(reg, &tmp);
> + tmp &= ~(mask << shift);
> + tmp |= ((val & mask) << shift);
> + ezx_pcap_write(reg, tmp);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(ezx_pcap_set_vaux);
> +
> +/* IRQ Handling */
> +
> +/* Array indexed by BIT POSITION of PCAP register, returns IRQ number */
> +static unsigned int pcap2irq[] = {
> + [0] = EZX_IRQ_ADCDONE,
> + [1] = EZX_IRQ_TS,
> + [2] = EZX_IRQ_1HZ, /* 1HZ */
> + [3] = EZX_IRQ_WH, /* WH */
> + [4] = EZX_IRQ_WL, /* WL */
> + [5] = EZX_IRQ_TODA, /* TODA */
> + [6] = EZX_IRQ_USB4V,
> + [7] = EZX_IRQ_ONOFF, /* ONOFF */
> + [8] = EZX_IRQ_ONOFF2, /* ONOFF2 */
> + [9] = EZX_IRQ_USB1V,
> + [10] = EZX_IRQ_MOBPORT, /* MOBPORT */
> + [11] = EZX_IRQ_MIC,
> + [12] = EZX_IRQ_HEADJACK,
> + [13] = EZX_IRQ_ST, /* ST */
> + [14] = EZX_IRQ_PC, /* PC */
> + [15] = EZX_IRQ_WARM, /* WARM */
> + [16] = EZX_IRQ_EOL, /* EOL */
> + [17] = EZX_IRQ_CLK, /* CLK */
> + [18] = EZX_IRQ_SYSRST, /* SYSRST */
> + [19] = 0,
> + [20] = EZX_IRQ_ADCDONE2,
> + [21] = EZX_IRQ_SOFTRESET, /* SOFTRESET */
> + [22] = EZX_IRQ_MNEXB, /* MNEXB */
> +};
> +
> +/* Array indexed by IRQ NUMBER, returns PCAP absolute value */
> +static unsigned int irq2pcap[] = {
> + [EZX_IRQ_MNEXB] = PCAP_IRQ_MNEXB,
> + [EZX_IRQ_SOFTRESET] = PCAP_IRQ_SOFTRESET,
> + [EZX_IRQ_SYSRST] = PCAP_IRQ_SYSRST,
> + [EZX_IRQ_CLK] = PCAP_IRQ_CLK,
> + [EZX_IRQ_EOL] = PCAP_IRQ_EOL,
> + [EZX_IRQ_WARM] = PCAP_IRQ_WARM,
> + [EZX_IRQ_PC] = PCAP_IRQ_PC,
> + [EZX_IRQ_ST] = PCAP_IRQ_ST,
> + [EZX_IRQ_MOBPORT] = PCAP_IRQ_MOBPORT,
> + [EZX_IRQ_ONOFF2] = PCAP_IRQ_ONOFF2,
> + [EZX_IRQ_ONOFF] = PCAP_IRQ_ONOFF,
> + [EZX_IRQ_TODA] = PCAP_IRQ_TODA,
> + [EZX_IRQ_WL] = PCAP_IRQ_WL,
> + [EZX_IRQ_WH] = PCAP_IRQ_WH,
> + [EZX_IRQ_1HZ] = PCAP_IRQ_1HZ,
> + [EZX_IRQ_USB4V] = PCAP_IRQ_USB4V,
> + [EZX_IRQ_USB1V] = PCAP_IRQ_USB1V,
> + [EZX_IRQ_HEADJACK] = PCAP_IRQ_A1,
> + [EZX_IRQ_MIC] = PCAP_IRQ_MB2,
> + [EZX_IRQ_TS] = PCAP_IRQ_TS,
> + [EZX_IRQ_ADCDONE] = PCAP_IRQ_ADCDONE,
> + [EZX_IRQ_ADCDONE2] = PCAP_IRQ_ADCDONE2,
> +};
> +
> +static void pcap_ack_irq(unsigned int irq)
> +{
> + ezx_pcap_write(PCAP_REG_ISR, irq2pcap[irq]);
> +}
> +
> +static void pcap_mask_irq(unsigned int irq)
> +{
> + u_int32_t reg;
> + unsigned long flag;
> +
> + spin_lock_irqsave(&ezx_ssp_lock, flag);
> + ezx_pcap_read(PCAP_REG_MSR, &reg);
> + reg |= irq2pcap[irq];
> + ezx_pcap_write(PCAP_REG_MSR, reg);
> + spin_unlock_irqrestore(&ezx_ssp_lock, flag);
> +}
> +
> +static void pcap_unmask_irq(unsigned int irq)
> +{
> + u_int32_t tmp;
> + unsigned long flag;
> +
> + spin_lock_irqsave(&ezx_ssp_lock, flag);
> + ezx_pcap_read(PCAP_REG_MSR, &tmp);
> + tmp &= ~irq2pcap[irq];
> + ezx_pcap_write(PCAP_REG_MSR, tmp);
> + spin_unlock_irqrestore(&ezx_ssp_lock, flag);
> +}
> +
> +static struct irq_chip pcap_chip = {
> + .name = "ezx-pcap",
> + .ack = pcap_ack_irq,
> + .mask = pcap_mask_irq,
> + .unmask = pcap_unmask_irq,
> +};
> +
> +/* handler for interrupt received from PCAP via GPIO */
> +static void pcap_irq_demux_handler(unsigned int irq, struct irq_desc *desc)
> +{
> + int i;
> + u_int32_t isr;
> +
> + desc->chip->ack(irq);
> + ezx_pcap_read(PCAP_REG_ISR, &isr);
> + for (i = ARRAY_SIZE(pcap2irq)-1; i >= 0; i--) {
> + unsigned int pirq = pcap2irq[i];
> + if (!(isr & irq2pcap[pirq]))
> + continue;
> + desc = &irq_desc[pirq];
> + desc_handle_irq(pirq, desc);
> + }
> +}
> +
> +static int ezx_pcap_remove(struct platform_device *pdev)
> +{
> + int irq;
> +
> + set_irq_chained_handler(pcap_irq, NULL);
> + for (irq = EZX_IRQ(0); irq <= EZX_IRQ(21); irq++) {
> + set_irq_chip(irq, NULL);
> + set_irq_handler(irq, NULL);
> + set_irq_flags(irq, 0);
> + }
> + ssp_exit(&ezx_ssp_dev);
> +
> + return 0;
> +}
> +
> +static int __init ezx_pcap_probe(struct platform_device *pdev)
> +{
> + unsigned int ret, irq;
> +
> + pcap_data = pdev->dev.platform_data;
> + if (pcap_data->cs >= 0) {
> + if (pcap_data->config & CS_INVERTED)
> + gpio_direction_output(pcap_data->cs, 1);
> + else
> + gpio_direction_output(pcap_data->cs, 0);
> + }
> + pcap_irq = platform_get_irq(pdev, 0);
> + if (pcap_irq < 0) {
> + printk(KERN_ERR "Unable to get IRQ for pcap!\n");
> + return pcap_irq;
> + }
> +
> + ret = ssp_init(&ezx_ssp_dev, pcap_data->port, 0);
> + if (ret) {
> + printk(KERN_ERR "Unable to register SSP handler!\n");
> + return ret;
> + }
> +
> + ssp_disable(&ezx_ssp_dev);
> + ssp_config(&ezx_ssp_dev,
> + (SSCR0_Motorola | SSCR0_DataSize(16) | SSCR0_EDSS),
> + (SSCR1_TxTresh(1) | SSCR1_RxTresh(1)),
> + 0, SSCR0_SerClkDiv(pcap_data->clk));
> + ssp_enable(&ezx_ssp_dev);
> +
> + if (pcap_data->init)
> + pcap_data->init();
> +
> + /* set up interrupt demultiplexing code for PCAP2 irqs */
> + for (irq = EZX_IRQ(0); irq <= EZX_IRQ(21); irq++) {
> + set_irq_chip(irq, &pcap_chip);
> + set_irq_handler(irq, handle_level_irq);
> + set_irq_flags(irq, IRQF_VALID);
> + }
> + set_irq_type(pcap_irq, IRQ_TYPE_EDGE_RISING);
> + set_irq_chained_handler(pcap_irq, pcap_irq_demux_handler);
> + set_irq_wake(pcap_irq, 1);
> +
> + /* mask/ack all PCAP interrupts */
> + ezx_pcap_write(PCAP_REG_MSR, PCAP_MASK_ALL_INTERRUPT);
> + ezx_pcap_write(PCAP_REG_ISR, PCAP_CLEAR_INTERRUPT_REGISTER);
> +
> +
> + printk(KERN_INFO "ezx-pcap: ssp driver registered\n");
> + return ret;
> +}
> +
> +#ifdef CONFIG_PM
> +static int ezx_pcap_suspend(struct platform_device *dev, pm_message_t state)
> +{
> + ssp_flush(&ezx_ssp_dev);
> + ssp_save_state(&ezx_ssp_dev, &ezx_ssp_state);
> + ssp_disable(&ezx_ssp_dev);
> + return 0;
> +}
> +
> +static int ezx_pcap_resume(struct platform_device *dev)
> +{
> + ssp_restore_state(&ezx_ssp_dev, &ezx_ssp_state);
> + ssp_enable(&ezx_ssp_dev);
> +
> + return 0;
> +}
> +#endif
> +
> +static struct platform_driver ezxpcap_driver = {
> + .probe = ezx_pcap_probe,
> + .remove = ezx_pcap_remove,
> +#ifdef CONFIG_PM
> + .suspend = ezx_pcap_suspend,
> + .resume = ezx_pcap_resume,
> +#endif
> + .driver = {
> + .name = "ezx-pcap",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init ezx_pcap_init(void)
> +{
> + return platform_driver_register(&ezxpcap_driver);
> +}
> +
> +subsys_initcall(ezx_pcap_init);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Harald Welte");
> +MODULE_DESCRIPTION("Motorola PCAP2 ASIC Driver");
> Index: linux-2.6-arm/include/linux/mfd/ezx-pcap.h
> ===================================================================
> --- /dev/null
> +++ linux-2.6-arm/include/linux/mfd/ezx-pcap.h
> @@ -0,0 +1,249 @@
> +/*
> + * Copyright 2007 Daniel Ribeiro <drwyrm@xxxxxxxxx>
> + *
> + * For further information, please see http://wiki.openezx.org/PCAP2
> + */
> +
> +#ifndef EZX_PCAP_H
> +#define EZX_PCAP_H
> +
> +struct pcap_platform_data {
> + int port; /* SSP port */
> + int cs; /* CS gpio */
> + int config;
> + int clk;
> + int (*init)(void); /* board specific driver init */
> +};
> +
> +#define CS_INVERTED 1
> +
> +#define PCAP_REGISTER_WRITE_OP_BIT 0x80000000
> +#define PCAP_REGISTER_READ_OP_BIT 0x00000000
> +
> +#define PCAP_REGISTER_VALUE_MASK 0x01ffffff
> +#define PCAP_REGISTER_ADDRESS_MASK 0x7c000000
> +#define PCAP_REGISTER_ADDRESS_SHIFT 26
> +#define PCAP_REGISTER_NUMBER 32
> +#define PCAP_CLEAR_INTERRUPT_REGISTER 0x01ffffff
> +#define PCAP_MASK_ALL_INTERRUPT 0x01ffffff
> +
> +#define pbit(reg, bit) ((reg << PCAP_REGISTER_ADDRESS_SHIFT) | bit)
> +
> +/* registers acessible by both pcap ports */
> +#define PCAP_REG_ISR 0x0 /* Interrupt Status */
> +#define PCAP_REG_MSR 0x1 /* Interrupt Mask */
> +#define PCAP_REG_PSTAT 0x2 /* Processor Status */
> +#define PCAP_REG_VREG2 0x6 /* Regulator Bank 2 Control */
> +#define PCAP_REG_AUXVREG 0x7 /* Auxiliary Regulator Control */
> +#define PCAP_REG_BATT 0x8 /* Battery Control */
> +#define PCAP_REG_ADC 0x9 /* AD Control */
> +#define PCAP_REG_ADR 0xa /* AD Result */
> +#define PCAP_REG_CODEC 0xb /* Audio Codec Control */
> +#define PCAP_REG_RX_AMPS 0xc /* RX Audio Amplifiers Control */
> +#define PCAP_REG_ST_DAC 0xd /* Stereo DAC Control */
> +#define PCAP_REG_BUSCTRL 0x14 /* Connectivity Control */
> +#define PCAP_REG_PERIPH 0x15 /* Peripheral Control */
> +#define PCAP_REG_LOWPWR 0x18 /* Regulator Low Power Control */
> +#define PCAP_REG_TX_AMPS 0x1a /* TX Audio Amplifiers Control */
> +#define PCAP_REG_GP 0x1b /* General Purpose */
> +
> +/* registers acessible by pcap port 1 only (a1200, e2 & e6) */
> +#define PCAP_REG_INT_SEL 0x3 /* Interrupt Select */
> +#define PCAP_REG_SWCTRL 0x4 /* Switching Regulator Control */
> +#define PCAP_REG_VREG1 0x5 /* Regulator Bank 1 Control */
> +#define PCAP_REG_RTC_TOD 0xe /* RTC Time of Day */
> +#define PCAP_REG_RTC_TODA 0xf /* RTC Time of Day Alarm */
> +#define PCAP_REG_RTC_DAY 0x10 /* RTC Day */
> +#define PCAP_REG_RTC_DAYA 0x11 /* RTC Day Alarm */
> +#define PCAP_REG_MTRTMR 0x12 /* AD Monitor Timer */
> +#define PCAP_REG_PWR 0x13 /* Power Control */
> +#define PCAP_REG_AUXVREG_MASK 0x16 /* Auxiliary Regulator Mask */
> +#define PCAP_REG_VENDOR_REV 0x17
> +#define PCAP_REG_PERIPH_MASK 0x19 /* Peripheral Mask */
> +
> +/* interrupts - registers 0x0, 0x1, 0x2, 0x3 */
> +#define PCAP_IRQ_ADCDONE (1 << 0) /* AD Conversion Done Port 1 */
> +#define PCAP_IRQ_TS (1 << 1) /* Touch Screen */
> +#define PCAP_IRQ_1HZ (1 << 2) /* 1HZ Timer */
> +#define PCAP_IRQ_WH (1 << 3) /* "...high"??? */
> +#define PCAP_IRQ_WL (1 << 4) /* "...low"??? */
> +#define PCAP_IRQ_TODA (1 << 5) /* RTC Time Of Day?
> + (see "RTC_TODA") */
> +#define PCAP_IRQ_USB4V (1 << 6) /* USB OTG */
> +#define PCAP_IRQ_ONOFF (1 << 7) /* in blob: "ONOFFSNS" */
> +#define PCAP_IRQ_ONOFF2 (1 << 8) /* in blob: "ONOFFSNS2" */
> +#define PCAP_IRQ_USB1V (1 << 9) /* USB below 1volt???
> + in blob: "USBDET_1V" */
> +#define PCAP_IRQ_MOBPORT (1 << 10) /* GSM-related?? ("mobport",
> + see 958_MotDoc.pdf); in blob: "MOBSENSB" */
> +#define PCAP_IRQ_MB2 (1 << 11) /* Mic; in blob: "MB2SNS" */
> +#define PCAP_IRQ_A1 (1 << 12) /* Audio jack;
> + in blob: "A1SNS" */
> +#define PCAP_IRQ_ST (1 << 13) /* called "MSTB" in blob */
> +#define PCAP_IRQ_PC (1 << 14)
> +#define PCAP_IRQ_WARM (1 << 15)
> +#define PCAP_IRQ_EOL (1 << 16) /* battery End Of Life???
> + (see below); in blob: "EOL_STAT" */
> +#define PCAP_IRQ_CLK (1 << 17) /* called "CLK_STAT" in blob */
> +#define PCAP_IRQ_SYSRST (1 << 18)
> +#define PCAP_IRQ_ADCDONE2 (1 << 20) /* AD Conversion Done Port 2 */
> +#define PCAP_IRQ_SOFTRESET (1 << 21)
> +#define PCAP_IRQ_MNEXB (1 << 22)
> +
> +/* register VREG2 (0x6) */
> +#define PCAP_VREG2_V1_STBY (1 << 0)
> +#define PCAP_VREG2_V2_STBY (1 << 1)
> +/* V3, SRAM: */
> +#define PCAP_VREG2_V3_STBY (1 << 2)
> +#define PCAP_VREG2_V4_STBY (1 << 3)
> +#define PCAP_VREG2_V5_STBY (1 << 4)
> +/* V6, E680 I2C camera?: */
> +#define PCAP_VREG2_V6_STBY (1 << 5)
> +#define PCAP_VREG2_V7_STBY (1 << 6)
> +/* V8, PLL: */
> +#define PCAP_VREG2_V8_STBY (1 << 7)
> +#define PCAP_VREG2_V9_STBY (1 << 8)
> +#define PCAP_VREG2_V10_STBY (1 << 9)
> +#define PCAP_VREG2_V1_LOWPWR (1 << 10)
> +#define PCAP_VREG2_V2_LOWPWR (1 << 11)
> +/* V3, SRAM: */
> +#define PCAP_VREG2_V3_LOWPWR (1 << 12)
> +#define PCAP_VREG2_V4_LOWPWR (1 << 13)
> +#define PCAP_VREG2_V5_LOWPWR (1 << 14)
> +/* V6, E680 I2C camera?: */
> +#define PCAP_VREG2_V6_LOWPWR (1 << 15)
> +#define PCAP_VREG2_V7_LOWPWR (1 << 16)
> +/* V8, PLL: */
> +#define PCAP_VREG2_V8_LOWPWR (1 << 17)
> +#define PCAP_VREG2_V9_LOWPWR (1 << 18)
> +#define PCAP_VREG2_V10_LOWPWR (1 << 19)
> +
> +/* register AUXVREG (0x7) */
> +#define VAUX1 0
> +#define VAUX2 1
> +#define VAUX3 2
> +#define VAUX4 3
> +#define VSIM 4
> +#define VSIM2 5
> +#define VVIB 6
> +#define VC 7
> +
> +#define VAUX_EN 0
> +#define VAUX_VAL 1
> +#define VAUX_MASK 2
> +#define VAUX_STBY 3
> +#define VAUX_LOWPWR 4
> +
> +#define PCAP_BATT_DAC_MASK 0x000000ff
> +#define PCAP_BATT_DAC_SHIFT 0
> +#define PCAP_BATT_B_FDBK (1 << 8)
> +#define PCAP_BATT_EXT_ISENSE (1 << 9)
> +#define PCAP_BATT_V_COIN_MASK 0x00003c00
> +#define PCAP_BATT_V_COIN_SHIFT 10
> +#define PCAP_BATT_I_COIN (1 << 14)
> +#define PCAP_BATT_COIN_CH_EN (1 << 15)
> +#define PCAP_BATT_EOL_SEL_MASK 0x000e0000
> +#define PCAP_BATT_EOL_SEL_SHIFT 17
> +#define PCAP_BATT_EOL_CMP_EN (1 << 20)
> +#define PCAP_BATT_BATT_DET_EN (1 << 21)
> +#define PCAP_BATT_THERMBIAS_CTRL (1 << 22)
> +
> +#define PCAP_ADC_ADEN (1 << 0)
> +#define PCAP_ADC_RAND (1 << 1)
> +#define PCAP_ADC_AD_SEL1 (1 << 2)
> +#define PCAP_ADC_AD_SEL2 (1 << 3)
> +#define PCAP_ADC_ADA1_MASK 0x00000070
> +#define PCAP_ADC_ADA1_SHIFT 4
> +#define PCAP_ADC_ADA2_MASK 0x00000380
> +#define PCAP_ADC_ADA2_SHIFT 7
> +#define PCAP_ADC_ATO_MASK 0x00003c00
> +#define PCAP_ADC_ATO_SHIFT 10
> +#define PCAP_ADC_ATOX (1 << 14)
> +#define PCAP_ADC_MTR1 (1 << 15)
> +#define PCAP_ADC_MTR2 (1 << 16)
> +#define PCAP_ADC_TS_M_MASK 0x000e0000
> +#define PCAP_ADC_TS_M_SHIFT 17
> +#define PCAP_ADC_TS_REF_LOWPWR (1 << 20)
> +#define PCAP_ADC_TS_REFENB (1 << 21)
> +#define PCAP_ADC_BATT_I_POLARITY (1 << 22)
> +#define PCAP_ADC_BATT_I_ADC (1 << 23)
> +
> +#define PCAP_ADR_ADD1_MASK 0x000003ff
> +#define PCAP_ADR_ADD1_SHIFT 0
> +#define PCAP_ADR_ADD2_MASK 0x000ffc00
> +#define PCAP_ADR_ADD2_SHIFT 10
> +#define PCAP_ADR_ADINC1 (1 << 20)
> +#define PCAP_ADR_ADINC2 (1 << 21)
> +#define PCAP_ADR_ASC (1 << 22)
> +#define PCAP_ADR_ONESHOT (1 << 23)
> +
> +#define PCAP_BUSCTRL_FSENB (1 << 0)
> +#define PCAP_BUSCTRL_USB_SUSPEND (1 << 1)
> +#define PCAP_BUSCTRL_USB_PU (1 << 2)
> +#define PCAP_BUSCTRL_USB_PD (1 << 3)
> +#define PCAP_BUSCTRL_VUSB_EN (1 << 4)
> +#define PCAP_BUSCTRL_USB_PS (1 << 5)
> +#define PCAP_BUSCTRL_VUSB_MSTR_EN (1 << 6)
> +#define PCAP_BUSCTRL_VBUS_PD_ENB (1 << 7)
> +#define PCAP_BUSCTRL_CURRLIM (1 << 8)
> +#define PCAP_BUSCTRL_RS232ENB (1 << 9)
> +#define PCAP_BUSCTRL_RS232_DIR (1 << 10)
> +#define PCAP_BUSCTRL_SE0_CONN (1 << 11)
> +#define PCAP_BUSCTRL_USB_PDM (1 << 12)
> +#define PCAP_BUSCTRL_BUS_PRI_ADJ (1 << 24)
> +
> +#define PCAP_BIT_PERIPH_BL_CTRL0 0x54000001
> +#define PCAP_BIT_PERIPH_BL_CTRL1 0x54000002
> +#define PCAP_BIT_PERIPH_BL_CTRL2 0x54000004
> +#define PCAP_BIT_PERIPH_BL_CTRL3 0x54000008
> +#define PCAP_BIT_PERIPH_BL_CTRL4 0x54000010
> +#define PCAP_BIT_PERIPH_LEDR_EN 0x54000020
> +#define PCAP_BIT_PERIPH_LEDG_EN 0x54000040
> +#define PCAP_BIT_PERIPH_LEDR_CTRL0 0x54000080
> +#define PCAP_BIT_PERIPH_LEDR_CTRL1 0x54000100
> +#define PCAP_BIT_PERIPH_LEDR_CTRL2 0x54000200
> +#define PCAP_BIT_PERIPH_LEDR_CTRL3 0x54000400
> +#define PCAP_BIT_PERIPH_LEDG_CTRL0 0x54000800
> +#define PCAP_BIT_PERIPH_LEDG_CTRL1 0x54001000
> +#define PCAP_BIT_PERIPH_LEDG_CTRL2 0x54002000
> +#define PCAP_BIT_PERIPH_LEDG_CTRL3 0x54004000
> +#define PCAP_BIT_PERIPH_LEDR_I0 0x54008000
> +#define PCAP_BIT_PERIPH_LEDR_I1 0x54010000
> +#define PCAP_BIT_PERIPH_LEDG_I0 0x54020000
> +#define PCAP_BIT_PERIPH_LEDG_I1 0x54040000
> +#define PCAP_BIT_PERIPH_SKIP 0x54080000
> +#define PCAP_BIT_PERIPH_BL2_CTRL0 0x54100000
> +#define PCAP_BIT_PERIPH_BL2_CTRL1 0x54200000
> +#define PCAP_BIT_PERIPH_BL2_CTRL2 0x54400000
> +#define PCAP_BIT_PERIPH_BL2_CTRL3 0x54800000
> +#define PCAP_BIT_PERIPH_BL2_CTRL4 0x55000000
> +
> +/* LOWPWR */
> +#define SW1 8
> +#define SW2 16
> +
> +#define SW_MODE 0
> +#define SW_VOLTAGE 4
> +
> +#define SW_VOLTAGE_900 0x0
> +#define SW_VOLTAGE_950 0x1
> +#define SW_VOLTAGE_1000 0x2
> +#define SW_VOLTAGE_1050 0x3
> +#define SW_VOLTAGE_1100 0x4
> +#define SW_VOLTAGE_1150 0x5
> +#define SW_VOLTAGE_1200 0x6
> +#define SW_VOLTAGE_1250 0x7
> +#define SW_VOLTAGE_1300 0x8
> +#define SW_VOLTAGE_1350 0x9
> +#define SW_VOLTAGE_1400 0xa
> +#define SW_VOLTAGE_1500 0xb
> +#define SW_VOLTAGE_1600 0xc
> +#define SW_VOLTAGE_1875 0xd
> +#define SW_VOLTAGE_2250 0xe
> +#define SW_VOLTAGE_4400 0xf
> +
> +void ezx_pcap_write(u_int8_t, u_int32_t);
> +void ezx_pcap_read(u_int8_t, u_int32_t *);
> +void ezx_pcap_set_sw(u_int8_t, u_int8_t, u_int8_t);
> +int ezx_pcap_set_vaux(u_int8_t, u_int8_t, u_int8_t);
> +#endif
> Index: linux-2.6-arm/drivers/mfd/Kconfig
> ===================================================================
> --- linux-2.6-arm.orig/drivers/mfd/Kconfig
> +++ linux-2.6-arm/drivers/mfd/Kconfig
> @@ -49,6 +49,13 @@
> help
> Support for Toshiba Mobile IO Controller TC6393XB
>
> +config EZX_PCAP
> + bool "PCAP Support"
> + depends on PXA_SSP && PXA_EZX
> + help
> + This enables the PCAP ASIC present on EZX Phones. This is
> + needed for MMC, TouchScreen, Sound, USB, etc..
> +
> endmenu
>
> menu "Multimedia Capabilities Port drivers"
> Index: linux-2.6-arm/drivers/mfd/Makefile
> ===================================================================
> --- linux-2.6-arm.orig/drivers/mfd/Makefile
> +++ linux-2.6-arm/drivers/mfd/Makefile
> @@ -12,6 +12,8 @@
>
> obj-$(CONFIG_MFD_CORE) += mfd-core.o
>
> +obj-$(CONFIG_EZX_PCAP) += ezx-pcap.o
> +
> obj-$(CONFIG_MCP) += mcp-core.o
> obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
> obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o
> Index: linux-2.6-arm/arch/arm/mach-pxa/Kconfig
> ===================================================================
> --- linux-2.6-arm.orig/arch/arm/mach-pxa/Kconfig
> +++ linux-2.6-arm/arch/arm/mach-pxa/Kconfig
> @@ -251,6 +251,8 @@
> select PXA27x
> select IWMMXT
> select HAVE_PWM
> + select PXA_SSP
> + select EZX_PCAP
>
> config MACH_EZX_A780
> bool "Motorola EZX A780"
> Index: linux-2.6-arm/arch/arm/mach-pxa/ezx.c
> ===================================================================
> --- linux-2.6-arm.orig/arch/arm/mach-pxa/ezx.c
> +++ linux-2.6-arm/arch/arm/mach-pxa/ezx.c
> @@ -15,7 +15,9 @@
> #include <linux/init.h>
> #include <linux/platform_device.h>
> #include <linux/delay.h>
> +#include <linux/gpio.h>
> #include <linux/pwm_backlight.h>
> +#include <linux/mfd/ezx-pcap.h>
>
> #include <asm/setup.h>
> #include <asm/arch/pxafb.h>
> @@ -87,7 +89,54 @@
> .lcd_conn = LCD_COLOR_TFT_18BPP,
> };
>
> +/* PCAP */
> +static int ezx_pcap_init(void)
> +{
> + /* disable all voltage regulators */
> + ezx_pcap_write(PCAP_REG_AUXVREG, 0);
> +
> + /* set SW1 sleep to keep SW1 1.3v in sync mode */
> + /* SW1 active in sync mode */
> + ezx_pcap_set_sw(SW1, SW_MODE, 0x1);
> +
> + /* set core voltage */
> + ezx_pcap_set_sw(SW1, SW_VOLTAGE, SW_VOLTAGE_1250);
> +
> + /* redirect all interrupts to AP */
> + if (!(machine_is_ezx_a780() || machine_is_ezx_e680()))
> + ezx_pcap_write(PCAP_REG_INT_SEL, 0);
> +
> + return 0;
> +}
> +
> +static struct pcap_platform_data ezx_pcap_platform_data = {
> + .port = 1,
> + .cs = 24,
> + .clk = 1,
> + .config = 0,
> + .init = ezx_pcap_init,
> +};
> +
> +static struct resource ezx_pcap_resources[] = {
> + [0] = {
> + .start = IRQ_GPIO1,
> + .end = IRQ_GPIO1,
> + .flags = IORESOURCE_IRQ,
> + },
> +};
> +
> +struct platform_device ezx_pcap_device = {
> + .name = "ezx-pcap",
> + .id = -1,
> + .num_resources = ARRAY_SIZE(ezx_pcap_resources),
> + .resource = ezx_pcap_resources,
> + .dev = {
> + .platform_data = &ezx_pcap_platform_data,
> + },
> +};
> +
> static struct platform_device *devices[] __initdata = {
> + &ezx_pcap_device,
> &ezx_backlight_device,
> };
>
> @@ -105,6 +154,11 @@
> GPIO46_STUART_RXD,
> GPIO47_STUART_TXD,
>
> + /* PCAP SSP */
> + GPIO29_SSP1_SCLK,
> + GPIO25_SSP1_TXD,
> + GPIO26_SSP1_RXD,
> +
> /* For A780 support (connected with Neptune GSM chip) */
> GPIO30_USB_P3_2, /* ICL_TXENB */
> GPIO31_USB_P3_6, /* ICL_VPOUT */
> @@ -118,11 +172,12 @@
> {
> pxa2xx_mfp_config(ARRAY_AND_SIZE(ezx_pin_config));
> pxa_set_i2c_info(NULL);
> - if (machine_is_ezx_a780() || machine_is_ezx_e680())
> + if (machine_is_ezx_a780() || machine_is_ezx_e680()) {
> set_pxa_fb_info(&ezx_fb_info_1);
> - else
> + ezx_pcap_platform_data.config |= CS_INVERTED;
> + } else {
> set_pxa_fb_info(&ezx_fb_info_2);
> -
> + }
> platform_add_devices(devices, ARRAY_SIZE(devices));
> }
>
> Index: linux-2.6-arm/include/asm-arm/arch-pxa/irqs.h
> ===================================================================
> --- linux-2.6-arm.orig/include/asm-arm/arch-pxa/irqs.h
> +++ linux-2.6-arm/include/asm-arm/arch-pxa/irqs.h
> @@ -186,6 +186,39 @@
> #endif
> #endif /* CONFIG_MACH_PCM027 */
>
> +#define EZX_IRQ(x) PXA_BOARD_IRQ(x)
> +
> +#ifdef CONFIG_PXA_EZX
> +#define EZX_IRQ_USB4V EZX_IRQ(0) /* EMU */
> +#define EZX_IRQ_USB1V EZX_IRQ(1) /* EMU */
> +#define EZX_IRQ_HEADJACK EZX_IRQ(2) /* Audio connector */
> +#define EZX_IRQ_MIC EZX_IRQ(3) /* Audio connector */
> +#define EZX_IRQ_ADCDONE EZX_IRQ(4)
> +#define EZX_IRQ_TS EZX_IRQ(5) /* TS touch */
> +#define EZX_IRQ_ADCDONE2 EZX_IRQ(6) /* TS x/y ADC ready */
> +#define EZX_IRQ_WH EZX_IRQ(7)
> +#define EZX_IRQ_WL EZX_IRQ(8)
> +#define EZX_IRQ_ONOFF EZX_IRQ(9)
> +#define EZX_IRQ_ONOFF2 EZX_IRQ(10)
> +#define EZX_IRQ_MOBPORT EZX_IRQ(11)
> +#define EZX_IRQ_TODA EZX_IRQ(12)
> +#define EZX_IRQ_1HZ EZX_IRQ(13)
> +#define EZX_IRQ_MNEXB EZX_IRQ(14)
> +#define EZX_IRQ_ST EZX_IRQ(15)
> +#define EZX_IRQ_PC EZX_IRQ(16)
> +#define EZX_IRQ_SYSRST EZX_IRQ(17)
> +#define EZX_IRQ_SOFTRESET EZX_IRQ(18)
> +#define EZX_IRQ_EOL EZX_IRQ(19)
> +#define EZX_IRQ_CLK EZX_IRQ(20)
> +#define EZX_IRQ_WARM EZX_IRQ(21)
> +#define EZX_LAST_IRQ EZX_IRQ_WARM
> +
> +#if PXA_BOARD_IRQ_END < EZX_LAST_IRQ
> +#undef PXA_BOARD_IRQ_END
> +#define PXA_BOARD_IRQ_END EZX_LAST_IRQ
> +#endif
> +#endif /* CONFIG_EZX */
> +
> /*
> * Extended IRQs for companion chips start from the last board-specific IRQ.
> * NOTE: unlike board specific IRQs, the number space for these IRQs cannot
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/