From 33f5cc605d28bd82be8a728090063203947158f3 Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Tue, 16 Oct 2012 12:55:38 +0530 Subject: [PATCH 001/119] input: TSC: ti_tscadc: Correct register usage This patch cleans up the wrong register definitions and usage for touchscreen controller. Bit masks were not defined earlier. For ex: consider a register define as: write(xyz, ABC) ABC is expected to be written to register bits 0-11. Here we see that value written is as expected, but this write is also affecting all the remaining bits of xyz. The remaining bits hold the default value as 0 and the write also sets it to zero. Hence wrong usage were not causing user visible bugs. Due to these reasons it was difficult to follow when compared with TRM. Unused definitions are also removed. Since there is change in some naming convention because of all the above mentioned reasons, the same has been updated throughout the code. Bit declarations are grouped to increase readability. Signed-off-by: Patil, Rachna Signed-off-by: Samuel Ortiz --- drivers/input/touchscreen/ti_tscadc.c | 128 +++++++++++++++++--------- 1 file changed, 82 insertions(+), 46 deletions(-) diff --git a/drivers/input/touchscreen/ti_tscadc.c b/drivers/input/touchscreen/ti_tscadc.c index d229c741d544..d198cab61a69 100644 --- a/drivers/input/touchscreen/ti_tscadc.c +++ b/drivers/input/touchscreen/ti_tscadc.c @@ -52,43 +52,79 @@ /* Register Bitfields */ #define IRQWKUP_ENB BIT(0) -#define STPENB_STEPENB 0x7FFF + +/* Step Enable */ +#define STEPENB_MASK (0x1FFFF << 0) +#define STEPENB(val) (val << 0) +#define STPENB_STEPENB STEPENB(0x7FFF) + +/* IRQ enable */ #define IRQENB_FIFO1THRES BIT(5) #define IRQENB_PENUP BIT(9) -#define STEPCONFIG_MODE_HWSYNC 0x2 -#define STEPCONFIG_SAMPLES_AVG (1 << 4) -#define STEPCONFIG_XPP (1 << 5) -#define STEPCONFIG_XNN (1 << 6) -#define STEPCONFIG_YPP (1 << 7) -#define STEPCONFIG_YNN (1 << 8) -#define STEPCONFIG_XNP (1 << 9) -#define STEPCONFIG_YPN (1 << 10) -#define STEPCONFIG_INM (1 << 18) -#define STEPCONFIG_INP (1 << 20) -#define STEPCONFIG_INP_5 (1 << 21) -#define STEPCONFIG_FIFO1 (1 << 26) -#define STEPCONFIG_OPENDLY 0xff -#define STEPCONFIG_Z1 (3 << 19) -#define STEPIDLE_INP (1 << 22) -#define STEPCHARGE_RFP (1 << 12) -#define STEPCHARGE_INM (1 << 15) -#define STEPCHARGE_INP (1 << 19) -#define STEPCHARGE_RFM (1 << 23) -#define STEPCHARGE_DELAY 0x1 -#define CNTRLREG_TSCSSENB (1 << 0) -#define CNTRLREG_STEPID (1 << 1) -#define CNTRLREG_STEPCONFIGWRT (1 << 2) -#define CNTRLREG_4WIRE (1 << 5) -#define CNTRLREG_5WIRE (1 << 6) -#define CNTRLREG_8WIRE (3 << 5) -#define CNTRLREG_TSCENB (1 << 7) -#define ADCFSM_STEPID 0x10 +/* Step Configuration */ +#define STEPCONFIG_MODE_MASK (3 << 0) +#define STEPCONFIG_MODE(val) (val << 0) +#define STEPCONFIG_MODE_HWSYNC STEPCONFIG_MODE(2) +#define STEPCONFIG_AVG_MASK (7 << 2) +#define STEPCONFIG_AVG(val) (val << 2) +#define STEPCONFIG_AVG_16 STEPCONFIG_AVG(4) +#define STEPCONFIG_XPP BIT(5) +#define STEPCONFIG_XNN BIT(6) +#define STEPCONFIG_YPP BIT(7) +#define STEPCONFIG_YNN BIT(8) +#define STEPCONFIG_XNP BIT(9) +#define STEPCONFIG_YPN BIT(10) +#define STEPCONFIG_INM_MASK (0xF << 15) +#define STEPCONFIG_INM(val) (val << 15) +#define STEPCONFIG_INM_ADCREFM STEPCONFIG_INM(8) +#define STEPCONFIG_INP_MASK (0xF << 19) +#define STEPCONFIG_INP(val) (val << 19) +#define STEPCONFIG_INP_AN2 STEPCONFIG_INP(2) +#define STEPCONFIG_INP_AN3 STEPCONFIG_INP(3) +#define STEPCONFIG_INP_AN4 STEPCONFIG_INP(4) +#define STEPCONFIG_INP_ADCREFM STEPCONFIG_INP(8) +#define STEPCONFIG_FIFO1 BIT(26) + +/* Delay register */ +#define STEPDELAY_OPEN_MASK (0x3FFFF << 0) +#define STEPDELAY_OPEN(val) (val << 0) +#define STEPCONFIG_OPENDLY STEPDELAY_OPEN(0x098) + +/* Charge Config */ +#define STEPCHARGE_RFP_MASK (7 << 12) +#define STEPCHARGE_RFP(val) (val << 12) +#define STEPCHARGE_RFP_XPUL STEPCHARGE_RFP(1) +#define STEPCHARGE_INM_MASK (0xF << 15) +#define STEPCHARGE_INM(val) (val << 15) +#define STEPCHARGE_INM_AN1 STEPCHARGE_INM(1) +#define STEPCHARGE_INP_MASK (0xF << 19) +#define STEPCHARGE_INP(val) (val << 19) +#define STEPCHARGE_INP_AN1 STEPCHARGE_INP(1) +#define STEPCHARGE_RFM_MASK (3 << 23) +#define STEPCHARGE_RFM(val) (val << 23) +#define STEPCHARGE_RFM_XNUR STEPCHARGE_RFM(1) + +/* Charge delay */ +#define CHARGEDLY_OPEN_MASK (0x3FFFF << 0) +#define CHARGEDLY_OPEN(val) (val << 0) +#define CHARGEDLY_OPENDLY CHARGEDLY_OPEN(1) + +/* Control register */ +#define CNTRLREG_TSCSSENB BIT(0) +#define CNTRLREG_STEPID BIT(1) +#define CNTRLREG_STEPCONFIGWRT BIT(2) +#define CNTRLREG_AFE_CTRL_MASK (3 << 5) +#define CNTRLREG_AFE_CTRL(val) (val << 5) +#define CNTRLREG_4WIRE CNTRLREG_AFE_CTRL(1) +#define CNTRLREG_5WIRE CNTRLREG_AFE_CTRL(2) +#define CNTRLREG_8WIRE CNTRLREG_AFE_CTRL(3) +#define CNTRLREG_TSCENB BIT(7) + +#define ADCFSM_STEPID 0x10 #define SEQ_SETTLE 275 #define ADC_CLK 3000000 #define MAX_12BIT ((1 << 12) - 1) -#define TSCADC_DELTA_X 15 -#define TSCADC_DELTA_Y 15 struct tscadc { struct input_dev *input; @@ -119,18 +155,18 @@ static void tscadc_step_config(struct tscadc *ts_dev) /* Configure the Step registers */ config = STEPCONFIG_MODE_HWSYNC | - STEPCONFIG_SAMPLES_AVG | STEPCONFIG_XPP; + STEPCONFIG_AVG_16 | STEPCONFIG_XPP; switch (ts_dev->wires) { case 4: - config |= STEPCONFIG_INP | STEPCONFIG_XNN; + config |= STEPCONFIG_INP_AN2 | STEPCONFIG_XNN; break; case 5: config |= STEPCONFIG_YNN | - STEPCONFIG_INP_5 | STEPCONFIG_XNN | + STEPCONFIG_INP_AN4 | STEPCONFIG_XNN | STEPCONFIG_YPP; break; case 8: - config |= STEPCONFIG_INP | STEPCONFIG_XNN; + config |= STEPCONFIG_INP_AN2 | STEPCONFIG_XNN; break; } @@ -141,14 +177,14 @@ static void tscadc_step_config(struct tscadc *ts_dev) config = 0; config = STEPCONFIG_MODE_HWSYNC | - STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YNN | - STEPCONFIG_INM | STEPCONFIG_FIFO1; + STEPCONFIG_AVG_16 | STEPCONFIG_YNN | + STEPCONFIG_INM_ADCREFM | STEPCONFIG_FIFO1; switch (ts_dev->wires) { case 4: config |= STEPCONFIG_YPP; break; case 5: - config |= STEPCONFIG_XPP | STEPCONFIG_INP_5 | + config |= STEPCONFIG_XPP | STEPCONFIG_INP_AN4 | STEPCONFIG_XNP | STEPCONFIG_YPN; break; case 8: @@ -164,21 +200,21 @@ static void tscadc_step_config(struct tscadc *ts_dev) config = 0; /* Charge step configuration */ config = STEPCONFIG_XPP | STEPCONFIG_YNN | - STEPCHARGE_RFP | STEPCHARGE_RFM | - STEPCHARGE_INM | STEPCHARGE_INP; + STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR | + STEPCHARGE_INM_AN1 | STEPCHARGE_INP_AN1; tscadc_writel(ts_dev, REG_CHARGECONFIG, config); - tscadc_writel(ts_dev, REG_CHARGEDELAY, STEPCHARGE_DELAY); + tscadc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY); config = 0; /* Configure to calculate pressure */ config = STEPCONFIG_MODE_HWSYNC | - STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YPP | - STEPCONFIG_XNN | STEPCONFIG_INM; + STEPCONFIG_AVG_16 | STEPCONFIG_YPP | + STEPCONFIG_XNN | STEPCONFIG_INM_ADCREFM; tscadc_writel(ts_dev, REG_STEPCONFIG13, config); tscadc_writel(ts_dev, REG_STEPDELAY13, STEPCONFIG_OPENDLY); - config |= STEPCONFIG_Z1 | STEPCONFIG_FIFO1; + config |= STEPCONFIG_INP_AN3 | STEPCONFIG_FIFO1; tscadc_writel(ts_dev, REG_STEPCONFIG14, config); tscadc_writel(ts_dev, REG_STEPDELAY14, STEPCONFIG_OPENDLY); @@ -190,8 +226,8 @@ static void tscadc_idle_config(struct tscadc *ts_config) unsigned int idleconfig; idleconfig = STEPCONFIG_YNN | - STEPCONFIG_INM | - STEPCONFIG_YPN | STEPIDLE_INP; + STEPCONFIG_INM_ADCREFM | + STEPCONFIG_YPN | STEPCONFIG_INP_ADCREFM; tscadc_writel(ts_config, REG_IDLECONFIG, idleconfig); } From d1fb57435c108b8dd66d7f47b4c60c1798dcae4c Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Tue, 16 Oct 2012 12:55:39 +0530 Subject: [PATCH 002/119] input: TSC: ti_tscadc: Add Step configuration as platform data There are 16 programmable Step Configuration registers which are used by the sequencer. Program the Steps in order to configure a channel input to be sampled. If the same step is applied several times, the coordinate values read are more accurate. Hence we provide the user an option of how many steps should be configured. For ex: If this value is assigned as 4, This means that 4 steps are applied to read x co-ordinate and 4 steps to read y co-ordinate. Furtheron the interrupt handler already holds code to use delta filter and report the best value out of these values to the input sub-system. Signed-off-by: Patil, Rachna Signed-off-by: Samuel Ortiz --- drivers/input/touchscreen/ti_tscadc.c | 25 +++++++++++++------------ include/linux/input/ti_tscadc.h | 6 ++++++ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/input/touchscreen/ti_tscadc.c b/drivers/input/touchscreen/ti_tscadc.c index d198cab61a69..c1bd8e5c132c 100644 --- a/drivers/input/touchscreen/ti_tscadc.c +++ b/drivers/input/touchscreen/ti_tscadc.c @@ -41,10 +41,6 @@ #define REG_CHARGEDELAY 0x060 #define REG_STEPCONFIG(n) (0x64 + ((n - 1) * 8)) #define REG_STEPDELAY(n) (0x68 + ((n - 1) * 8)) -#define REG_STEPCONFIG13 0x0C4 -#define REG_STEPDELAY13 0x0C8 -#define REG_STEPCONFIG14 0x0CC -#define REG_STEPDELAY14 0x0D0 #define REG_FIFO0CNT 0xE4 #define REG_FIFO1THR 0xF4 #define REG_FIFO0 0x100 @@ -134,6 +130,7 @@ struct tscadc { unsigned int wires; unsigned int x_plate_resistance; bool pen_down; + int steps_to_configure; }; static unsigned int tscadc_readl(struct tscadc *ts, unsigned int reg) @@ -150,9 +147,10 @@ static void tscadc_writel(struct tscadc *tsc, unsigned int reg, static void tscadc_step_config(struct tscadc *ts_dev) { unsigned int config; - int i; + int i, total_steps; /* Configure the Step registers */ + total_steps = 2 * ts_dev->steps_to_configure; config = STEPCONFIG_MODE_HWSYNC | STEPCONFIG_AVG_16 | STEPCONFIG_XPP; @@ -170,7 +168,7 @@ static void tscadc_step_config(struct tscadc *ts_dev) break; } - for (i = 1; i < 7; i++) { + for (i = 1; i <= ts_dev->steps_to_configure; i++) { tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); } @@ -192,7 +190,7 @@ static void tscadc_step_config(struct tscadc *ts_dev) break; } - for (i = 7; i < 13; i++) { + for (i = (ts_dev->steps_to_configure + 1); i <= total_steps; i++) { tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); } @@ -211,12 +209,14 @@ static void tscadc_step_config(struct tscadc *ts_dev) config = STEPCONFIG_MODE_HWSYNC | STEPCONFIG_AVG_16 | STEPCONFIG_YPP | STEPCONFIG_XNN | STEPCONFIG_INM_ADCREFM; - tscadc_writel(ts_dev, REG_STEPCONFIG13, config); - tscadc_writel(ts_dev, REG_STEPDELAY13, STEPCONFIG_OPENDLY); + tscadc_writel(ts_dev, REG_STEPCONFIG(total_steps + 1), config); + tscadc_writel(ts_dev, REG_STEPDELAY(total_steps + 1), + STEPCONFIG_OPENDLY); config |= STEPCONFIG_INP_AN3 | STEPCONFIG_FIFO1; - tscadc_writel(ts_dev, REG_STEPCONFIG14, config); - tscadc_writel(ts_dev, REG_STEPDELAY14, STEPCONFIG_OPENDLY); + tscadc_writel(ts_dev, REG_STEPCONFIG(total_steps + 2), config); + tscadc_writel(ts_dev, REG_STEPDELAY(total_steps + 2), + STEPCONFIG_OPENDLY); tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); } @@ -379,6 +379,7 @@ static int __devinit tscadc_probe(struct platform_device *pdev) ts_dev->irq = irq; ts_dev->wires = pdata->wires; ts_dev->x_plate_resistance = pdata->x_plate_resistance; + ts_dev->steps_to_configure = pdata->steps_to_configure; res = request_mem_region(res->start, resource_size(res), pdev->name); if (!res) { @@ -447,7 +448,7 @@ static int __devinit tscadc_probe(struct platform_device *pdev) tscadc_idle_config(ts_dev); tscadc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO1THRES); tscadc_step_config(ts_dev); - tscadc_writel(ts_dev, REG_FIFO1THR, 6); + tscadc_writel(ts_dev, REG_FIFO1THR, ts_dev->steps_to_configure); ctrl |= CNTRLREG_TSCSSENB; tscadc_writel(ts_dev, REG_CTRL, ctrl); diff --git a/include/linux/input/ti_tscadc.h b/include/linux/input/ti_tscadc.h index b10a527a92a4..ad442a3eb68b 100644 --- a/include/linux/input/ti_tscadc.h +++ b/include/linux/input/ti_tscadc.h @@ -7,11 +7,17 @@ * i.e. 4/5/8 wire touchscreen support * on the platform. * @x_plate_resistance: X plate resistance. + * @steps_to_configure: The sequencer supports a total of + * 16 programmable steps. + * A step configured to read a single + * co-ordinate value, can be applied + * more number of times for better results. */ struct tsc_data { int wires; int x_plate_resistance; + int steps_to_configure; }; #endif From 30af55f930c2f69d8f3dcd658fb3ee352ebc54c1 Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Tue, 16 Oct 2012 12:55:40 +0530 Subject: [PATCH 003/119] input: TSC: ti_tscadc: set FIFO0 threshold Interrupt Code currently uses FIFO1 threshold interrupt. since this is a MFD, Dedicating FIFO0 to touchscreen and making way for other devices to use FIFO1 as well. The FIFO can be shared between 2 devices but since the interrupt used is threshold interrupt on FIFO1, we would end up having wrong interrupts. Hence changing the same. Signed-off-by: Patil, Rachna Signed-off-by: Samuel Ortiz --- drivers/input/touchscreen/ti_tscadc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/ti_tscadc.c b/drivers/input/touchscreen/ti_tscadc.c index c1bd8e5c132c..2cc19b09e31c 100644 --- a/drivers/input/touchscreen/ti_tscadc.c +++ b/drivers/input/touchscreen/ti_tscadc.c @@ -42,6 +42,7 @@ #define REG_STEPCONFIG(n) (0x64 + ((n - 1) * 8)) #define REG_STEPDELAY(n) (0x68 + ((n - 1) * 8)) #define REG_FIFO0CNT 0xE4 +#define REG_FIFO0THR 0xE8 #define REG_FIFO1THR 0xF4 #define REG_FIFO0 0x100 #define REG_FIFO1 0x200 @@ -55,6 +56,7 @@ #define STPENB_STEPENB STEPENB(0x7FFF) /* IRQ enable */ +#define IRQENB_FIFO0THRES BIT(2) #define IRQENB_FIFO1THRES BIT(5) #define IRQENB_PENUP BIT(9) @@ -277,7 +279,7 @@ static irqreturn_t tscadc_irq(int irq, void *dev) unsigned int fsm; status = tscadc_readl(ts_dev, REG_IRQSTATUS); - if (status & IRQENB_FIFO1THRES) { + if (status & IRQENB_FIFO0THRES) { tscadc_read_coordinates(ts_dev, &x, &y); z1 = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; @@ -303,7 +305,7 @@ static irqreturn_t tscadc_irq(int irq, void *dev) input_sync(input_dev); } } - irqclr |= IRQENB_FIFO1THRES; + irqclr |= IRQENB_FIFO0THRES; } /* @@ -446,9 +448,9 @@ static int __devinit tscadc_probe(struct platform_device *pdev) tscadc_writel(ts_dev, REG_CTRL, ctrl); tscadc_idle_config(ts_dev); - tscadc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO1THRES); + tscadc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES); tscadc_step_config(ts_dev); - tscadc_writel(ts_dev, REG_FIFO1THR, ts_dev->steps_to_configure); + tscadc_writel(ts_dev, REG_FIFO0THR, ts_dev->steps_to_configure); ctrl |= CNTRLREG_TSCSSENB; tscadc_writel(ts_dev, REG_CTRL, ctrl); From c6e86db2bec56e05c7ad1524ed2273a4cc8e8868 Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Tue, 16 Oct 2012 12:55:41 +0530 Subject: [PATCH 004/119] input: TSC: ti_tscadc: Remove definition of End Of Interrupt register The touchscreen IP uses level sensitive interrupts rather than edge sensitive interrupts and therefore the is no need to use the EOI register to have the module re-strobe the interrupt line if there pending interrupts exist. Signed-off-by: Patil, Rachna Signed-off-by: Samuel Ortiz --- drivers/input/touchscreen/ti_tscadc.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/input/touchscreen/ti_tscadc.c b/drivers/input/touchscreen/ti_tscadc.c index 2cc19b09e31c..ec0a442478a8 100644 --- a/drivers/input/touchscreen/ti_tscadc.c +++ b/drivers/input/touchscreen/ti_tscadc.c @@ -27,7 +27,6 @@ #include #include -#define REG_IRQEOI 0x020 #define REG_RAWIRQSTATUS 0x024 #define REG_IRQSTATUS 0x028 #define REG_IRQENABLE 0x02C @@ -330,8 +329,6 @@ static irqreturn_t tscadc_irq(int irq, void *dev) } tscadc_writel(ts_dev, REG_IRQSTATUS, irqclr); - /* check pending interrupts */ - tscadc_writel(ts_dev, REG_IRQEOI, 0x0); tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); return IRQ_HANDLED; From 55c04de5176ea3eac6fdc469a6a063c5cb91ed7c Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Tue, 16 Oct 2012 12:55:42 +0530 Subject: [PATCH 005/119] input: TSC: ti_tscadc: Rename the existing touchscreen driver Make way for addition of MFD driver. The existing touchsreen driver is a MFD client. For better readability we rename the file to indicate its functionality as only touchscreen. Signed-off-by: Patil, Rachna Signed-off-by: Samuel Ortiz --- drivers/input/touchscreen/Kconfig | 4 +- drivers/input/touchscreen/Makefile | 2 +- .../{ti_tscadc.c => ti_am335x_tsc.c} | 96 +++++++++---------- .../input/{ti_tscadc.h => ti_am335x_tsc.h} | 4 +- 4 files changed, 53 insertions(+), 53 deletions(-) rename drivers/input/touchscreen/{ti_tscadc.c => ti_am335x_tsc.c} (83%) rename include/linux/input/{ti_tscadc.h => ti_am335x_tsc.h} (88%) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index f7668b24c378..d31dc5faeaaf 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -529,7 +529,7 @@ config TOUCHSCREEN_TOUCHWIN To compile this driver as a module, choose M here: the module will be called touchwin. -config TOUCHSCREEN_TI_TSCADC +config TOUCHSCREEN_TI_AM335X_TSC tristate "TI Touchscreen Interface" depends on ARCH_OMAP2PLUS help @@ -539,7 +539,7 @@ config TOUCHSCREEN_TI_TSCADC If unsure, say N. To compile this driver as a module, choose M here: the - module will be called ti_tscadc. + module will be called ti_am335x_tsc. config TOUCHSCREEN_ATMEL_TSADCC tristate "Atmel Touchscreen Interface" diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 178eb128d90f..7c4c78ebe49e 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -52,7 +52,7 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o -obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC) += ti_tscadc.o +obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o diff --git a/drivers/input/touchscreen/ti_tscadc.c b/drivers/input/touchscreen/ti_am335x_tsc.c similarity index 83% rename from drivers/input/touchscreen/ti_tscadc.c rename to drivers/input/touchscreen/ti_am335x_tsc.c index ec0a442478a8..462950acb97b 100644 --- a/drivers/input/touchscreen/ti_tscadc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #define REG_RAWIRQSTATUS 0x024 @@ -123,7 +123,7 @@ #define ADC_CLK 3000000 #define MAX_12BIT ((1 << 12) - 1) -struct tscadc { +struct titsc { struct input_dev *input; struct clk *tsc_ick; void __iomem *tsc_base; @@ -134,18 +134,18 @@ struct tscadc { int steps_to_configure; }; -static unsigned int tscadc_readl(struct tscadc *ts, unsigned int reg) +static unsigned int titsc_readl(struct titsc *ts, unsigned int reg) { return readl(ts->tsc_base + reg); } -static void tscadc_writel(struct tscadc *tsc, unsigned int reg, +static void titsc_writel(struct titsc *tsc, unsigned int reg, unsigned int val) { writel(val, tsc->tsc_base + reg); } -static void tscadc_step_config(struct tscadc *ts_dev) +static void titsc_step_config(struct titsc *ts_dev) { unsigned int config; int i, total_steps; @@ -170,8 +170,8 @@ static void tscadc_step_config(struct tscadc *ts_dev) } for (i = 1; i <= ts_dev->steps_to_configure; i++) { - tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); - tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); + titsc_writel(ts_dev, REG_STEPCONFIG(i), config); + titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); } config = 0; @@ -192,8 +192,8 @@ static void tscadc_step_config(struct tscadc *ts_dev) } for (i = (ts_dev->steps_to_configure + 1); i <= total_steps; i++) { - tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); - tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); + titsc_writel(ts_dev, REG_STEPCONFIG(i), config); + titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); } config = 0; @@ -202,40 +202,40 @@ static void tscadc_step_config(struct tscadc *ts_dev) STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR | STEPCHARGE_INM_AN1 | STEPCHARGE_INP_AN1; - tscadc_writel(ts_dev, REG_CHARGECONFIG, config); - tscadc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY); + titsc_writel(ts_dev, REG_CHARGECONFIG, config); + titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY); config = 0; /* Configure to calculate pressure */ config = STEPCONFIG_MODE_HWSYNC | STEPCONFIG_AVG_16 | STEPCONFIG_YPP | STEPCONFIG_XNN | STEPCONFIG_INM_ADCREFM; - tscadc_writel(ts_dev, REG_STEPCONFIG(total_steps + 1), config); - tscadc_writel(ts_dev, REG_STEPDELAY(total_steps + 1), + titsc_writel(ts_dev, REG_STEPCONFIG(total_steps + 1), config); + titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 1), STEPCONFIG_OPENDLY); config |= STEPCONFIG_INP_AN3 | STEPCONFIG_FIFO1; - tscadc_writel(ts_dev, REG_STEPCONFIG(total_steps + 2), config); - tscadc_writel(ts_dev, REG_STEPDELAY(total_steps + 2), + titsc_writel(ts_dev, REG_STEPCONFIG(total_steps + 2), config); + titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 2), STEPCONFIG_OPENDLY); - tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); + titsc_writel(ts_dev, REG_SE, STPENB_STEPENB); } -static void tscadc_idle_config(struct tscadc *ts_config) +static void titsc_idle_config(struct titsc *ts_config) { unsigned int idleconfig; idleconfig = STEPCONFIG_YNN | STEPCONFIG_INM_ADCREFM | STEPCONFIG_YPN | STEPCONFIG_INP_ADCREFM; - tscadc_writel(ts_config, REG_IDLECONFIG, idleconfig); + titsc_writel(ts_config, REG_IDLECONFIG, idleconfig); } -static void tscadc_read_coordinates(struct tscadc *ts_dev, +static void titsc_read_coordinates(struct titsc *ts_dev, unsigned int *x, unsigned int *y) { - unsigned int fifocount = tscadc_readl(ts_dev, REG_FIFO0CNT); + unsigned int fifocount = titsc_readl(ts_dev, REG_FIFO0CNT); unsigned int prev_val_x = ~0, prev_val_y = ~0; unsigned int prev_diff_x = ~0, prev_diff_y = ~0; unsigned int read, diff; @@ -250,7 +250,7 @@ static void tscadc_read_coordinates(struct tscadc *ts_dev, * if true the value is reported to the sub system. */ for (i = 0; i < fifocount - 1; i++) { - read = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; + read = titsc_readl(ts_dev, REG_FIFO0) & 0xfff; diff = abs(read - prev_val_x); if (diff < prev_diff_x) { prev_diff_x = diff; @@ -258,7 +258,7 @@ static void tscadc_read_coordinates(struct tscadc *ts_dev, } prev_val_x = read; - read = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff; + read = titsc_readl(ts_dev, REG_FIFO1) & 0xfff; diff = abs(read - prev_val_y); if (diff < prev_diff_y) { prev_diff_y = diff; @@ -268,21 +268,21 @@ static void tscadc_read_coordinates(struct tscadc *ts_dev, } } -static irqreturn_t tscadc_irq(int irq, void *dev) +static irqreturn_t titsc_irq(int irq, void *dev) { - struct tscadc *ts_dev = dev; + struct titsc *ts_dev = dev; struct input_dev *input_dev = ts_dev->input; unsigned int status, irqclr = 0; unsigned int x = 0, y = 0; unsigned int z1, z2, z; unsigned int fsm; - status = tscadc_readl(ts_dev, REG_IRQSTATUS); + status = titsc_readl(ts_dev, REG_IRQSTATUS); if (status & IRQENB_FIFO0THRES) { - tscadc_read_coordinates(ts_dev, &x, &y); + titsc_read_coordinates(ts_dev, &x, &y); - z1 = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; - z2 = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff; + z1 = titsc_readl(ts_dev, REG_FIFO0) & 0xfff; + z2 = titsc_readl(ts_dev, REG_FIFO1) & 0xfff; if (ts_dev->pen_down && z1 != 0 && z2 != 0) { /* @@ -313,10 +313,10 @@ static irqreturn_t tscadc_irq(int irq, void *dev) */ udelay(SEQ_SETTLE); - status = tscadc_readl(ts_dev, REG_RAWIRQSTATUS); + status = titsc_readl(ts_dev, REG_RAWIRQSTATUS); if (status & IRQENB_PENUP) { /* Pen up event */ - fsm = tscadc_readl(ts_dev, REG_ADCFSM); + fsm = titsc_readl(ts_dev, REG_ADCFSM); if (fsm == ADCFSM_STEPID) { ts_dev->pen_down = false; input_report_key(input_dev, BTN_TOUCH, 0); @@ -328,9 +328,9 @@ static irqreturn_t tscadc_irq(int irq, void *dev) irqclr |= IRQENB_PENUP; } - tscadc_writel(ts_dev, REG_IRQSTATUS, irqclr); + titsc_writel(ts_dev, REG_IRQSTATUS, irqclr); - tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); + titsc_writel(ts_dev, REG_SE, STPENB_STEPENB); return IRQ_HANDLED; } @@ -338,11 +338,11 @@ static irqreturn_t tscadc_irq(int irq, void *dev) * The functions for inserting/removing driver as a module. */ -static int __devinit tscadc_probe(struct platform_device *pdev) +static int __devinit titsc_probe(struct platform_device *pdev) { const struct tsc_data *pdata = pdev->dev.platform_data; struct resource *res; - struct tscadc *ts_dev; + struct titsc *ts_dev; struct input_dev *input_dev; struct clk *clk; int err; @@ -366,7 +366,7 @@ static int __devinit tscadc_probe(struct platform_device *pdev) } /* Allocate memory for device */ - ts_dev = kzalloc(sizeof(struct tscadc), GFP_KERNEL); + ts_dev = kzalloc(sizeof(struct titsc), GFP_KERNEL); input_dev = input_allocate_device(); if (!ts_dev || !input_dev) { dev_err(&pdev->dev, "failed to allocate memory.\n"); @@ -394,7 +394,7 @@ static int __devinit tscadc_probe(struct platform_device *pdev) goto err_release_mem_region; } - err = request_irq(ts_dev->irq, tscadc_irq, + err = request_irq(ts_dev->irq, titsc_irq, 0, pdev->dev.driver->name, ts_dev); if (err) { dev_err(&pdev->dev, "failed to allocate irq.\n"); @@ -423,10 +423,10 @@ static int __devinit tscadc_probe(struct platform_device *pdev) goto err_disable_clk; } /* CLKDIV needs to be configured to the value minus 1 */ - tscadc_writel(ts_dev, REG_CLKDIV, clk_value - 1); + titsc_writel(ts_dev, REG_CLKDIV, clk_value - 1); /* Enable wake-up of the SoC using touchscreen */ - tscadc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); + titsc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_TSCENB | @@ -442,15 +442,15 @@ static int __devinit tscadc_probe(struct platform_device *pdev) ctrl |= CNTRLREG_8WIRE; break; } - tscadc_writel(ts_dev, REG_CTRL, ctrl); + titsc_writel(ts_dev, REG_CTRL, ctrl); - tscadc_idle_config(ts_dev); - tscadc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES); - tscadc_step_config(ts_dev); - tscadc_writel(ts_dev, REG_FIFO0THR, ts_dev->steps_to_configure); + titsc_idle_config(ts_dev); + titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES); + titsc_step_config(ts_dev); + titsc_writel(ts_dev, REG_FIFO0THR, ts_dev->steps_to_configure); ctrl |= CNTRLREG_TSCSSENB; - tscadc_writel(ts_dev, REG_CTRL, ctrl); + titsc_writel(ts_dev, REG_CTRL, ctrl); input_dev->name = "ti-tsc-adc"; input_dev->dev.parent = &pdev->dev; @@ -485,9 +485,9 @@ err_free_mem: return err; } -static int __devexit tscadc_remove(struct platform_device *pdev) +static int __devexit titsc_remove(struct platform_device *pdev) { - struct tscadc *ts_dev = platform_get_drvdata(pdev); + struct titsc *ts_dev = platform_get_drvdata(pdev); struct resource *res; free_irq(ts_dev->irq, ts_dev); @@ -508,8 +508,8 @@ static int __devexit tscadc_remove(struct platform_device *pdev) } static struct platform_driver ti_tsc_driver = { - .probe = tscadc_probe, - .remove = __devexit_p(tscadc_remove), + .probe = titsc_probe, + .remove = __devexit_p(titsc_remove), .driver = { .name = "tsc", .owner = THIS_MODULE, diff --git a/include/linux/input/ti_tscadc.h b/include/linux/input/ti_am335x_tsc.h similarity index 88% rename from include/linux/input/ti_tscadc.h rename to include/linux/input/ti_am335x_tsc.h index ad442a3eb68b..49269a2aa329 100644 --- a/include/linux/input/ti_tscadc.h +++ b/include/linux/input/ti_am335x_tsc.h @@ -1,5 +1,5 @@ -#ifndef __LINUX_TI_TSCADC_H -#define __LINUX_TI_TSCADC_H +#ifndef __LINUX_TI_AM335X_TSC_H +#define __LINUX_TI_AM335X_TSC_H /** * struct tsc_data Touchscreen wire configuration From 01636eb970a029897b06fb96026941429212ddd9 Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Tue, 16 Oct 2012 12:55:43 +0530 Subject: [PATCH 006/119] mfd: ti_tscadc: Add support for TI's TSC/ADC MFDevice Add the mfd core driver which supports touchscreen and ADC. With this patch we are only adding infrastructure to support the MFD clients. Signed-off-by: Patil, Rachna Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 11 ++ drivers/mfd/Makefile | 1 + drivers/mfd/ti_am335x_tscadc.c | 250 +++++++++++++++++++++++++++ include/linux/mfd/ti_am335x_tscadc.h | 137 +++++++++++++++ 4 files changed, 399 insertions(+) create mode 100644 drivers/mfd/ti_am335x_tscadc.c create mode 100644 include/linux/mfd/ti_am335x_tscadc.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index acab3ef8a310..9bba7f78ff36 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -94,6 +94,17 @@ config MFD_TI_SSP To compile this driver as a module, choose M here: the module will be called ti-ssp. +config MFD_TI_AM335X_TSCADC + tristate "TI ADC / Touch Screen chip support" + select MFD_CORE + select REGMAP + select REGMAP_MMIO + help + If you say yes here you get support for Texas Instruments series + of Touch Screen /ADC chips. + To compile this driver as a module, choose M here: the + module will be called ti_am335x_tscadc. + config HTC_EGPIO bool "HTC EGPIO support" depends on GENERIC_HARDIRQS && GPIOLIB && ARM diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d8ccb630ddb0..442c17e40741 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o +obj-$(CONFIG_MFD_TI_AM335X_TSCADC) += ti_am335x_tscadc.o obj-$(CONFIG_MFD_STA2X11) += sta2x11-mfd.o obj-$(CONFIG_MFD_STMPE) += stmpe.o diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c new file mode 100644 index 000000000000..14df67bc390f --- /dev/null +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -0,0 +1,250 @@ +/* + * TI Touch Screen / ADC MFD driver + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static unsigned int tscadc_readl(struct ti_tscadc_dev *tsadc, unsigned int reg) +{ + unsigned int val; + + regmap_read(tsadc->regmap_tscadc, reg, &val); + return val; +} + +static void tscadc_writel(struct ti_tscadc_dev *tsadc, unsigned int reg, + unsigned int val) +{ + regmap_write(tsadc->regmap_tscadc, reg, val); +} + +static const struct regmap_config tscadc_regmap_config = { + .name = "ti_tscadc", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, +}; + +static void tscadc_idle_config(struct ti_tscadc_dev *config) +{ + unsigned int idleconfig; + + idleconfig = STEPCONFIG_YNN | STEPCONFIG_INM_ADCREFM | + STEPCONFIG_INP_ADCREFM | STEPCONFIG_YPN; + + tscadc_writel(config, REG_IDLECONFIG, idleconfig); +} + +static int __devinit ti_tscadc_probe(struct platform_device *pdev) +{ + struct ti_tscadc_dev *tscadc; + struct resource *res; + struct clk *clk; + struct mfd_tscadc_board *pdata = pdev->dev.platform_data; + int irq; + int err, ctrl; + int clk_value, clock_rate; + + if (!pdata) { + dev_err(&pdev->dev, "Could not find platform data\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no memory resource defined.\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq ID is specified.\n"); + return -EINVAL; + } + + /* Allocate memory for device */ + tscadc = devm_kzalloc(&pdev->dev, + sizeof(struct ti_tscadc_dev), GFP_KERNEL); + if (!tscadc) { + dev_err(&pdev->dev, "failed to allocate memory.\n"); + return -ENOMEM; + } + tscadc->dev = &pdev->dev; + tscadc->irq = irq; + + res = devm_request_mem_region(&pdev->dev, + res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to reserve registers.\n"); + err = -EBUSY; + goto err; + } + + tscadc->tscadc_base = devm_ioremap(&pdev->dev, + res->start, resource_size(res)); + if (!tscadc->tscadc_base) { + dev_err(&pdev->dev, "failed to map registers.\n"); + err = -ENOMEM; + goto err; + } + + tscadc->regmap_tscadc = devm_regmap_init_mmio(&pdev->dev, + tscadc->tscadc_base, &tscadc_regmap_config); + if (IS_ERR(tscadc->regmap_tscadc)) { + dev_err(&pdev->dev, "regmap init failed\n"); + err = PTR_ERR(tscadc->regmap_tscadc); + goto err; + } + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + /* + * The TSC_ADC_Subsystem has 2 clock domains + * OCP_CLK and ADC_CLK. + * The ADC clock is expected to run at target of 3MHz, + * and expected to capture 12-bit data at a rate of 200 KSPS. + * The TSC_ADC_SS controller design assumes the OCP clock is + * at least 6x faster than the ADC clock. + */ + clk = clk_get(&pdev->dev, "adc_tsc_fck"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get TSC fck\n"); + err = PTR_ERR(clk); + goto err_disable_clk; + } + clock_rate = clk_get_rate(clk); + clk_put(clk); + clk_value = clock_rate / ADC_CLK; + if (clk_value < MAX_CLK_DIV) { + dev_err(&pdev->dev, "clock input less than min clock requirement\n"); + err = -EINVAL; + goto err_disable_clk; + } + /* TSCADC_CLKDIV needs to be configured to the value minus 1 */ + clk_value = clk_value - 1; + tscadc_writel(tscadc, REG_CLKDIV, clk_value); + + /* Set the control register bits */ + ctrl = CNTRLREG_STEPCONFIGWRT | + CNTRLREG_TSCENB | + CNTRLREG_STEPID | + CNTRLREG_4WIRE; + tscadc_writel(tscadc, REG_CTRL, ctrl); + + /* Set register bits for Idle Config Mode */ + tscadc_idle_config(tscadc); + + /* Enable the TSC module enable bit */ + ctrl = tscadc_readl(tscadc, REG_CTRL); + ctrl |= CNTRLREG_TSCSSENB; + tscadc_writel(tscadc, REG_CTRL, ctrl); + + err = mfd_add_devices(&pdev->dev, pdev->id, tscadc->cells, + TSCADC_CELLS, NULL, 0, NULL); + if (err < 0) + goto err_disable_clk; + + device_init_wakeup(&pdev->dev, true); + platform_set_drvdata(pdev, tscadc); + + return 0; + +err_disable_clk: + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); +err: + return err; +} + +static int __devexit ti_tscadc_remove(struct platform_device *pdev) +{ + struct ti_tscadc_dev *tscadc = platform_get_drvdata(pdev); + + tscadc_writel(tscadc, REG_SE, 0x00); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + mfd_remove_devices(tscadc->dev); + + return 0; +} + +#ifdef CONFIG_PM +static int tscadc_suspend(struct device *dev) +{ + struct ti_tscadc_dev *tscadc_dev = dev_get_drvdata(dev); + + tscadc_writel(tscadc_dev, REG_SE, 0x00); + pm_runtime_put_sync(dev); + + return 0; +} + +static int tscadc_resume(struct device *dev) +{ + struct ti_tscadc_dev *tscadc_dev = dev_get_drvdata(dev); + unsigned int restore, ctrl; + + pm_runtime_get_sync(dev); + + /* context restore */ + ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_TSCENB | + CNTRLREG_STEPID | CNTRLREG_4WIRE; + tscadc_writel(tscadc_dev, REG_CTRL, ctrl); + tscadc_idle_config(tscadc_dev); + tscadc_writel(tscadc_dev, REG_SE, STPENB_STEPENB); + restore = tscadc_readl(tscadc_dev, REG_CTRL); + tscadc_writel(tscadc_dev, REG_CTRL, + (restore | CNTRLREG_TSCSSENB)); + + return 0; +} + +static const struct dev_pm_ops tscadc_pm_ops = { + .suspend = tscadc_suspend, + .resume = tscadc_resume, +}; +#define TSCADC_PM_OPS (&tscadc_pm_ops) +#else +#define TSCADC_PM_OPS NULL +#endif + +static struct platform_driver ti_tscadc_driver = { + .driver = { + .name = "ti_tscadc", + .owner = THIS_MODULE, + .pm = TSCADC_PM_OPS, + }, + .probe = ti_tscadc_probe, + .remove = __devexit_p(ti_tscadc_remove), + +}; + +module_platform_driver(ti_tscadc_driver); + +MODULE_DESCRIPTION("TI touchscreen / ADC MFD controller driver"); +MODULE_AUTHOR("Rachna Patil "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h new file mode 100644 index 000000000000..b7232b157061 --- /dev/null +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -0,0 +1,137 @@ +#ifndef __LINUX_TI_AM335X_TSCADC_MFD_H +#define __LINUX_TI_AM335X_TSCADC_MFD_H + +/* + * TI Touch Screen / ADC MFD driver + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +#define REG_RAWIRQSTATUS 0x024 +#define REG_IRQSTATUS 0x028 +#define REG_IRQENABLE 0x02C +#define REG_IRQCLR 0x030 +#define REG_IRQWAKEUP 0x034 +#define REG_CTRL 0x040 +#define REG_ADCFSM 0x044 +#define REG_CLKDIV 0x04C +#define REG_SE 0x054 +#define REG_IDLECONFIG 0x058 +#define REG_CHARGECONFIG 0x05C +#define REG_CHARGEDELAY 0x060 +#define REG_STEPCONFIG(n) (0x64 + ((n - 1) * 8)) +#define REG_STEPDELAY(n) (0x68 + ((n - 1) * 8)) +#define REG_FIFO0CNT 0xE4 +#define REG_FIFO0THR 0xE8 +#define REG_FIFO1CNT 0xF0 +#define REG_FIFO1THR 0xF4 +#define REG_FIFO0 0x100 +#define REG_FIFO1 0x200 + +/* Register Bitfields */ +/* IRQ wakeup enable */ +#define IRQWKUP_ENB BIT(0) + +/* Step Enable */ +#define STEPENB_MASK (0x1FFFF << 0) +#define STEPENB(val) ((val) << 0) +#define STPENB_STEPENB STEPENB(0x1FFFF) + +/* IRQ enable */ +#define IRQENB_HW_PEN BIT(0) +#define IRQENB_FIFO0THRES BIT(2) +#define IRQENB_FIFO1THRES BIT(5) +#define IRQENB_PENUP BIT(9) + +/* Step Configuration */ +#define STEPCONFIG_MODE_MASK (3 << 0) +#define STEPCONFIG_MODE(val) ((val) << 0) +#define STEPCONFIG_MODE_HWSYNC STEPCONFIG_MODE(2) +#define STEPCONFIG_AVG_MASK (7 << 2) +#define STEPCONFIG_AVG(val) ((val) << 2) +#define STEPCONFIG_AVG_16 STEPCONFIG_AVG(4) +#define STEPCONFIG_XPP BIT(5) +#define STEPCONFIG_XNN BIT(6) +#define STEPCONFIG_YPP BIT(7) +#define STEPCONFIG_YNN BIT(8) +#define STEPCONFIG_XNP BIT(9) +#define STEPCONFIG_YPN BIT(10) +#define STEPCONFIG_INM_MASK (0xF << 15) +#define STEPCONFIG_INM(val) ((val) << 15) +#define STEPCONFIG_INM_ADCREFM STEPCONFIG_INM(8) +#define STEPCONFIG_INP_MASK (0xF << 19) +#define STEPCONFIG_INP(val) ((val) << 19) +#define STEPCONFIG_INP_AN2 STEPCONFIG_INP(2) +#define STEPCONFIG_INP_AN3 STEPCONFIG_INP(3) +#define STEPCONFIG_INP_AN4 STEPCONFIG_INP(4) +#define STEPCONFIG_INP_ADCREFM STEPCONFIG_INP(8) +#define STEPCONFIG_FIFO1 BIT(26) + +/* Delay register */ +#define STEPDELAY_OPEN_MASK (0x3FFFF << 0) +#define STEPDELAY_OPEN(val) ((val) << 0) +#define STEPCONFIG_OPENDLY STEPDELAY_OPEN(0x098) +#define STEPDELAY_SAMPLE_MASK (0xFF << 24) +#define STEPDELAY_SAMPLE(val) ((val) << 24) +#define STEPCONFIG_SAMPLEDLY STEPDELAY_SAMPLE(0) + +/* Charge Config */ +#define STEPCHARGE_RFP_MASK (7 << 12) +#define STEPCHARGE_RFP(val) ((val) << 12) +#define STEPCHARGE_RFP_XPUL STEPCHARGE_RFP(1) +#define STEPCHARGE_INM_MASK (0xF << 15) +#define STEPCHARGE_INM(val) ((val) << 15) +#define STEPCHARGE_INM_AN1 STEPCHARGE_INM(1) +#define STEPCHARGE_INP_MASK (0xF << 19) +#define STEPCHARGE_INP(val) ((val) << 19) +#define STEPCHARGE_INP_AN1 STEPCHARGE_INP(1) +#define STEPCHARGE_RFM_MASK (3 << 23) +#define STEPCHARGE_RFM(val) ((val) << 23) +#define STEPCHARGE_RFM_XNUR STEPCHARGE_RFM(1) + +/* Charge delay */ +#define CHARGEDLY_OPEN_MASK (0x3FFFF << 0) +#define CHARGEDLY_OPEN(val) ((val) << 0) +#define CHARGEDLY_OPENDLY CHARGEDLY_OPEN(1) + +/* Control register */ +#define CNTRLREG_TSCSSENB BIT(0) +#define CNTRLREG_STEPID BIT(1) +#define CNTRLREG_STEPCONFIGWRT BIT(2) +#define CNTRLREG_POWERDOWN BIT(4) +#define CNTRLREG_AFE_CTRL_MASK (3 << 5) +#define CNTRLREG_AFE_CTRL(val) ((val) << 5) +#define CNTRLREG_4WIRE CNTRLREG_AFE_CTRL(1) +#define CNTRLREG_5WIRE CNTRLREG_AFE_CTRL(2) +#define CNTRLREG_8WIRE CNTRLREG_AFE_CTRL(3) +#define CNTRLREG_TSCENB BIT(7) + +#define ADC_CLK 3000000 +#define MAX_CLK_DIV 7 + +#define TSCADC_CELLS 0 + +struct mfd_tscadc_board { + struct tsc_data *tsc_init; +}; + +struct ti_tscadc_dev { + struct device *dev; + struct regmap *regmap_tscadc; + void __iomem *tscadc_base; + int irq; + struct mfd_cell cells[TSCADC_CELLS]; +}; + +#endif From 2b99bafab19145a72e2c557326fc4662a864a162 Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Tue, 16 Oct 2012 12:55:44 +0530 Subject: [PATCH 007/119] input: TSC: ti_tsc: Convert TSC into a MFDevice This patch converts touchscreen into a MFD client. All the register definitions, clock initialization, etc has been moved to MFD core driver. Signed-off-by: Patil, Rachna Signed-off-by: Samuel Ortiz --- drivers/input/touchscreen/Kconfig | 2 +- drivers/input/touchscreen/ti_am335x_tsc.c | 322 +++++++--------------- drivers/mfd/ti_am335x_tscadc.c | 11 + include/linux/mfd/ti_am335x_tscadc.h | 10 +- 4 files changed, 120 insertions(+), 225 deletions(-) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index d31dc5faeaaf..0c45caddd41c 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -531,7 +531,7 @@ config TOUCHSCREEN_TOUCHWIN config TOUCHSCREEN_TI_AM335X_TSC tristate "TI Touchscreen Interface" - depends on ARCH_OMAP2PLUS + depends on MFD_TI_AM335X_TSCADC help Say Y here if you have 4/5/8 wire touchscreen controller to be connected to the ADC controller on your TI AM335x SoC. diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 462950acb97b..7a18a8a15228 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -27,106 +27,15 @@ #include #include -#define REG_RAWIRQSTATUS 0x024 -#define REG_IRQSTATUS 0x028 -#define REG_IRQENABLE 0x02C -#define REG_IRQWAKEUP 0x034 -#define REG_CTRL 0x040 -#define REG_ADCFSM 0x044 -#define REG_CLKDIV 0x04C -#define REG_SE 0x054 -#define REG_IDLECONFIG 0x058 -#define REG_CHARGECONFIG 0x05C -#define REG_CHARGEDELAY 0x060 -#define REG_STEPCONFIG(n) (0x64 + ((n - 1) * 8)) -#define REG_STEPDELAY(n) (0x68 + ((n - 1) * 8)) -#define REG_FIFO0CNT 0xE4 -#define REG_FIFO0THR 0xE8 -#define REG_FIFO1THR 0xF4 -#define REG_FIFO0 0x100 -#define REG_FIFO1 0x200 - -/* Register Bitfields */ -#define IRQWKUP_ENB BIT(0) - -/* Step Enable */ -#define STEPENB_MASK (0x1FFFF << 0) -#define STEPENB(val) (val << 0) -#define STPENB_STEPENB STEPENB(0x7FFF) - -/* IRQ enable */ -#define IRQENB_FIFO0THRES BIT(2) -#define IRQENB_FIFO1THRES BIT(5) -#define IRQENB_PENUP BIT(9) - -/* Step Configuration */ -#define STEPCONFIG_MODE_MASK (3 << 0) -#define STEPCONFIG_MODE(val) (val << 0) -#define STEPCONFIG_MODE_HWSYNC STEPCONFIG_MODE(2) -#define STEPCONFIG_AVG_MASK (7 << 2) -#define STEPCONFIG_AVG(val) (val << 2) -#define STEPCONFIG_AVG_16 STEPCONFIG_AVG(4) -#define STEPCONFIG_XPP BIT(5) -#define STEPCONFIG_XNN BIT(6) -#define STEPCONFIG_YPP BIT(7) -#define STEPCONFIG_YNN BIT(8) -#define STEPCONFIG_XNP BIT(9) -#define STEPCONFIG_YPN BIT(10) -#define STEPCONFIG_INM_MASK (0xF << 15) -#define STEPCONFIG_INM(val) (val << 15) -#define STEPCONFIG_INM_ADCREFM STEPCONFIG_INM(8) -#define STEPCONFIG_INP_MASK (0xF << 19) -#define STEPCONFIG_INP(val) (val << 19) -#define STEPCONFIG_INP_AN2 STEPCONFIG_INP(2) -#define STEPCONFIG_INP_AN3 STEPCONFIG_INP(3) -#define STEPCONFIG_INP_AN4 STEPCONFIG_INP(4) -#define STEPCONFIG_INP_ADCREFM STEPCONFIG_INP(8) -#define STEPCONFIG_FIFO1 BIT(26) - -/* Delay register */ -#define STEPDELAY_OPEN_MASK (0x3FFFF << 0) -#define STEPDELAY_OPEN(val) (val << 0) -#define STEPCONFIG_OPENDLY STEPDELAY_OPEN(0x098) - -/* Charge Config */ -#define STEPCHARGE_RFP_MASK (7 << 12) -#define STEPCHARGE_RFP(val) (val << 12) -#define STEPCHARGE_RFP_XPUL STEPCHARGE_RFP(1) -#define STEPCHARGE_INM_MASK (0xF << 15) -#define STEPCHARGE_INM(val) (val << 15) -#define STEPCHARGE_INM_AN1 STEPCHARGE_INM(1) -#define STEPCHARGE_INP_MASK (0xF << 19) -#define STEPCHARGE_INP(val) (val << 19) -#define STEPCHARGE_INP_AN1 STEPCHARGE_INP(1) -#define STEPCHARGE_RFM_MASK (3 << 23) -#define STEPCHARGE_RFM(val) (val << 23) -#define STEPCHARGE_RFM_XNUR STEPCHARGE_RFM(1) - -/* Charge delay */ -#define CHARGEDLY_OPEN_MASK (0x3FFFF << 0) -#define CHARGEDLY_OPEN(val) (val << 0) -#define CHARGEDLY_OPENDLY CHARGEDLY_OPEN(1) - -/* Control register */ -#define CNTRLREG_TSCSSENB BIT(0) -#define CNTRLREG_STEPID BIT(1) -#define CNTRLREG_STEPCONFIGWRT BIT(2) -#define CNTRLREG_AFE_CTRL_MASK (3 << 5) -#define CNTRLREG_AFE_CTRL(val) (val << 5) -#define CNTRLREG_4WIRE CNTRLREG_AFE_CTRL(1) -#define CNTRLREG_5WIRE CNTRLREG_AFE_CTRL(2) -#define CNTRLREG_8WIRE CNTRLREG_AFE_CTRL(3) -#define CNTRLREG_TSCENB BIT(7) +#include #define ADCFSM_STEPID 0x10 #define SEQ_SETTLE 275 -#define ADC_CLK 3000000 #define MAX_12BIT ((1 << 12) - 1) struct titsc { struct input_dev *input; - struct clk *tsc_ick; - void __iomem *tsc_base; + struct ti_tscadc_dev *mfd_tscadc; unsigned int irq; unsigned int wires; unsigned int x_plate_resistance; @@ -136,13 +45,13 @@ struct titsc { static unsigned int titsc_readl(struct titsc *ts, unsigned int reg) { - return readl(ts->tsc_base + reg); + return readl(ts->mfd_tscadc->tscadc_base + reg); } static void titsc_writel(struct titsc *tsc, unsigned int reg, unsigned int val) { - writel(val, tsc->tsc_base + reg); + writel(val, tsc->mfd_tscadc->tscadc_base + reg); } static void titsc_step_config(struct titsc *ts_dev) @@ -219,17 +128,7 @@ static void titsc_step_config(struct titsc *ts_dev) titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 2), STEPCONFIG_OPENDLY); - titsc_writel(ts_dev, REG_SE, STPENB_STEPENB); -} - -static void titsc_idle_config(struct titsc *ts_config) -{ - unsigned int idleconfig; - - idleconfig = STEPCONFIG_YNN | - STEPCONFIG_INM_ADCREFM | - STEPCONFIG_YPN | STEPCONFIG_INP_ADCREFM; - titsc_writel(ts_config, REG_IDLECONFIG, idleconfig); + titsc_writel(ts_dev, REG_SE, STPENB_STEPENB_TC); } static void titsc_read_coordinates(struct titsc *ts_dev, @@ -239,7 +138,7 @@ static void titsc_read_coordinates(struct titsc *ts_dev, unsigned int prev_val_x = ~0, prev_val_y = ~0; unsigned int prev_diff_x = ~0, prev_diff_y = ~0; unsigned int read, diff; - unsigned int i; + unsigned int i, channel; /* * Delta filter is used to remove large variations in sampled @@ -250,21 +149,32 @@ static void titsc_read_coordinates(struct titsc *ts_dev, * if true the value is reported to the sub system. */ for (i = 0; i < fifocount - 1; i++) { - read = titsc_readl(ts_dev, REG_FIFO0) & 0xfff; - diff = abs(read - prev_val_x); - if (diff < prev_diff_x) { - prev_diff_x = diff; - *x = read; + read = titsc_readl(ts_dev, REG_FIFO0); + channel = read & 0xf0000; + channel = channel >> 0x10; + if ((channel >= 0) && (channel < ts_dev->steps_to_configure)) { + read &= 0xfff; + diff = abs(read - prev_val_x); + if (diff < prev_diff_x) { + prev_diff_x = diff; + *x = read; + } + prev_val_x = read; } - prev_val_x = read; - read = titsc_readl(ts_dev, REG_FIFO1) & 0xfff; - diff = abs(read - prev_val_y); - if (diff < prev_diff_y) { - prev_diff_y = diff; - *y = read; + read = titsc_readl(ts_dev, REG_FIFO1); + channel = read & 0xf0000; + channel = channel >> 0x10; + if ((channel >= ts_dev->steps_to_configure) && + (channel < (2 * ts_dev->steps_to_configure - 1))) { + read &= 0xfff; + diff = abs(read - prev_val_y); + if (diff < prev_diff_y) { + prev_diff_y = diff; + *y = read; + } + prev_val_y = read; } - prev_val_y = read; } } @@ -276,6 +186,8 @@ static irqreturn_t titsc_irq(int irq, void *dev) unsigned int x = 0, y = 0; unsigned int z1, z2, z; unsigned int fsm; + unsigned int fifo1count, fifo0count; + int i; status = titsc_readl(ts_dev, REG_IRQSTATUS); if (status & IRQENB_FIFO0THRES) { @@ -284,6 +196,14 @@ static irqreturn_t titsc_irq(int irq, void *dev) z1 = titsc_readl(ts_dev, REG_FIFO0) & 0xfff; z2 = titsc_readl(ts_dev, REG_FIFO1) & 0xfff; + fifo1count = titsc_readl(ts_dev, REG_FIFO1CNT); + for (i = 0; i < fifo1count; i++) + titsc_readl(ts_dev, REG_FIFO1); + + fifo0count = titsc_readl(ts_dev, REG_FIFO0CNT); + for (i = 0; i < fifo0count; i++) + titsc_readl(ts_dev, REG_FIFO0); + if (ts_dev->pen_down && z1 != 0 && z2 != 0) { /* * Calculate pressure using formula @@ -330,7 +250,7 @@ static irqreturn_t titsc_irq(int irq, void *dev) titsc_writel(ts_dev, REG_IRQSTATUS, irqclr); - titsc_writel(ts_dev, REG_SE, STPENB_STEPENB); + titsc_writel(ts_dev, REG_SE, STPENB_STEPENB_TC); return IRQ_HANDLED; } @@ -340,28 +260,16 @@ static irqreturn_t titsc_irq(int irq, void *dev) static int __devinit titsc_probe(struct platform_device *pdev) { - const struct tsc_data *pdata = pdev->dev.platform_data; - struct resource *res; struct titsc *ts_dev; struct input_dev *input_dev; - struct clk *clk; + struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data; + struct mfd_tscadc_board *pdata; int err; - int clk_value, ctrl, irq; + + pdata = tscadc_dev->dev->platform_data; if (!pdata) { - dev_err(&pdev->dev, "missing platform data.\n"); - return -EINVAL; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no memory resource defined.\n"); - return -EINVAL; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no irq ID is specified.\n"); + dev_err(&pdev->dev, "Could not find platform data\n"); return -EINVAL; } @@ -374,85 +282,26 @@ static int __devinit titsc_probe(struct platform_device *pdev) goto err_free_mem; } + tscadc_dev->tsc = ts_dev; + ts_dev->mfd_tscadc = tscadc_dev; ts_dev->input = input_dev; - ts_dev->irq = irq; - ts_dev->wires = pdata->wires; - ts_dev->x_plate_resistance = pdata->x_plate_resistance; - ts_dev->steps_to_configure = pdata->steps_to_configure; - - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (!res) { - dev_err(&pdev->dev, "failed to reserve registers.\n"); - err = -EBUSY; - goto err_free_mem; - } - - ts_dev->tsc_base = ioremap(res->start, resource_size(res)); - if (!ts_dev->tsc_base) { - dev_err(&pdev->dev, "failed to map registers.\n"); - err = -ENOMEM; - goto err_release_mem_region; - } + ts_dev->irq = tscadc_dev->irq; + ts_dev->wires = pdata->tsc_init->wires; + ts_dev->x_plate_resistance = pdata->tsc_init->x_plate_resistance; + ts_dev->steps_to_configure = pdata->tsc_init->steps_to_configure; err = request_irq(ts_dev->irq, titsc_irq, 0, pdev->dev.driver->name, ts_dev); if (err) { dev_err(&pdev->dev, "failed to allocate irq.\n"); - goto err_unmap_regs; + goto err_free_mem; } - ts_dev->tsc_ick = clk_get(&pdev->dev, "adc_tsc_ick"); - if (IS_ERR(ts_dev->tsc_ick)) { - dev_err(&pdev->dev, "failed to get TSC ick\n"); - goto err_free_irq; - } - clk_enable(ts_dev->tsc_ick); - - clk = clk_get(&pdev->dev, "adc_tsc_fck"); - if (IS_ERR(clk)) { - dev_err(&pdev->dev, "failed to get TSC fck\n"); - err = PTR_ERR(clk); - goto err_disable_clk; - } - - clk_value = clk_get_rate(clk) / ADC_CLK; - clk_put(clk); - - if (clk_value < 7) { - dev_err(&pdev->dev, "clock input less than min clock requirement\n"); - goto err_disable_clk; - } - /* CLKDIV needs to be configured to the value minus 1 */ - titsc_writel(ts_dev, REG_CLKDIV, clk_value - 1); - - /* Enable wake-up of the SoC using touchscreen */ - titsc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); - - ctrl = CNTRLREG_STEPCONFIGWRT | - CNTRLREG_TSCENB | - CNTRLREG_STEPID; - switch (ts_dev->wires) { - case 4: - ctrl |= CNTRLREG_4WIRE; - break; - case 5: - ctrl |= CNTRLREG_5WIRE; - break; - case 8: - ctrl |= CNTRLREG_8WIRE; - break; - } - titsc_writel(ts_dev, REG_CTRL, ctrl); - - titsc_idle_config(ts_dev); titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES); titsc_step_config(ts_dev); titsc_writel(ts_dev, REG_FIFO0THR, ts_dev->steps_to_configure); - ctrl |= CNTRLREG_TSCSSENB; - titsc_writel(ts_dev, REG_CTRL, ctrl); - - input_dev->name = "ti-tsc-adc"; + input_dev->name = "ti-tsc"; input_dev->dev.parent = &pdev->dev; input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); @@ -465,20 +314,13 @@ static int __devinit titsc_probe(struct platform_device *pdev) /* register to the input system */ err = input_register_device(input_dev); if (err) - goto err_disable_clk; + goto err_free_irq; platform_set_drvdata(pdev, ts_dev); return 0; -err_disable_clk: - clk_disable(ts_dev->tsc_ick); - clk_put(ts_dev->tsc_ick); err_free_irq: free_irq(ts_dev->irq, ts_dev); -err_unmap_regs: - iounmap(ts_dev->tsc_base); -err_release_mem_region: - release_mem_region(res->start, resource_size(res)); err_free_mem: input_free_device(input_dev); kfree(ts_dev); @@ -487,32 +329,66 @@ err_free_mem: static int __devexit titsc_remove(struct platform_device *pdev) { - struct titsc *ts_dev = platform_get_drvdata(pdev); - struct resource *res; + struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data; + struct titsc *ts_dev = tscadc_dev->tsc; free_irq(ts_dev->irq, ts_dev); input_unregister_device(ts_dev->input); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - iounmap(ts_dev->tsc_base); - release_mem_region(res->start, resource_size(res)); - - clk_disable(ts_dev->tsc_ick); - clk_put(ts_dev->tsc_ick); - - kfree(ts_dev); - platform_set_drvdata(pdev, NULL); + kfree(ts_dev); return 0; } +#ifdef CONFIG_PM +static int titsc_suspend(struct device *dev) +{ + struct ti_tscadc_dev *tscadc_dev = dev->platform_data; + struct titsc *ts_dev = tscadc_dev->tsc; + unsigned int idle; + + if (device_may_wakeup(tscadc_dev->dev)) { + idle = titsc_readl(ts_dev, REG_IRQENABLE); + titsc_writel(ts_dev, REG_IRQENABLE, + (idle | IRQENB_HW_PEN)); + titsc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); + } + return 0; +} + +static int titsc_resume(struct device *dev) +{ + struct ti_tscadc_dev *tscadc_dev = dev->platform_data; + struct titsc *ts_dev = tscadc_dev->tsc; + + if (device_may_wakeup(tscadc_dev->dev)) { + titsc_writel(ts_dev, REG_IRQWAKEUP, + 0x00); + titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); + } + titsc_step_config(ts_dev); + titsc_writel(ts_dev, REG_FIFO0THR, + ts_dev->steps_to_configure); + return 0; +} + +static const struct dev_pm_ops titsc_pm_ops = { + .suspend = titsc_suspend, + .resume = titsc_resume, +}; +#define TITSC_PM_OPS (&titsc_pm_ops) +#else +#define TITSC_PM_OPS NULL +#endif + static struct platform_driver ti_tsc_driver = { .probe = titsc_probe, .remove = __devexit_p(titsc_remove), .driver = { .name = "tsc", .owner = THIS_MODULE, + .pm = TITSC_PM_OPS, }, }; module_platform_driver(ti_tsc_driver); diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index 14df67bc390f..d812be4b61df 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -24,6 +24,7 @@ #include #include +#include static unsigned int tscadc_readl(struct ti_tscadc_dev *tsadc, unsigned int reg) { @@ -62,15 +63,19 @@ static int __devinit ti_tscadc_probe(struct platform_device *pdev) struct resource *res; struct clk *clk; struct mfd_tscadc_board *pdata = pdev->dev.platform_data; + struct mfd_cell *cell; int irq; int err, ctrl; int clk_value, clock_rate; + int tsc_wires; if (!pdata) { dev_err(&pdev->dev, "Could not find platform data\n"); return -EINVAL; } + tsc_wires = pdata->tsc_init->wires; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "no memory resource defined.\n"); @@ -161,6 +166,12 @@ static int __devinit ti_tscadc_probe(struct platform_device *pdev) ctrl |= CNTRLREG_TSCSSENB; tscadc_writel(tscadc, REG_CTRL, ctrl); + /* TSC Cell */ + cell = &tscadc->cells[TSC_CELL]; + cell->name = "tsc"; + cell->platform_data = tscadc; + cell->pdata_size = sizeof(*tscadc); + err = mfd_add_devices(&pdev->dev, pdev->id, tscadc->cells, TSCADC_CELLS, NULL, 0, NULL); if (err < 0) diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h index b7232b157061..fc18b2ef753f 100644 --- a/include/linux/mfd/ti_am335x_tscadc.h +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -47,6 +47,7 @@ #define STEPENB_MASK (0x1FFFF << 0) #define STEPENB(val) ((val) << 0) #define STPENB_STEPENB STEPENB(0x1FFFF) +#define STPENB_STEPENB_TC STEPENB(0x1FFF) /* IRQ enable */ #define IRQENB_HW_PEN BIT(0) @@ -120,7 +121,11 @@ #define ADC_CLK 3000000 #define MAX_CLK_DIV 7 -#define TSCADC_CELLS 0 +#define TSCADC_CELLS 1 + +enum tscadc_cells { + TSC_CELL, +}; struct mfd_tscadc_board { struct tsc_data *tsc_init; @@ -132,6 +137,9 @@ struct ti_tscadc_dev { void __iomem *tscadc_base; int irq; struct mfd_cell cells[TSCADC_CELLS]; + + /* tsc device */ + struct titsc *tsc; }; #endif From 5e53a69b44e893227b046a7bc74db3cb40d7f39b Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Tue, 16 Oct 2012 12:55:45 +0530 Subject: [PATCH 008/119] IIO : ADC: tiadc: Add support of TI's ADC driver This patch adds support for TI's ADC driver. This is a multifunctional device. Analog input lines are provided on which voltage measurements can be carried out. You can have upto 8 input lines. Signed-off-by: Patil, Rachna Acked-by: Jonathan Cameron Signed-off-by: Samuel Ortiz --- drivers/iio/adc/Kconfig | 7 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ti_am335x_adc.c | 260 ++++++++++++++++++++ drivers/mfd/ti_am335x_tscadc.c | 18 +- include/linux/mfd/ti_am335x_tscadc.h | 9 +- include/linux/platform_data/ti_am335x_adc.h | 14 ++ 6 files changed, 307 insertions(+), 2 deletions(-) create mode 100644 drivers/iio/adc/ti_am335x_adc.c create mode 100644 include/linux/platform_data/ti_am335x_adc.h diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 492758120338..1401ed1af39f 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -60,4 +60,11 @@ config LP8788_ADC help Say yes here to build support for TI LP8788 ADC. +config TI_AM335X_ADC + tristate "TI's ADC driver" + depends on MFD_TI_AM335X_TSCADC + help + Say yes here to build support for Texas Instruments ADC + driver which is also a MFD client. + endmenu diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 900995d5e179..4410a90fc84b 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_AD7476) += ad7476.o obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o +obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c new file mode 100644 index 000000000000..02a43c87a8a3 --- /dev/null +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -0,0 +1,260 @@ +/* + * TI ADC MFD driver + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct tiadc_device { + struct ti_tscadc_dev *mfd_tscadc; + int channels; +}; + +static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg) +{ + return readl(adc->mfd_tscadc->tscadc_base + reg); +} + +static void tiadc_writel(struct tiadc_device *adc, unsigned int reg, + unsigned int val) +{ + writel(val, adc->mfd_tscadc->tscadc_base + reg); +} + +static void tiadc_step_config(struct tiadc_device *adc_dev) +{ + unsigned int stepconfig; + int i, channels = 0, steps; + + /* + * There are 16 configurable steps and 8 analog input + * lines available which are shared between Touchscreen and ADC. + * + * Steps backwards i.e. from 16 towards 0 are used by ADC + * depending on number of input lines needed. + * Channel would represent which analog input + * needs to be given to ADC to digitalize data. + */ + + steps = TOTAL_STEPS - adc_dev->channels; + channels = TOTAL_CHANNELS - adc_dev->channels; + + stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1; + + for (i = (steps + 1); i <= TOTAL_STEPS; i++) { + tiadc_writel(adc_dev, REG_STEPCONFIG(i), + stepconfig | STEPCONFIG_INP(channels)); + tiadc_writel(adc_dev, REG_STEPDELAY(i), + STEPCONFIG_OPENDLY); + channels++; + } + tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB); +} + +static int tiadc_channel_init(struct iio_dev *indio_dev, int channels) +{ + struct iio_chan_spec *chan_array; + int i; + + indio_dev->num_channels = channels; + chan_array = kcalloc(indio_dev->num_channels, + sizeof(struct iio_chan_spec), GFP_KERNEL); + + if (chan_array == NULL) + return -ENOMEM; + + for (i = 0; i < (indio_dev->num_channels); i++) { + struct iio_chan_spec *chan = chan_array + i; + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = i; + chan->info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT; + } + + indio_dev->channels = chan_array; + + return indio_dev->num_channels; +} + +static void tiadc_channels_remove(struct iio_dev *indio_dev) +{ + kfree(indio_dev->channels); +} + +static int tiadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + int i; + unsigned int fifo1count, readx1; + + /* + * When the sub-system is first enabled, + * the sequencer will always start with the + * lowest step (1) and continue until step (16). + * For ex: If we have enabled 4 ADC channels and + * currently use only 1 out of them, the + * sequencer still configures all the 4 steps, + * leading to 3 unwanted data. + * Hence we need to flush out this data. + */ + + fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); + for (i = 0; i < fifo1count; i++) { + readx1 = tiadc_readl(adc_dev, REG_FIFO1); + if (i == chan->channel) + *val = readx1 & 0xfff; + } + tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB); + + return IIO_VAL_INT; +} + +static const struct iio_info tiadc_info = { + .read_raw = &tiadc_read_raw, +}; + +static int __devinit tiadc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct tiadc_device *adc_dev; + struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data; + struct mfd_tscadc_board *pdata; + int err; + + pdata = tscadc_dev->dev->platform_data; + if (!pdata || !pdata->adc_init) { + dev_err(&pdev->dev, "Could not find platform data\n"); + return -EINVAL; + } + + indio_dev = iio_device_alloc(sizeof(struct tiadc_device)); + if (indio_dev == NULL) { + dev_err(&pdev->dev, "failed to allocate iio device\n"); + err = -ENOMEM; + goto err_ret; + } + adc_dev = iio_priv(indio_dev); + + adc_dev->mfd_tscadc = tscadc_dev; + adc_dev->channels = pdata->adc_init->adc_channels; + + indio_dev->dev.parent = &pdev->dev; + indio_dev->name = dev_name(&pdev->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &tiadc_info; + + tiadc_step_config(adc_dev); + + err = tiadc_channel_init(indio_dev, adc_dev->channels); + if (err < 0) + goto err_free_device; + + err = iio_device_register(indio_dev); + if (err) + goto err_free_channels; + + platform_set_drvdata(pdev, indio_dev); + + return 0; + +err_free_channels: + tiadc_channels_remove(indio_dev); +err_free_device: + iio_device_free(indio_dev); +err_ret: + return err; +} + +static int __devexit tiadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + tiadc_channels_remove(indio_dev); + + iio_device_free(indio_dev); + + return 0; +} + +#ifdef CONFIG_PM +static int tiadc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct ti_tscadc_dev *tscadc_dev = dev->platform_data; + unsigned int idle; + + if (!device_may_wakeup(tscadc_dev->dev)) { + idle = tiadc_readl(adc_dev, REG_CTRL); + idle &= ~(CNTRLREG_TSCSSENB); + tiadc_writel(adc_dev, REG_CTRL, (idle | + CNTRLREG_POWERDOWN)); + } + + return 0; +} + +static int tiadc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tiadc_device *adc_dev = iio_priv(indio_dev); + unsigned int restore; + + /* Make sure ADC is powered up */ + restore = tiadc_readl(adc_dev, REG_CTRL); + restore &= ~(CNTRLREG_POWERDOWN); + tiadc_writel(adc_dev, REG_CTRL, restore); + + tiadc_step_config(adc_dev); + + return 0; +} + +static const struct dev_pm_ops tiadc_pm_ops = { + .suspend = tiadc_suspend, + .resume = tiadc_resume, +}; +#define TIADC_PM_OPS (&tiadc_pm_ops) +#else +#define TIADC_PM_OPS NULL +#endif + +static struct platform_driver tiadc_driver = { + .driver = { + .name = "tiadc", + .owner = THIS_MODULE, + .pm = TIADC_PM_OPS, + }, + .probe = tiadc_probe, + .remove = __devexit_p(tiadc_remove), +}; + +module_platform_driver(tiadc_driver); + +MODULE_DESCRIPTION("TI ADC controller driver"); +MODULE_AUTHOR("Rachna Patil "); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index d812be4b61df..e947dd8bbcc3 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -25,6 +25,7 @@ #include #include +#include static unsigned int tscadc_readl(struct ti_tscadc_dev *tsadc, unsigned int reg) { @@ -67,14 +68,23 @@ static int __devinit ti_tscadc_probe(struct platform_device *pdev) int irq; int err, ctrl; int clk_value, clock_rate; - int tsc_wires; + int tsc_wires, adc_channels = 0, total_channels; if (!pdata) { dev_err(&pdev->dev, "Could not find platform data\n"); return -EINVAL; } + if (pdata->adc_init) + adc_channels = pdata->adc_init->adc_channels; + tsc_wires = pdata->tsc_init->wires; + total_channels = tsc_wires + adc_channels; + + if (total_channels > 8) { + dev_err(&pdev->dev, "Number of i/p channels more than 8\n"); + return -EINVAL; + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -172,6 +182,12 @@ static int __devinit ti_tscadc_probe(struct platform_device *pdev) cell->platform_data = tscadc; cell->pdata_size = sizeof(*tscadc); + /* ADC Cell */ + cell = &tscadc->cells[ADC_CELL]; + cell->name = "tiadc"; + cell->platform_data = tscadc; + cell->pdata_size = sizeof(*tscadc); + err = mfd_add_devices(&pdev->dev, pdev->id, tscadc->cells, TSCADC_CELLS, NULL, 0, NULL); if (err < 0) diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h index fc18b2ef753f..c79ad5d2f271 100644 --- a/include/linux/mfd/ti_am335x_tscadc.h +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -120,15 +120,19 @@ #define ADC_CLK 3000000 #define MAX_CLK_DIV 7 +#define TOTAL_STEPS 16 +#define TOTAL_CHANNELS 8 -#define TSCADC_CELLS 1 +#define TSCADC_CELLS 2 enum tscadc_cells { TSC_CELL, + ADC_CELL, }; struct mfd_tscadc_board { struct tsc_data *tsc_init; + struct adc_data *adc_init; }; struct ti_tscadc_dev { @@ -140,6 +144,9 @@ struct ti_tscadc_dev { /* tsc device */ struct titsc *tsc; + + /* adc device */ + struct adc_device *adc; }; #endif diff --git a/include/linux/platform_data/ti_am335x_adc.h b/include/linux/platform_data/ti_am335x_adc.h new file mode 100644 index 000000000000..e41d5834cb84 --- /dev/null +++ b/include/linux/platform_data/ti_am335x_adc.h @@ -0,0 +1,14 @@ +#ifndef __LINUX_TI_AM335X_ADC_H +#define __LINUX_TI_AM335X_ADC_H + +/** + * struct adc_data ADC Input information + * @adc_channels: Number of analog inputs + * available for ADC. + */ + +struct adc_data { + unsigned int adc_channels; +}; + +#endif From cd0f34b08f98af72bb2f74fe4bd251558fc734d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 12 Jul 2012 09:57:52 +0000 Subject: [PATCH 009/119] mfd: mc13xxx: Change probing details for mc13xxx devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes auto-detection of which variant of mc13xxx is used because mc34708 uses a different layout in the revision register that doesn't allow differentiation any more. Signed-off-by: Uwe Kleine-König Acked-by: Marc Reilly Signed-off-by: Samuel Ortiz --- drivers/mfd/mc13xxx-core.c | 79 ++++++++++++++------------------------ drivers/mfd/mc13xxx-i2c.c | 16 +++++--- drivers/mfd/mc13xxx-spi.c | 25 ++++++------ drivers/mfd/mc13xxx.h | 17 +++++--- 4 files changed, 61 insertions(+), 76 deletions(-) diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 1aba0238f426..40afdb9b73f5 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -410,62 +410,36 @@ static irqreturn_t mc13xxx_irq_thread(int irq, void *data) return IRQ_RETVAL(handled); } -static const char *mc13xxx_chipname[] = { - [MC13XXX_ID_MC13783] = "mc13783", - [MC13XXX_ID_MC13892] = "mc13892", -}; - #define maskval(reg, mask) (((reg) & (mask)) >> __ffs(mask)) -static int mc13xxx_identify(struct mc13xxx *mc13xxx) +static void mc13xxx_print_revision(struct mc13xxx *mc13xxx, u32 revision) { - u32 icid; - u32 revision; - int ret; - - /* - * Get the generation ID from register 46, as apparently some older - * IC revisions only have this info at this location. Newer ICs seem to - * have both. - */ - ret = mc13xxx_reg_read(mc13xxx, 46, &icid); - if (ret) - return ret; - - icid = (icid >> 6) & 0x7; - - switch (icid) { - case 2: - mc13xxx->ictype = MC13XXX_ID_MC13783; - break; - case 7: - mc13xxx->ictype = MC13XXX_ID_MC13892; - break; - default: - mc13xxx->ictype = MC13XXX_ID_INVALID; - break; - } - - if (mc13xxx->ictype == MC13XXX_ID_MC13783 || - mc13xxx->ictype == MC13XXX_ID_MC13892) { - ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision); - - dev_info(mc13xxx->dev, "%s: rev: %d.%d, " - "fin: %d, fab: %d, icid: %d/%d\n", - mc13xxx_chipname[mc13xxx->ictype], - maskval(revision, MC13XXX_REVISION_REVFULL), - maskval(revision, MC13XXX_REVISION_REVMETAL), - maskval(revision, MC13XXX_REVISION_FIN), - maskval(revision, MC13XXX_REVISION_FAB), - maskval(revision, MC13XXX_REVISION_ICID), - maskval(revision, MC13XXX_REVISION_ICIDCODE)); - } - - return (mc13xxx->ictype == MC13XXX_ID_INVALID) ? -ENODEV : 0; + dev_info(mc13xxx->dev, "%s: rev: %d.%d, " + "fin: %d, fab: %d, icid: %d/%d\n", + mc13xxx->variant->name, + maskval(revision, MC13XXX_REVISION_REVFULL), + maskval(revision, MC13XXX_REVISION_REVMETAL), + maskval(revision, MC13XXX_REVISION_FIN), + maskval(revision, MC13XXX_REVISION_FAB), + maskval(revision, MC13XXX_REVISION_ICID), + maskval(revision, MC13XXX_REVISION_ICIDCODE)); } +/* These are only exported for mc13xxx-i2c and mc13xxx-spi */ +struct mc13xxx_variant mc13xxx_variant_mc13783 = { + .name = "mc13783", + .print_revision = mc13xxx_print_revision, +}; +EXPORT_SYMBOL_GPL(mc13xxx_variant_mc13783); + +struct mc13xxx_variant mc13xxx_variant_mc13892 = { + .name = "mc13892", + .print_revision = mc13xxx_print_revision, +}; +EXPORT_SYMBOL_GPL(mc13xxx_variant_mc13892); + static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx) { - return mc13xxx_chipname[mc13xxx->ictype]; + return mc13xxx->variant->name; } int mc13xxx_get_flags(struct mc13xxx *mc13xxx) @@ -653,13 +627,16 @@ int mc13xxx_common_init(struct mc13xxx *mc13xxx, struct mc13xxx_platform_data *pdata, int irq) { int ret; + u32 revision; mc13xxx_lock(mc13xxx); - ret = mc13xxx_identify(mc13xxx); + ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision); if (ret) goto err_revision; + mc13xxx->variant->print_revision(mc13xxx, revision); + /* mask all irqs */ ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK0, 0x00ffffff); if (ret) diff --git a/drivers/mfd/mc13xxx-i2c.c b/drivers/mfd/mc13xxx-i2c.c index 9d18dde3cd2a..4a0afc7f2d4e 100644 --- a/drivers/mfd/mc13xxx-i2c.c +++ b/drivers/mfd/mc13xxx-i2c.c @@ -24,7 +24,7 @@ static const struct i2c_device_id mc13xxx_i2c_device_id[] = { { .name = "mc13892", - .driver_data = MC13XXX_ID_MC13892, + .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc13892, }, { /* sentinel */ } @@ -34,7 +34,7 @@ MODULE_DEVICE_TABLE(i2c, mc13xxx_i2c_device_id); static const struct of_device_id mc13xxx_dt_ids[] = { { .compatible = "fsl,mc13892", - .data = (void *) &mc13xxx_i2c_device_id[0], + .data = &mc13xxx_variant_mc13892, }, { /* sentinel */ } @@ -76,11 +76,15 @@ static int mc13xxx_i2c_probe(struct i2c_client *client, return ret; } - ret = mc13xxx_common_init(mc13xxx, pdata, client->irq); + if (client->dev.of_node) { + const struct of_device_id *of_id = + of_match_device(mc13xxx_dt_ids, &client->dev); + mc13xxx->variant = of_id->data; + } else { + mc13xxx->variant = (void *)id->driver_data; + } - if (ret == 0 && (id->driver_data != mc13xxx->ictype)) - dev_warn(mc13xxx->dev, - "device id doesn't match auto detection!\n"); + ret = mc13xxx_common_init(mc13xxx, pdata, client->irq); return ret; } diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c index 0bdb43a0aff0..9b1e60827f37 100644 --- a/drivers/mfd/mc13xxx-spi.c +++ b/drivers/mfd/mc13xxx-spi.c @@ -28,10 +28,10 @@ static const struct spi_device_id mc13xxx_device_id[] = { { .name = "mc13783", - .driver_data = MC13XXX_ID_MC13783, + .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc13783, }, { .name = "mc13892", - .driver_data = MC13XXX_ID_MC13892, + .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc13892, }, { /* sentinel */ } @@ -39,8 +39,8 @@ static const struct spi_device_id mc13xxx_device_id[] = { MODULE_DEVICE_TABLE(spi, mc13xxx_device_id); static const struct of_device_id mc13xxx_dt_ids[] = { - { .compatible = "fsl,mc13783", .data = (void *) MC13XXX_ID_MC13783, }, - { .compatible = "fsl,mc13892", .data = (void *) MC13XXX_ID_MC13892, }, + { .compatible = "fsl,mc13783", .data = &mc13xxx_variant_mc13783, }, + { .compatible = "fsl,mc13892", .data = &mc13xxx_variant_mc13892, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids); @@ -144,19 +144,18 @@ static int mc13xxx_spi_probe(struct spi_device *spi) return ret; } - ret = mc13xxx_common_init(mc13xxx, pdata, spi->irq); + if (spi->dev.of_node) { + const struct of_device_id *of_id = + of_match_device(mc13xxx_dt_ids, &spi->dev); - if (ret) { - dev_set_drvdata(&spi->dev, NULL); + mc13xxx->variant = of_id->data; } else { - const struct spi_device_id *devid = - spi_get_device_id(spi); - if (!devid || devid->driver_data != mc13xxx->ictype) - dev_warn(mc13xxx->dev, - "device id doesn't match auto detection!\n"); + const struct spi_device_id *id_entry = spi_get_device_id(spi); + + mc13xxx->variant = (void *)id_entry->driver_data; } - return ret; + return mc13xxx_common_init(mc13xxx, pdata, spi->irq); } static int __devexit mc13xxx_spi_remove(struct spi_device *spi) diff --git a/drivers/mfd/mc13xxx.h b/drivers/mfd/mc13xxx.h index bbba06feea06..78bf4c3c1fac 100644 --- a/drivers/mfd/mc13xxx.h +++ b/drivers/mfd/mc13xxx.h @@ -13,19 +13,24 @@ #include #include -enum mc13xxx_id { - MC13XXX_ID_MC13783, - MC13XXX_ID_MC13892, - MC13XXX_ID_INVALID, +#define MC13XXX_NUMREGS 0x3f + +struct mc13xxx; + +struct mc13xxx_variant { + const char *name; + void (*print_revision)(struct mc13xxx *mc13xxx, u32 revision); }; -#define MC13XXX_NUMREGS 0x3f +extern struct mc13xxx_variant + mc13xxx_variant_mc13783, + mc13xxx_variant_mc13892; struct mc13xxx { struct regmap *regmap; struct device *dev; - enum mc13xxx_id ictype; + const struct mc13xxx_variant *variant; struct mutex lock; int irq; From 0312e024d6cde5ef02900c4c6e2f5bb982e24af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=83=C2=B6nig?= Date: Thu, 12 Jul 2012 09:57:53 +0000 Subject: [PATCH 010/119] mfd: mc13xxx: Add support for mc34708 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Acked-by: Marc Reilly Signed-off-by: Samuel Ortiz --- drivers/mfd/mc13xxx-core.c | 21 +++++++++++++++++++++ drivers/mfd/mc13xxx-i2c.c | 6 ++++++ drivers/mfd/mc13xxx-spi.c | 4 ++++ drivers/mfd/mc13xxx.h | 3 ++- 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 40afdb9b73f5..2a9b100c4825 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -119,6 +119,11 @@ #define MC13XXX_REVISION_FAB (0x03 << 11) #define MC13XXX_REVISION_ICIDCODE (0x3f << 13) +#define MC34708_REVISION_REVMETAL (0x07 << 0) +#define MC34708_REVISION_REVFULL (0x07 << 3) +#define MC34708_REVISION_FIN (0x07 << 6) +#define MC34708_REVISION_FAB (0x07 << 9) + #define MC13XXX_ADC1 44 #define MC13XXX_ADC1_ADEN (1 << 0) #define MC13XXX_ADC1_RAND (1 << 1) @@ -424,6 +429,16 @@ static void mc13xxx_print_revision(struct mc13xxx *mc13xxx, u32 revision) maskval(revision, MC13XXX_REVISION_ICIDCODE)); } +static void mc34708_print_revision(struct mc13xxx *mc13xxx, u32 revision) +{ + dev_info(mc13xxx->dev, "%s: rev %d.%d, fin: %d, fab: %d\n", + mc13xxx->variant->name, + maskval(revision, MC34708_REVISION_REVFULL), + maskval(revision, MC34708_REVISION_REVMETAL), + maskval(revision, MC34708_REVISION_FIN), + maskval(revision, MC34708_REVISION_FAB)); +} + /* These are only exported for mc13xxx-i2c and mc13xxx-spi */ struct mc13xxx_variant mc13xxx_variant_mc13783 = { .name = "mc13783", @@ -437,6 +452,12 @@ struct mc13xxx_variant mc13xxx_variant_mc13892 = { }; EXPORT_SYMBOL_GPL(mc13xxx_variant_mc13892); +struct mc13xxx_variant mc13xxx_variant_mc34708 = { + .name = "mc34708", + .print_revision = mc34708_print_revision, +}; +EXPORT_SYMBOL_GPL(mc13xxx_variant_mc34708); + static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx) { return mc13xxx->variant->name; diff --git a/drivers/mfd/mc13xxx-i2c.c b/drivers/mfd/mc13xxx-i2c.c index 4a0afc7f2d4e..bfc1284537ea 100644 --- a/drivers/mfd/mc13xxx-i2c.c +++ b/drivers/mfd/mc13xxx-i2c.c @@ -25,6 +25,9 @@ static const struct i2c_device_id mc13xxx_i2c_device_id[] = { { .name = "mc13892", .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc13892, + }, { + .name = "mc34708", + .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc34708, }, { /* sentinel */ } @@ -35,6 +38,9 @@ static const struct of_device_id mc13xxx_dt_ids[] = { { .compatible = "fsl,mc13892", .data = &mc13xxx_variant_mc13892, + }, { + .compatible = "fsl,mc34708", + .data = &mc13xxx_variant_mc34708, }, { /* sentinel */ } diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c index 9b1e60827f37..afca4f92f0cf 100644 --- a/drivers/mfd/mc13xxx-spi.c +++ b/drivers/mfd/mc13xxx-spi.c @@ -32,6 +32,9 @@ static const struct spi_device_id mc13xxx_device_id[] = { }, { .name = "mc13892", .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc13892, + }, { + .name = "mc34708", + .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc34708, }, { /* sentinel */ } @@ -41,6 +44,7 @@ MODULE_DEVICE_TABLE(spi, mc13xxx_device_id); static const struct of_device_id mc13xxx_dt_ids[] = { { .compatible = "fsl,mc13783", .data = &mc13xxx_variant_mc13783, }, { .compatible = "fsl,mc13892", .data = &mc13xxx_variant_mc13892, }, + { .compatible = "fsl,mc34708", .data = &mc13xxx_variant_mc34708, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids); diff --git a/drivers/mfd/mc13xxx.h b/drivers/mfd/mc13xxx.h index 78bf4c3c1fac..460ec5c7b18c 100644 --- a/drivers/mfd/mc13xxx.h +++ b/drivers/mfd/mc13xxx.h @@ -24,7 +24,8 @@ struct mc13xxx_variant { extern struct mc13xxx_variant mc13xxx_variant_mc13783, - mc13xxx_variant_mc13892; + mc13xxx_variant_mc13892, + mc13xxx_variant_mc34708; struct mc13xxx { struct regmap *regmap; From ada8a8a13b13a2749818524a7949935f68d2b3eb Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Mon, 29 Oct 2012 13:49:33 +0800 Subject: [PATCH 011/119] mfd: Add realtek pcie card reader driver Realtek PCI-E card reader driver adapts requests from upper-level sdmmc/memstick layer to the real physical card reader. Signed-off-by: Wei WANG Reviewed-by: Arnd Bergmann Tested-by: Borislav Petkov Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 9 + drivers/mfd/Makefile | 3 + drivers/mfd/rtl8411.c | 251 +++++++ drivers/mfd/rts5209.c | 223 ++++++ drivers/mfd/rts5229.c | 205 +++++ drivers/mfd/rtsx_pcr.c | 1251 +++++++++++++++++++++++++++++++ drivers/mfd/rtsx_pcr.h | 32 + include/linux/mfd/rtsx_common.h | 48 ++ include/linux/mfd/rtsx_pci.h | 794 ++++++++++++++++++++ 9 files changed, 2816 insertions(+) create mode 100644 drivers/mfd/rtl8411.c create mode 100644 drivers/mfd/rts5209.c create mode 100644 drivers/mfd/rts5229.c create mode 100644 drivers/mfd/rtsx_pcr.c create mode 100644 drivers/mfd/rtsx_pcr.h create mode 100644 include/linux/mfd/rtsx_common.h create mode 100644 include/linux/mfd/rtsx_pci.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9bba7f78ff36..d0cb4d4bc2cc 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -63,6 +63,15 @@ config MFD_SM501_GPIO lines on the SM501. The platform data is used to supply the base number for the first GPIO line to register. +config MFD_RTSX_PCI + tristate "Support for Realtek PCI-E card reader" + depends on PCI + help + This supports for Realtek PCI-Express card reader including rts5209, + rts5229, rtl8411, etc. Realtek card reader supports access to many + types of memory cards, such as Memory Stick, Memory Stick Pro, + Secure Digital and MultiMediaCard. + config MFD_ASIC3 bool "Support for Compaq ASIC3" depends on GENERIC_HARDIRQS && GPIOLIB && ARM diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 442c17e40741..a4093a44304a 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -9,6 +9,9 @@ obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o +rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o +obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o + obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o diff --git a/drivers/mfd/rtl8411.c b/drivers/mfd/rtl8411.c new file mode 100644 index 000000000000..89f046ca9e41 --- /dev/null +++ b/drivers/mfd/rtl8411.c @@ -0,0 +1,251 @@ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + * Author: + * Wei WANG + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + */ + +#include +#include +#include +#include + +#include "rtsx_pcr.h" + +static u8 rtl8411_get_ic_version(struct rtsx_pcr *pcr) +{ + u8 val; + + rtsx_pci_read_register(pcr, SYS_VER, &val); + return val & 0x0F; +} + +static int rtl8411_extra_init_hw(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, CD_PAD_CTL, + CD_DISABLE_MASK | CD_AUTO_DISABLE, CD_ENABLE); +} + +static int rtl8411_turn_on_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x00); +} + +static int rtl8411_turn_off_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x01); +} + +static int rtl8411_enable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0xFF, 0x0D); +} + +static int rtl8411_disable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0x08, 0x00); +} + +static int rtl8411_card_power_on(struct rtsx_pcr *pcr, int card) +{ + int err; + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + BPP_POWER_MASK, BPP_POWER_5_PERCENT_ON); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_CTL, + BPP_LDO_POWB, BPP_LDO_SUSPEND); + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + /* To avoid too large in-rush current */ + udelay(150); + + err = rtsx_pci_write_register(pcr, CARD_PWR_CTL, + BPP_POWER_MASK, BPP_POWER_10_PERCENT_ON); + if (err < 0) + return err; + + udelay(150); + + err = rtsx_pci_write_register(pcr, CARD_PWR_CTL, + BPP_POWER_MASK, BPP_POWER_15_PERCENT_ON); + if (err < 0) + return err; + + udelay(150); + + err = rtsx_pci_write_register(pcr, CARD_PWR_CTL, + BPP_POWER_MASK, BPP_POWER_ON); + if (err < 0) + return err; + + return rtsx_pci_write_register(pcr, LDO_CTL, BPP_LDO_POWB, BPP_LDO_ON); +} + +static int rtl8411_card_power_off(struct rtsx_pcr *pcr, int card) +{ + int err; + + err = rtsx_pci_write_register(pcr, CARD_PWR_CTL, + BPP_POWER_MASK, BPP_POWER_OFF); + if (err < 0) + return err; + + return rtsx_pci_write_register(pcr, LDO_CTL, + BPP_LDO_POWB, BPP_LDO_SUSPEND); +} + +static unsigned int rtl8411_cd_deglitch(struct rtsx_pcr *pcr) +{ + unsigned int card_exist; + + card_exist = rtsx_pci_readl(pcr, RTSX_BIPR); + card_exist &= CARD_EXIST; + if (!card_exist) { + /* Enable card CD */ + rtsx_pci_write_register(pcr, CD_PAD_CTL, + CD_DISABLE_MASK, CD_ENABLE); + /* Enable card interrupt */ + rtsx_pci_write_register(pcr, EFUSE_CONTENT, 0xe0, 0x00); + return 0; + } + + if (hweight32(card_exist) > 1) { + rtsx_pci_write_register(pcr, CARD_PWR_CTL, + BPP_POWER_MASK, BPP_POWER_5_PERCENT_ON); + msleep(100); + + card_exist = rtsx_pci_readl(pcr, RTSX_BIPR); + if (card_exist & MS_EXIST) + card_exist = MS_EXIST; + else if (card_exist & SD_EXIST) + card_exist = SD_EXIST; + else + card_exist = 0; + + rtsx_pci_write_register(pcr, CARD_PWR_CTL, + BPP_POWER_MASK, BPP_POWER_OFF); + + dev_dbg(&(pcr->pci->dev), + "After CD deglitch, card_exist = 0x%x\n", + card_exist); + } + + if (card_exist & MS_EXIST) { + /* Disable SD interrupt */ + rtsx_pci_write_register(pcr, EFUSE_CONTENT, 0xe0, 0x40); + rtsx_pci_write_register(pcr, CD_PAD_CTL, + CD_DISABLE_MASK, MS_CD_EN_ONLY); + } else if (card_exist & SD_EXIST) { + /* Disable MS interrupt */ + rtsx_pci_write_register(pcr, EFUSE_CONTENT, 0xe0, 0x80); + rtsx_pci_write_register(pcr, CD_PAD_CTL, + CD_DISABLE_MASK, SD_CD_EN_ONLY); + } + + return card_exist; +} + +static const struct pcr_ops rtl8411_pcr_ops = { + .extra_init_hw = rtl8411_extra_init_hw, + .optimize_phy = NULL, + .turn_on_led = rtl8411_turn_on_led, + .turn_off_led = rtl8411_turn_off_led, + .enable_auto_blink = rtl8411_enable_auto_blink, + .disable_auto_blink = rtl8411_disable_auto_blink, + .card_power_on = rtl8411_card_power_on, + .card_power_off = rtl8411_card_power_off, + .cd_deglitch = rtl8411_cd_deglitch, +}; + +/* SD Pull Control Enable: + * SD_DAT[3:0] ==> pull up + * SD_CD ==> pull up + * SD_WP ==> pull up + * SD_CMD ==> pull up + * SD_CLK ==> pull down + */ +static const u32 rtl8411_sd_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL1, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xA9), + RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09), + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x09), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04), + 0, +}; + +/* SD Pull Control Disable: + * SD_DAT[3:0] ==> pull down + * SD_CD ==> pull up + * SD_WP ==> pull down + * SD_CMD ==> pull down + * SD_CLK ==> pull down + */ +static const u32 rtl8411_sd_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65), + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0x95), + RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09), + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04), + 0, +}; + +/* MS Pull Control Enable: + * MS CD ==> pull up + * others ==> pull down + */ +static const u32 rtl8411_ms_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65), + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0x95), + RTSX_REG_PAIR(CARD_PULL_CTL4, 0x05), + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04), + 0, +}; + +/* MS Pull Control Disable: + * MS CD ==> pull up + * others ==> pull down + */ +static const u32 rtl8411_ms_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65), + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0x95), + RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09), + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04), + 0, +}; + +void rtl8411_init_params(struct rtsx_pcr *pcr) +{ + pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; + pcr->num_slots = 2; + pcr->ops = &rtl8411_pcr_ops; + + pcr->ic_version = rtl8411_get_ic_version(pcr); + pcr->sd_pull_ctl_enable_tbl = rtl8411_sd_pull_ctl_enable_tbl; + pcr->sd_pull_ctl_disable_tbl = rtl8411_sd_pull_ctl_disable_tbl; + pcr->ms_pull_ctl_enable_tbl = rtl8411_ms_pull_ctl_enable_tbl; + pcr->ms_pull_ctl_disable_tbl = rtl8411_ms_pull_ctl_disable_tbl; +} diff --git a/drivers/mfd/rts5209.c b/drivers/mfd/rts5209.c new file mode 100644 index 000000000000..283a4f148084 --- /dev/null +++ b/drivers/mfd/rts5209.c @@ -0,0 +1,223 @@ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + * Author: + * Wei WANG + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + */ + +#include +#include +#include + +#include "rtsx_pcr.h" + +static u8 rts5209_get_ic_version(struct rtsx_pcr *pcr) +{ + u8 val; + + val = rtsx_pci_readb(pcr, 0x1C); + return val & 0x0F; +} + +static void rts5209_init_vendor_cfg(struct rtsx_pcr *pcr) +{ + u32 val; + + rtsx_pci_read_config_dword(pcr, 0x724, &val); + dev_dbg(&(pcr->pci->dev), "Cfg 0x724: 0x%x\n", val); + + if (!(val & 0x80)) { + if (val & 0x08) + pcr->ms_pmos = false; + else + pcr->ms_pmos = true; + } +} + +static int rts5209_extra_init_hw(struct rtsx_pcr *pcr) +{ + rtsx_pci_init_cmd(pcr); + + /* Turn off LED */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_GPIO, 0xFF, 0x03); + /* Configure GPIO as output */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_GPIO_DIR, 0xFF, 0x03); + + return rtsx_pci_send_cmd(pcr, 100); +} + +static int rts5209_optimize_phy(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_phy_register(pcr, 0x00, 0xB966); +} + +static int rts5209_turn_on_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x00); +} + +static int rts5209_turn_off_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x01); +} + +static int rts5209_enable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0xFF, 0x0D); +} + +static int rts5209_disable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0x08, 0x00); +} + +static int rts5209_card_power_on(struct rtsx_pcr *pcr, int card) +{ + int err; + u8 pwr_mask, partial_pwr_on, pwr_on; + + pwr_mask = SD_POWER_MASK; + partial_pwr_on = SD_PARTIAL_POWER_ON; + pwr_on = SD_POWER_ON; + + if (pcr->ms_pmos && (card == RTSX_MS_CARD)) { + pwr_mask = MS_POWER_MASK; + partial_pwr_on = MS_PARTIAL_POWER_ON; + pwr_on = MS_POWER_ON; + } + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + pwr_mask, partial_pwr_on); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0x04); + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + /* To avoid too large in-rush current */ + udelay(150); + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, pwr_mask, pwr_on); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0x00); + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + return 0; +} + +static int rts5209_card_power_off(struct rtsx_pcr *pcr, int card) +{ + u8 pwr_mask, pwr_off; + + pwr_mask = SD_POWER_MASK; + pwr_off = SD_POWER_OFF; + + if (pcr->ms_pmos && (card == RTSX_MS_CARD)) { + pwr_mask = MS_POWER_MASK; + pwr_off = MS_POWER_OFF; + } + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + pwr_mask | PMOS_STRG_MASK, pwr_off | PMOS_STRG_400mA); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0X06); + return rtsx_pci_send_cmd(pcr, 100); +} + +static const struct pcr_ops rts5209_pcr_ops = { + .extra_init_hw = rts5209_extra_init_hw, + .optimize_phy = rts5209_optimize_phy, + .turn_on_led = rts5209_turn_on_led, + .turn_off_led = rts5209_turn_off_led, + .enable_auto_blink = rts5209_enable_auto_blink, + .disable_auto_blink = rts5209_disable_auto_blink, + .card_power_on = rts5209_card_power_on, + .card_power_off = rts5209_card_power_off, + .cd_deglitch = NULL, +}; + +/* SD Pull Control Enable: + * SD_DAT[3:0] ==> pull up + * SD_CD ==> pull up + * SD_WP ==> pull up + * SD_CMD ==> pull up + * SD_CLK ==> pull down + */ +static const u32 rts5209_sd_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL1, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9), + 0, +}; + +/* SD Pull Control Disable: + * SD_DAT[3:0] ==> pull down + * SD_CD ==> pull up + * SD_WP ==> pull down + * SD_CMD ==> pull down + * SD_CLK ==> pull down + */ +static const u32 rts5209_sd_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL1, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5), + 0, +}; + +/* MS Pull Control Enable: + * MS CD ==> pull up + * others ==> pull down + */ +static const u32 rts5209_ms_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), + 0, +}; + +/* MS Pull Control Disable: + * MS CD ==> pull up + * others ==> pull down + */ +static const u32 rts5209_ms_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), + 0, +}; + +void rts5209_init_params(struct rtsx_pcr *pcr) +{ + pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | + EXTRA_CAPS_SD_SDR104 | EXTRA_CAPS_MMC_8BIT; + pcr->num_slots = 2; + pcr->ops = &rts5209_pcr_ops; + + rts5209_init_vendor_cfg(pcr); + + pcr->ic_version = rts5209_get_ic_version(pcr); + pcr->sd_pull_ctl_enable_tbl = rts5209_sd_pull_ctl_enable_tbl; + pcr->sd_pull_ctl_disable_tbl = rts5209_sd_pull_ctl_disable_tbl; + pcr->ms_pull_ctl_enable_tbl = rts5209_ms_pull_ctl_enable_tbl; + pcr->ms_pull_ctl_disable_tbl = rts5209_ms_pull_ctl_disable_tbl; +} diff --git a/drivers/mfd/rts5229.c b/drivers/mfd/rts5229.c new file mode 100644 index 000000000000..b9dbab266fda --- /dev/null +++ b/drivers/mfd/rts5229.c @@ -0,0 +1,205 @@ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + * Author: + * Wei WANG + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + */ + +#include +#include +#include + +#include "rtsx_pcr.h" + +static u8 rts5229_get_ic_version(struct rtsx_pcr *pcr) +{ + u8 val; + + rtsx_pci_read_register(pcr, DUMMY_REG_RESET_0, &val); + return val & 0x0F; +} + +static int rts5229_extra_init_hw(struct rtsx_pcr *pcr) +{ + rtsx_pci_init_cmd(pcr); + + /* Configure GPIO as output */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02); + /* Switch LDO3318 source from DV33 to card_3v3 */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x00); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01); + /* LED shine disabled, set initial shine cycle period */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02); + + return rtsx_pci_send_cmd(pcr, 100); +} + +static int rts5229_optimize_phy(struct rtsx_pcr *pcr) +{ + /* Optimize RX sensitivity */ + return rtsx_pci_write_phy_register(pcr, 0x00, 0xBA42); +} + +static int rts5229_turn_on_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02); +} + +static int rts5229_turn_off_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00); +} + +static int rts5229_enable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08); +} + +static int rts5229_disable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00); +} + +static int rts5229_card_power_on(struct rtsx_pcr *pcr, int card) +{ + int err; + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + SD_POWER_MASK, SD_PARTIAL_POWER_ON); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0x02); + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + /* To avoid too large in-rush current */ + udelay(150); + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + SD_POWER_MASK, SD_POWER_ON); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0x06); + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + return 0; +} + +static int rts5229_card_power_off(struct rtsx_pcr *pcr, int card) +{ + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + SD_POWER_MASK | PMOS_STRG_MASK, + SD_POWER_OFF | PMOS_STRG_400mA); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0X00); + return rtsx_pci_send_cmd(pcr, 100); +} + +static const struct pcr_ops rts5229_pcr_ops = { + .extra_init_hw = rts5229_extra_init_hw, + .optimize_phy = rts5229_optimize_phy, + .turn_on_led = rts5229_turn_on_led, + .turn_off_led = rts5229_turn_off_led, + .enable_auto_blink = rts5229_enable_auto_blink, + .disable_auto_blink = rts5229_disable_auto_blink, + .card_power_on = rts5229_card_power_on, + .card_power_off = rts5229_card_power_off, + .cd_deglitch = NULL, +}; + +/* SD Pull Control Enable: + * SD_DAT[3:0] ==> pull up + * SD_CD ==> pull up + * SD_WP ==> pull up + * SD_CMD ==> pull up + * SD_CLK ==> pull down + */ +static const u32 rts5229_sd_pull_ctl_enable_tbl1[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9), + 0, +}; + +/* For RTS5229 version C */ +static const u32 rts5229_sd_pull_ctl_enable_tbl2[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD9), + 0, +}; + +/* SD Pull Control Disable: + * SD_DAT[3:0] ==> pull down + * SD_CD ==> pull up + * SD_WP ==> pull down + * SD_CMD ==> pull down + * SD_CLK ==> pull down + */ +static const u32 rts5229_sd_pull_ctl_disable_tbl1[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5), + 0, +}; + +/* For RTS5229 version C */ +static const u32 rts5229_sd_pull_ctl_disable_tbl2[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE5), + 0, +}; + +/* MS Pull Control Enable: + * MS CD ==> pull up + * others ==> pull down + */ +static const u32 rts5229_ms_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), + 0, +}; + +/* MS Pull Control Disable: + * MS CD ==> pull up + * others ==> pull down + */ +static const u32 rts5229_ms_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), + 0, +}; + +void rts5229_init_params(struct rtsx_pcr *pcr) +{ + pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; + pcr->num_slots = 2; + pcr->ops = &rts5229_pcr_ops; + + pcr->ic_version = rts5229_get_ic_version(pcr); + if (pcr->ic_version == IC_VER_C) { + pcr->sd_pull_ctl_enable_tbl = rts5229_sd_pull_ctl_enable_tbl2; + pcr->sd_pull_ctl_disable_tbl = rts5229_sd_pull_ctl_disable_tbl2; + } else { + pcr->sd_pull_ctl_enable_tbl = rts5229_sd_pull_ctl_enable_tbl1; + pcr->sd_pull_ctl_disable_tbl = rts5229_sd_pull_ctl_disable_tbl1; + } + pcr->ms_pull_ctl_enable_tbl = rts5229_ms_pull_ctl_enable_tbl; + pcr->ms_pull_ctl_disable_tbl = rts5229_ms_pull_ctl_disable_tbl; +} diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c new file mode 100644 index 000000000000..56d4377c62c2 --- /dev/null +++ b/drivers/mfd/rtsx_pcr.c @@ -0,0 +1,1251 @@ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + * Author: + * Wei WANG + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rtsx_pcr.h" + +static bool msi_en = true; +module_param(msi_en, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(msi_en, "Enable MSI"); + +static DEFINE_IDR(rtsx_pci_idr); +static DEFINE_SPINLOCK(rtsx_pci_lock); + +static struct mfd_cell rtsx_pcr_cells[] = { + [RTSX_SD_CARD] = { + .name = DRV_NAME_RTSX_PCI_SDMMC, + }, + [RTSX_MS_CARD] = { + .name = DRV_NAME_RTSX_PCI_MS, + }, +}; + +static DEFINE_PCI_DEVICE_TABLE(rtsx_pci_ids) = { + { PCI_DEVICE(0x10EC, 0x5209), PCI_CLASS_OTHERS << 16, 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5229), PCI_CLASS_OTHERS << 16, 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5289), PCI_CLASS_OTHERS << 16, 0xFF0000 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, rtsx_pci_ids); + +void rtsx_pci_start_run(struct rtsx_pcr *pcr) +{ + /* If pci device removed, don't queue idle work any more */ + if (pcr->remove_pci) + return; + + if (pcr->state != PDEV_STAT_RUN) { + pcr->state = PDEV_STAT_RUN; + if (pcr->ops->enable_auto_blink) + pcr->ops->enable_auto_blink(pcr); + } + + mod_delayed_work(system_wq, &pcr->idle_work, msecs_to_jiffies(200)); +} +EXPORT_SYMBOL_GPL(rtsx_pci_start_run); + +int rtsx_pci_write_register(struct rtsx_pcr *pcr, u16 addr, u8 mask, u8 data) +{ + int i; + u32 val = HAIMR_WRITE_START; + + val |= (u32)(addr & 0x3FFF) << 16; + val |= (u32)mask << 8; + val |= (u32)data; + + rtsx_pci_writel(pcr, RTSX_HAIMR, val); + + for (i = 0; i < MAX_RW_REG_CNT; i++) { + val = rtsx_pci_readl(pcr, RTSX_HAIMR); + if ((val & HAIMR_TRANS_END) == 0) { + if (data != (u8)val) + return -EIO; + return 0; + } + } + + return -ETIMEDOUT; +} +EXPORT_SYMBOL_GPL(rtsx_pci_write_register); + +int rtsx_pci_read_register(struct rtsx_pcr *pcr, u16 addr, u8 *data) +{ + u32 val = HAIMR_READ_START; + int i; + + val |= (u32)(addr & 0x3FFF) << 16; + rtsx_pci_writel(pcr, RTSX_HAIMR, val); + + for (i = 0; i < MAX_RW_REG_CNT; i++) { + val = rtsx_pci_readl(pcr, RTSX_HAIMR); + if ((val & HAIMR_TRANS_END) == 0) + break; + } + + if (i >= MAX_RW_REG_CNT) + return -ETIMEDOUT; + + if (data) + *data = (u8)(val & 0xFF); + + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_pci_read_register); + +int rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val) +{ + int err, i, finished = 0; + u8 tmp; + + rtsx_pci_init_cmd(pcr); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYDATA0, 0xFF, (u8)val); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYDATA1, 0xFF, (u8)(val >> 8)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYADDR, 0xFF, addr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYRWCTL, 0xFF, 0x81); + + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + for (i = 0; i < 100000; i++) { + err = rtsx_pci_read_register(pcr, PHYRWCTL, &tmp); + if (err < 0) + return err; + + if (!(tmp & 0x80)) { + finished = 1; + break; + } + } + + if (!finished) + return -ETIMEDOUT; + + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_pci_write_phy_register); + +int rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val) +{ + int err, i, finished = 0; + u16 data; + u8 *ptr, tmp; + + rtsx_pci_init_cmd(pcr); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYADDR, 0xFF, addr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYRWCTL, 0xFF, 0x80); + + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + for (i = 0; i < 100000; i++) { + err = rtsx_pci_read_register(pcr, PHYRWCTL, &tmp); + if (err < 0) + return err; + + if (!(tmp & 0x80)) { + finished = 1; + break; + } + } + + if (!finished) + return -ETIMEDOUT; + + rtsx_pci_init_cmd(pcr); + + rtsx_pci_add_cmd(pcr, READ_REG_CMD, PHYDATA0, 0, 0); + rtsx_pci_add_cmd(pcr, READ_REG_CMD, PHYDATA1, 0, 0); + + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + ptr = rtsx_pci_get_cmd_data(pcr); + data = ((u16)ptr[1] << 8) | ptr[0]; + + if (val) + *val = data; + + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_pci_read_phy_register); + +void rtsx_pci_stop_cmd(struct rtsx_pcr *pcr) +{ + rtsx_pci_writel(pcr, RTSX_HCBCTLR, STOP_CMD); + rtsx_pci_writel(pcr, RTSX_HDBCTLR, STOP_DMA); + + rtsx_pci_write_register(pcr, DMACTL, 0x80, 0x80); + rtsx_pci_write_register(pcr, RBCTL, 0x80, 0x80); +} +EXPORT_SYMBOL_GPL(rtsx_pci_stop_cmd); + +void rtsx_pci_add_cmd(struct rtsx_pcr *pcr, + u8 cmd_type, u16 reg_addr, u8 mask, u8 data) +{ + unsigned long flags; + u32 val = 0; + u32 *ptr = (u32 *)(pcr->host_cmds_ptr); + + val |= (u32)(cmd_type & 0x03) << 30; + val |= (u32)(reg_addr & 0x3FFF) << 16; + val |= (u32)mask << 8; + val |= (u32)data; + + spin_lock_irqsave(&pcr->lock, flags); + ptr += pcr->ci; + if (pcr->ci < (HOST_CMDS_BUF_LEN / 4)) { + put_unaligned_le32(val, ptr); + ptr++; + pcr->ci++; + } + spin_unlock_irqrestore(&pcr->lock, flags); +} +EXPORT_SYMBOL_GPL(rtsx_pci_add_cmd); + +void rtsx_pci_send_cmd_no_wait(struct rtsx_pcr *pcr) +{ + u32 val = 1 << 31; + + rtsx_pci_writel(pcr, RTSX_HCBAR, pcr->host_cmds_addr); + + val |= (u32)(pcr->ci * 4) & 0x00FFFFFF; + /* Hardware Auto Response */ + val |= 0x40000000; + rtsx_pci_writel(pcr, RTSX_HCBCTLR, val); +} +EXPORT_SYMBOL_GPL(rtsx_pci_send_cmd_no_wait); + +int rtsx_pci_send_cmd(struct rtsx_pcr *pcr, int timeout) +{ + struct completion trans_done; + u32 val = 1 << 31; + long timeleft; + unsigned long flags; + int err = 0; + + spin_lock_irqsave(&pcr->lock, flags); + + /* set up data structures for the wakeup system */ + pcr->done = &trans_done; + pcr->trans_result = TRANS_NOT_READY; + init_completion(&trans_done); + + rtsx_pci_writel(pcr, RTSX_HCBAR, pcr->host_cmds_addr); + + val |= (u32)(pcr->ci * 4) & 0x00FFFFFF; + /* Hardware Auto Response */ + val |= 0x40000000; + rtsx_pci_writel(pcr, RTSX_HCBCTLR, val); + + spin_unlock_irqrestore(&pcr->lock, flags); + + /* Wait for TRANS_OK_INT */ + timeleft = wait_for_completion_interruptible_timeout( + &trans_done, msecs_to_jiffies(timeout)); + if (timeleft <= 0) { + dev_dbg(&(pcr->pci->dev), "Timeout (%s %d)\n", + __func__, __LINE__); + err = -ETIMEDOUT; + goto finish_send_cmd; + } + + spin_lock_irqsave(&pcr->lock, flags); + if (pcr->trans_result == TRANS_RESULT_FAIL) + err = -EINVAL; + else if (pcr->trans_result == TRANS_RESULT_OK) + err = 0; + else if (pcr->trans_result == TRANS_NO_DEVICE) + err = -ENODEV; + spin_unlock_irqrestore(&pcr->lock, flags); + +finish_send_cmd: + spin_lock_irqsave(&pcr->lock, flags); + pcr->done = NULL; + spin_unlock_irqrestore(&pcr->lock, flags); + + if ((err < 0) && (err != -ENODEV)) + rtsx_pci_stop_cmd(pcr); + + if (pcr->finish_me) + complete(pcr->finish_me); + + return err; +} +EXPORT_SYMBOL_GPL(rtsx_pci_send_cmd); + +static void rtsx_pci_add_sg_tbl(struct rtsx_pcr *pcr, + dma_addr_t addr, unsigned int len, int end) +{ + u64 *ptr = (u64 *)(pcr->host_sg_tbl_ptr) + pcr->sgi; + u64 val; + u8 option = SG_VALID | SG_TRANS_DATA; + + dev_dbg(&(pcr->pci->dev), "DMA addr: 0x%x, Len: 0x%x\n", + (unsigned int)addr, len); + + if (end) + option |= SG_END; + val = ((u64)addr << 32) | ((u64)len << 12) | option; + + put_unaligned_le64(val, ptr); + ptr++; + pcr->sgi++; +} + +int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist, + int num_sg, bool read, int timeout) +{ + struct completion trans_done; + u8 dir; + int err = 0, i, count; + long timeleft; + unsigned long flags; + struct scatterlist *sg; + enum dma_data_direction dma_dir; + u32 val; + dma_addr_t addr; + unsigned int len; + + dev_dbg(&(pcr->pci->dev), "--> %s: num_sg = %d\n", __func__, num_sg); + + /* don't transfer data during abort processing */ + if (pcr->remove_pci) + return -EINVAL; + + if ((sglist == NULL) || (num_sg <= 0)) + return -EINVAL; + + if (read) { + dir = DEVICE_TO_HOST; + dma_dir = DMA_FROM_DEVICE; + } else { + dir = HOST_TO_DEVICE; + dma_dir = DMA_TO_DEVICE; + } + + count = dma_map_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir); + if (count < 1) { + dev_err(&(pcr->pci->dev), "scatterlist map failed\n"); + return -EINVAL; + } + dev_dbg(&(pcr->pci->dev), "DMA mapping count: %d\n", count); + + val = ((u32)(dir & 0x01) << 29) | TRIG_DMA | ADMA_MODE; + pcr->sgi = 0; + for_each_sg(sglist, sg, count, i) { + addr = sg_dma_address(sg); + len = sg_dma_len(sg); + rtsx_pci_add_sg_tbl(pcr, addr, len, i == count - 1); + } + + spin_lock_irqsave(&pcr->lock, flags); + + pcr->done = &trans_done; + pcr->trans_result = TRANS_NOT_READY; + init_completion(&trans_done); + rtsx_pci_writel(pcr, RTSX_HDBAR, pcr->host_sg_tbl_addr); + rtsx_pci_writel(pcr, RTSX_HDBCTLR, val); + + spin_unlock_irqrestore(&pcr->lock, flags); + + timeleft = wait_for_completion_interruptible_timeout( + &trans_done, msecs_to_jiffies(timeout)); + if (timeleft <= 0) { + dev_dbg(&(pcr->pci->dev), "Timeout (%s %d)\n", + __func__, __LINE__); + err = -ETIMEDOUT; + goto out; + } + + spin_lock_irqsave(&pcr->lock, flags); + + if (pcr->trans_result == TRANS_RESULT_FAIL) + err = -EINVAL; + else if (pcr->trans_result == TRANS_NO_DEVICE) + err = -ENODEV; + + spin_unlock_irqrestore(&pcr->lock, flags); + +out: + spin_lock_irqsave(&pcr->lock, flags); + pcr->done = NULL; + spin_unlock_irqrestore(&pcr->lock, flags); + + dma_unmap_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir); + + if ((err < 0) && (err != -ENODEV)) + rtsx_pci_stop_cmd(pcr); + + if (pcr->finish_me) + complete(pcr->finish_me); + + return err; +} +EXPORT_SYMBOL_GPL(rtsx_pci_transfer_data); + +int rtsx_pci_read_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len) +{ + int err; + int i, j; + u16 reg; + u8 *ptr; + + if (buf_len > 512) + buf_len = 512; + + ptr = buf; + reg = PPBUF_BASE2; + for (i = 0; i < buf_len / 256; i++) { + rtsx_pci_init_cmd(pcr); + + for (j = 0; j < 256; j++) + rtsx_pci_add_cmd(pcr, READ_REG_CMD, reg++, 0, 0); + + err = rtsx_pci_send_cmd(pcr, 250); + if (err < 0) + return err; + + memcpy(ptr, rtsx_pci_get_cmd_data(pcr), 256); + ptr += 256; + } + + if (buf_len % 256) { + rtsx_pci_init_cmd(pcr); + + for (j = 0; j < buf_len % 256; j++) + rtsx_pci_add_cmd(pcr, READ_REG_CMD, reg++, 0, 0); + + err = rtsx_pci_send_cmd(pcr, 250); + if (err < 0) + return err; + } + + memcpy(ptr, rtsx_pci_get_cmd_data(pcr), buf_len % 256); + + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_pci_read_ppbuf); + +int rtsx_pci_write_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len) +{ + int err; + int i, j; + u16 reg; + u8 *ptr; + + if (buf_len > 512) + buf_len = 512; + + ptr = buf; + reg = PPBUF_BASE2; + for (i = 0; i < buf_len / 256; i++) { + rtsx_pci_init_cmd(pcr); + + for (j = 0; j < 256; j++) { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + reg++, 0xFF, *ptr); + ptr++; + } + + err = rtsx_pci_send_cmd(pcr, 250); + if (err < 0) + return err; + } + + if (buf_len % 256) { + rtsx_pci_init_cmd(pcr); + + for (j = 0; j < buf_len % 256; j++) { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + reg++, 0xFF, *ptr); + ptr++; + } + + err = rtsx_pci_send_cmd(pcr, 250); + if (err < 0) + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_pci_write_ppbuf); + +static int rtsx_pci_set_pull_ctl(struct rtsx_pcr *pcr, const u32 *tbl) +{ + int err; + + rtsx_pci_init_cmd(pcr); + + while (*tbl & 0xFFFF0000) { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + (u16)(*tbl >> 16), 0xFF, (u8)(*tbl)); + tbl++; + } + + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + return 0; +} + +int rtsx_pci_card_pull_ctl_enable(struct rtsx_pcr *pcr, int card) +{ + const u32 *tbl; + + if (card == RTSX_SD_CARD) + tbl = pcr->sd_pull_ctl_enable_tbl; + else if (card == RTSX_MS_CARD) + tbl = pcr->ms_pull_ctl_enable_tbl; + else + return -EINVAL; + + return rtsx_pci_set_pull_ctl(pcr, tbl); +} +EXPORT_SYMBOL_GPL(rtsx_pci_card_pull_ctl_enable); + +int rtsx_pci_card_pull_ctl_disable(struct rtsx_pcr *pcr, int card) +{ + const u32 *tbl; + + if (card == RTSX_SD_CARD) + tbl = pcr->sd_pull_ctl_disable_tbl; + else if (card == RTSX_MS_CARD) + tbl = pcr->ms_pull_ctl_disable_tbl; + else + return -EINVAL; + + + return rtsx_pci_set_pull_ctl(pcr, tbl); +} +EXPORT_SYMBOL_GPL(rtsx_pci_card_pull_ctl_disable); + +static void rtsx_pci_enable_bus_int(struct rtsx_pcr *pcr) +{ + pcr->bier = TRANS_OK_INT_EN | TRANS_FAIL_INT_EN | SD_INT_EN; + + if (pcr->num_slots > 1) + pcr->bier |= MS_INT_EN; + + /* Enable Bus Interrupt */ + rtsx_pci_writel(pcr, RTSX_BIER, pcr->bier); + + dev_dbg(&(pcr->pci->dev), "RTSX_BIER: 0x%08x\n", pcr->bier); +} + +static inline u8 double_ssc_depth(u8 depth) +{ + return ((depth > 1) ? (depth - 1) : depth); +} + +static u8 revise_ssc_depth(u8 ssc_depth, u8 div) +{ + if (div > CLK_DIV_1) { + if (ssc_depth > (div - 1)) + ssc_depth -= (div - 1); + else + ssc_depth = SSC_DEPTH_4M; + } + + return ssc_depth; +} + +int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, + u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk) +{ + int err, clk; + u8 N, min_N, max_N, clk_divider; + u8 mcu_cnt, div, max_div; + u8 depth[] = { + [RTSX_SSC_DEPTH_4M] = SSC_DEPTH_4M, + [RTSX_SSC_DEPTH_2M] = SSC_DEPTH_2M, + [RTSX_SSC_DEPTH_1M] = SSC_DEPTH_1M, + [RTSX_SSC_DEPTH_500K] = SSC_DEPTH_500K, + [RTSX_SSC_DEPTH_250K] = SSC_DEPTH_250K, + }; + + if (initial_mode) { + /* We use 250k(around) here, in initial stage */ + clk_divider = SD_CLK_DIVIDE_128; + card_clock = 30000000; + } else { + clk_divider = SD_CLK_DIVIDE_0; + } + err = rtsx_pci_write_register(pcr, SD_CFG1, + SD_CLK_DIVIDE_MASK, clk_divider); + if (err < 0) + return err; + + card_clock /= 1000000; + dev_dbg(&(pcr->pci->dev), "Switch card clock to %dMHz\n", card_clock); + + min_N = 80; + max_N = 208; + max_div = CLK_DIV_8; + + clk = card_clock; + if (!initial_mode && double_clk) + clk = card_clock * 2; + dev_dbg(&(pcr->pci->dev), + "Internal SSC clock: %dMHz (cur_clock = %d)\n", + clk, pcr->cur_clock); + + if (clk == pcr->cur_clock) + return 0; + + N = (u8)(clk - 2); + if ((clk <= 2) || (N > max_N)) + return -EINVAL; + + mcu_cnt = (u8)(125/clk + 3); + if (mcu_cnt > 15) + mcu_cnt = 15; + + /* Make sure that the SSC clock div_n is equal or greater than min_N */ + div = CLK_DIV_1; + while ((N < min_N) && (div < max_div)) { + N = (N + 2) * 2 - 2; + div++; + } + dev_dbg(&(pcr->pci->dev), "N = %d, div = %d\n", N, div); + + ssc_depth = depth[ssc_depth]; + if (double_clk) + ssc_depth = double_ssc_depth(ssc_depth); + + ssc_depth = revise_ssc_depth(ssc_depth, div); + dev_dbg(&(pcr->pci->dev), "ssc_depth = %d\n", ssc_depth); + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, + CLK_LOW_FREQ, CLK_LOW_FREQ); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_DIV, + 0xFF, (div << 4) | mcu_cnt); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, + SSC_DEPTH_MASK, ssc_depth); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, N); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB); + if (vpclk) { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, + PHASE_NOT_RESET, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, + PHASE_NOT_RESET, PHASE_NOT_RESET); + } + + err = rtsx_pci_send_cmd(pcr, 2000); + if (err < 0) + return err; + + /* Wait SSC clock stable */ + udelay(10); + err = rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, 0); + if (err < 0) + return err; + + pcr->cur_clock = clk; + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_pci_switch_clock); + +int rtsx_pci_card_power_on(struct rtsx_pcr *pcr, int card) +{ + if (pcr->ops->card_power_on) + return pcr->ops->card_power_on(pcr, card); + + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_pci_card_power_on); + +int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card) +{ + if (pcr->ops->card_power_off) + return pcr->ops->card_power_off(pcr, card); + + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_pci_card_power_off); + +unsigned int rtsx_pci_card_exist(struct rtsx_pcr *pcr) +{ + unsigned int val; + + val = rtsx_pci_readl(pcr, RTSX_BIPR); + if (pcr->ops->cd_deglitch) + val = pcr->ops->cd_deglitch(pcr); + + return val; +} +EXPORT_SYMBOL_GPL(rtsx_pci_card_exist); + +void rtsx_pci_complete_unfinished_transfer(struct rtsx_pcr *pcr) +{ + struct completion finish; + + pcr->finish_me = &finish; + init_completion(&finish); + + if (pcr->done) + complete(pcr->done); + + if (!pcr->remove_pci) + rtsx_pci_stop_cmd(pcr); + + wait_for_completion_interruptible_timeout(&finish, + msecs_to_jiffies(2)); + pcr->finish_me = NULL; +} +EXPORT_SYMBOL_GPL(rtsx_pci_complete_unfinished_transfer); + +static void rtsx_pci_card_detect(struct work_struct *work) +{ + struct delayed_work *dwork; + struct rtsx_pcr *pcr; + unsigned long flags; + unsigned int card_detect = 0; + u32 irq_status; + + dwork = to_delayed_work(work); + pcr = container_of(dwork, struct rtsx_pcr, carddet_work); + + dev_dbg(&(pcr->pci->dev), "--> %s\n", __func__); + + spin_lock_irqsave(&pcr->lock, flags); + + irq_status = rtsx_pci_readl(pcr, RTSX_BIPR); + dev_dbg(&(pcr->pci->dev), "irq_status: 0x%08x\n", irq_status); + + if (pcr->card_inserted || pcr->card_removed) { + dev_dbg(&(pcr->pci->dev), + "card_inserted: 0x%x, card_removed: 0x%x\n", + pcr->card_inserted, pcr->card_removed); + + if (pcr->ops->cd_deglitch) + pcr->card_inserted = pcr->ops->cd_deglitch(pcr); + + card_detect = pcr->card_inserted | pcr->card_removed; + pcr->card_inserted = 0; + pcr->card_removed = 0; + } + + spin_unlock_irqrestore(&pcr->lock, flags); + + if (card_detect & SD_EXIST) + pcr->slots[RTSX_SD_CARD].card_event( + pcr->slots[RTSX_SD_CARD].p_dev); + if (card_detect & MS_EXIST) + pcr->slots[RTSX_MS_CARD].card_event( + pcr->slots[RTSX_MS_CARD].p_dev); +} + +static irqreturn_t rtsx_pci_isr(int irq, void *dev_id) +{ + struct rtsx_pcr *pcr = dev_id; + u32 int_reg; + + if (!pcr) + return IRQ_NONE; + + spin_lock(&pcr->lock); + + int_reg = rtsx_pci_readl(pcr, RTSX_BIPR); + /* Clear interrupt flag */ + rtsx_pci_writel(pcr, RTSX_BIPR, int_reg); + if ((int_reg & pcr->bier) == 0) { + spin_unlock(&pcr->lock); + return IRQ_NONE; + } + if (int_reg == 0xFFFFFFFF) { + spin_unlock(&pcr->lock); + return IRQ_HANDLED; + } + + int_reg &= (pcr->bier | 0x7FFFFF); + + if (int_reg & SD_INT) { + if (int_reg & SD_EXIST) { + pcr->card_inserted |= SD_EXIST; + } else { + pcr->card_removed |= SD_EXIST; + pcr->card_inserted &= ~SD_EXIST; + } + } + + if (int_reg & MS_INT) { + if (int_reg & MS_EXIST) { + pcr->card_inserted |= MS_EXIST; + } else { + pcr->card_removed |= MS_EXIST; + pcr->card_inserted &= ~MS_EXIST; + } + } + + if (pcr->card_inserted || pcr->card_removed) + schedule_delayed_work(&pcr->carddet_work, + msecs_to_jiffies(200)); + + if (int_reg & (NEED_COMPLETE_INT | DELINK_INT)) { + if (int_reg & (TRANS_FAIL_INT | DELINK_INT)) { + pcr->trans_result = TRANS_RESULT_FAIL; + if (pcr->done) + complete(pcr->done); + } else if (int_reg & TRANS_OK_INT) { + pcr->trans_result = TRANS_RESULT_OK; + if (pcr->done) + complete(pcr->done); + } + } + + spin_unlock(&pcr->lock); + return IRQ_HANDLED; +} + +static int rtsx_pci_acquire_irq(struct rtsx_pcr *pcr) +{ + dev_info(&(pcr->pci->dev), "%s: pcr->msi_en = %d, pci->irq = %d\n", + __func__, pcr->msi_en, pcr->pci->irq); + + if (request_irq(pcr->pci->irq, rtsx_pci_isr, + pcr->msi_en ? 0 : IRQF_SHARED, + DRV_NAME_RTSX_PCI, pcr)) { + dev_err(&(pcr->pci->dev), + "rtsx_sdmmc: unable to grab IRQ %d, disabling device\n", + pcr->pci->irq); + return -1; + } + + pcr->irq = pcr->pci->irq; + pci_intx(pcr->pci, !pcr->msi_en); + + return 0; +} + +static void rtsx_pci_idle_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct rtsx_pcr *pcr = container_of(dwork, struct rtsx_pcr, idle_work); + + dev_dbg(&(pcr->pci->dev), "--> %s\n", __func__); + + mutex_lock(&pcr->pcr_mutex); + + pcr->state = PDEV_STAT_IDLE; + + if (pcr->ops->disable_auto_blink) + pcr->ops->disable_auto_blink(pcr); + if (pcr->ops->turn_off_led) + pcr->ops->turn_off_led(pcr); + + mutex_unlock(&pcr->pcr_mutex); +} + +static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) +{ + int err; + + rtsx_pci_writel(pcr, RTSX_HCBAR, pcr->host_cmds_addr); + + rtsx_pci_enable_bus_int(pcr); + + /* Power on SSC */ + err = rtsx_pci_write_register(pcr, FPDCTL, SSC_POWER_DOWN, 0); + if (err < 0) + return err; + + /* Wait SSC power stable */ + udelay(200); + + if (pcr->ops->optimize_phy) { + err = pcr->ops->optimize_phy(pcr); + if (err < 0) + return err; + } + + rtsx_pci_init_cmd(pcr); + + /* Set mcu_cnt to 7 to ensure data can be sampled properly */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_DIV, 0x07, 0x07); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, HOST_SLEEP_STATE, 0x03, 0x00); + /* Disable card clock */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, 0x1E, 0); + /* Reset ASPM state to default value */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, ASPM_FORCE_CTL, 0x3F, 0); + /* Reset delink mode */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CHANGE_LINK_STATE, 0x0A, 0); + /* Card driving select */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DRIVE_SEL, + 0x07, DRIVER_TYPE_D); + /* Enable SSC Clock */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, + 0xFF, SSC_8X_EN | SSC_SEL_4M); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, 0x12); + /* Disable cd_pwr_save */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CHANGE_LINK_STATE, 0x16, 0x10); + /* Clear Link Ready Interrupt */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0, + LINK_RDY_INT, LINK_RDY_INT); + /* Enlarge the estimation window of PERST# glitch + * to reduce the chance of invalid card interrupt + */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PERST_GLITCH_WIDTH, 0xFF, 0x80); + /* Update RC oscillator to 400k + * bit[0] F_HIGH: for RC oscillator, Rst_value is 1'b1 + * 1: 2M 0: 400k + */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RCCTL, 0x01, 0x00); + /* Set interrupt write clear + * bit 1: U_elbi_if_rd_clr_en + * 1: Enable ELBI interrupt[31:22] & [7:0] flag read clear + * 0: ELBI interrupt flag[31:22] & [7:0] only can be write clear + */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, NFTS_TX_CTRL, 0x02, 0); + /* Force CLKREQ# PIN to drive 0 to request clock */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0x08, 0x08); + + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + /* Enable clk_request_n to enable clock power management */ + rtsx_pci_write_config_byte(pcr, 0x81, 1); + /* Enter L1 when host tx idle */ + rtsx_pci_write_config_byte(pcr, 0x70F, 0x5B); + + if (pcr->ops->extra_init_hw) { + err = pcr->ops->extra_init_hw(pcr); + if (err < 0) + return err; + } + + return 0; +} + +static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) +{ + int err; + + spin_lock_init(&pcr->lock); + mutex_init(&pcr->pcr_mutex); + + switch (PCI_PID(pcr)) { + default: + case 0x5209: + rts5209_init_params(pcr); + break; + + case 0x5229: + rts5229_init_params(pcr); + break; + + case 0x5289: + rtl8411_init_params(pcr); + break; + } + + dev_dbg(&(pcr->pci->dev), "PID: 0x%04x, IC version: 0x%02x\n", + PCI_PID(pcr), pcr->ic_version); + + pcr->slots = kcalloc(pcr->num_slots, sizeof(struct rtsx_slot), + GFP_KERNEL); + if (!pcr->slots) + return -ENOMEM; + + pcr->state = PDEV_STAT_IDLE; + err = rtsx_pci_init_hw(pcr); + if (err < 0) { + kfree(pcr->slots); + return err; + } + + return 0; +} + +static int __devinit rtsx_pci_probe(struct pci_dev *pcidev, + const struct pci_device_id *id) +{ + struct rtsx_pcr *pcr; + struct pcr_handle *handle; + u32 base, len; + int ret, i; + + dev_dbg(&(pcidev->dev), + ": Realtek PCI-E Card Reader found at %s [%04x:%04x] (rev %x)\n", + pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device, + (int)pcidev->revision); + + ret = pci_enable_device(pcidev); + if (ret) + return ret; + + ret = pci_request_regions(pcidev, DRV_NAME_RTSX_PCI); + if (ret) + goto disable; + + pcr = kzalloc(sizeof(*pcr), GFP_KERNEL); + if (!pcr) { + ret = -ENOMEM; + goto release_pci; + } + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) { + ret = -ENOMEM; + goto free_pcr; + } + handle->pcr = pcr; + + if (!idr_pre_get(&rtsx_pci_idr, GFP_KERNEL)) { + ret = -ENOMEM; + goto free_handle; + } + + spin_lock(&rtsx_pci_lock); + ret = idr_get_new(&rtsx_pci_idr, pcr, &pcr->id); + spin_unlock(&rtsx_pci_lock); + if (ret) + goto free_handle; + + pcr->pci = pcidev; + dev_set_drvdata(&pcidev->dev, handle); + + len = pci_resource_len(pcidev, 0); + base = pci_resource_start(pcidev, 0); + pcr->remap_addr = ioremap_nocache(base, len); + if (!pcr->remap_addr) { + ret = -ENOMEM; + goto free_host; + } + + pcr->rtsx_resv_buf = dma_alloc_coherent(&(pcidev->dev), + RTSX_RESV_BUF_LEN, &(pcr->rtsx_resv_buf_addr), + GFP_KERNEL); + if (pcr->rtsx_resv_buf == NULL) { + ret = -ENXIO; + goto unmap; + } + pcr->host_cmds_ptr = pcr->rtsx_resv_buf; + pcr->host_cmds_addr = pcr->rtsx_resv_buf_addr; + pcr->host_sg_tbl_ptr = pcr->rtsx_resv_buf + HOST_CMDS_BUF_LEN; + pcr->host_sg_tbl_addr = pcr->rtsx_resv_buf_addr + HOST_CMDS_BUF_LEN; + + pcr->card_inserted = 0; + pcr->card_removed = 0; + INIT_DELAYED_WORK(&pcr->carddet_work, rtsx_pci_card_detect); + INIT_DELAYED_WORK(&pcr->idle_work, rtsx_pci_idle_work); + + pcr->msi_en = msi_en; + if (pcr->msi_en) { + ret = pci_enable_msi(pcidev); + if (ret < 0) + pcr->msi_en = false; + } + + ret = rtsx_pci_acquire_irq(pcr); + if (ret < 0) + goto free_dma; + + pci_set_master(pcidev); + synchronize_irq(pcr->irq); + + ret = rtsx_pci_init_chip(pcr); + if (ret < 0) + goto disable_irq; + + for (i = 0; i < ARRAY_SIZE(rtsx_pcr_cells); i++) { + rtsx_pcr_cells[i].platform_data = handle; + rtsx_pcr_cells[i].pdata_size = sizeof(*handle); + } + ret = mfd_add_devices(&pcidev->dev, pcr->id, rtsx_pcr_cells, + ARRAY_SIZE(rtsx_pcr_cells), NULL, 0, NULL); + if (ret < 0) + goto disable_irq; + + schedule_delayed_work(&pcr->idle_work, msecs_to_jiffies(200)); + + return 0; + +disable_irq: + free_irq(pcr->irq, (void *)pcr); +free_dma: + dma_free_coherent(&(pcr->pci->dev), RTSX_RESV_BUF_LEN, + pcr->rtsx_resv_buf, pcr->rtsx_resv_buf_addr); +unmap: + iounmap(pcr->remap_addr); +free_host: + dev_set_drvdata(&pcidev->dev, NULL); +free_handle: + kfree(handle); +free_pcr: + kfree(pcr); +release_pci: + pci_release_regions(pcidev); +disable: + pci_disable_device(pcidev); + + return ret; +} + +static void __devexit rtsx_pci_remove(struct pci_dev *pcidev) +{ + struct pcr_handle *handle = pci_get_drvdata(pcidev); + struct rtsx_pcr *pcr = handle->pcr; + + pcr->remove_pci = true; + + cancel_delayed_work(&pcr->carddet_work); + cancel_delayed_work(&pcr->idle_work); + + mfd_remove_devices(&pcidev->dev); + + dma_free_coherent(&(pcr->pci->dev), RTSX_RESV_BUF_LEN, + pcr->rtsx_resv_buf, pcr->rtsx_resv_buf_addr); + free_irq(pcr->irq, (void *)pcr); + if (pcr->msi_en) + pci_disable_msi(pcr->pci); + iounmap(pcr->remap_addr); + + dev_set_drvdata(&pcidev->dev, NULL); + pci_release_regions(pcidev); + pci_disable_device(pcidev); + + spin_lock(&rtsx_pci_lock); + idr_remove(&rtsx_pci_idr, pcr->id); + spin_unlock(&rtsx_pci_lock); + + kfree(pcr->slots); + kfree(pcr); + kfree(handle); + + dev_dbg(&(pcidev->dev), + ": Realtek PCI-E Card Reader at %s [%04x:%04x] has been removed\n", + pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device); +} + +#ifdef CONFIG_PM + +static int rtsx_pci_suspend(struct pci_dev *pcidev, pm_message_t state) +{ + struct pcr_handle *handle; + struct rtsx_pcr *pcr; + int ret = 0; + + dev_dbg(&(pcidev->dev), "--> %s\n", __func__); + + handle = pci_get_drvdata(pcidev); + pcr = handle->pcr; + + cancel_delayed_work(&pcr->carddet_work); + cancel_delayed_work(&pcr->idle_work); + + mutex_lock(&pcr->pcr_mutex); + + if (pcr->ops->turn_off_led) + pcr->ops->turn_off_led(pcr); + + rtsx_pci_writel(pcr, RTSX_BIER, 0); + pcr->bier = 0; + + rtsx_pci_write_register(pcr, PETXCFG, 0x08, 0x08); + rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, 0x02); + + pci_save_state(pcidev); + pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0); + pci_disable_device(pcidev); + pci_set_power_state(pcidev, pci_choose_state(pcidev, state)); + + mutex_unlock(&pcr->pcr_mutex); + return ret; +} + +static int rtsx_pci_resume(struct pci_dev *pcidev) +{ + struct pcr_handle *handle; + struct rtsx_pcr *pcr; + int ret = 0; + + dev_dbg(&(pcidev->dev), "--> %s\n", __func__); + + handle = pci_get_drvdata(pcidev); + pcr = handle->pcr; + + mutex_lock(&pcr->pcr_mutex); + + pci_set_power_state(pcidev, PCI_D0); + pci_restore_state(pcidev); + ret = pci_enable_device(pcidev); + if (ret) + goto out; + pci_set_master(pcidev); + + ret = rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, 0x00); + if (ret) + goto out; + + ret = rtsx_pci_init_hw(pcr); + if (ret) + goto out; + + schedule_delayed_work(&pcr->idle_work, msecs_to_jiffies(200)); + +out: + mutex_unlock(&pcr->pcr_mutex); + return ret; +} + +#else /* CONFIG_PM */ + +#define rtsx_pci_suspend NULL +#define rtsx_pci_resume NULL + +#endif /* CONFIG_PM */ + +static struct pci_driver rtsx_pci_driver = { + .name = DRV_NAME_RTSX_PCI, + .id_table = rtsx_pci_ids, + .probe = rtsx_pci_probe, + .remove = __devexit_p(rtsx_pci_remove), + .suspend = rtsx_pci_suspend, + .resume = rtsx_pci_resume, +}; +module_pci_driver(rtsx_pci_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Wei WANG "); +MODULE_DESCRIPTION("Realtek PCI-E Card Reader Driver"); diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h new file mode 100644 index 000000000000..12462c1df1a9 --- /dev/null +++ b/drivers/mfd/rtsx_pcr.h @@ -0,0 +1,32 @@ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + * Author: + * Wei WANG + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + */ + +#ifndef __RTSX_PCR_H +#define __RTSX_PCR_H + +#include + +void rts5209_init_params(struct rtsx_pcr *pcr); +void rts5229_init_params(struct rtsx_pcr *pcr); +void rtl8411_init_params(struct rtsx_pcr *pcr); + +#endif diff --git a/include/linux/mfd/rtsx_common.h b/include/linux/mfd/rtsx_common.h new file mode 100644 index 000000000000..a8d393e3066b --- /dev/null +++ b/include/linux/mfd/rtsx_common.h @@ -0,0 +1,48 @@ +/* Driver for Realtek driver-based card reader + * + * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + * Author: + * Wei WANG + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + */ + +#ifndef __RTSX_COMMON_H +#define __RTSX_COMMON_H + +#define DRV_NAME_RTSX_PCI "rtsx_pci" +#define DRV_NAME_RTSX_PCI_SDMMC "rtsx_pci_sdmmc" +#define DRV_NAME_RTSX_PCI_MS "rtsx_pci_ms" + +#define RTSX_REG_PAIR(addr, val) (((u32)(addr) << 16) | (u8)(val)) + +#define RTSX_SSC_DEPTH_4M 0x01 +#define RTSX_SSC_DEPTH_2M 0x02 +#define RTSX_SSC_DEPTH_1M 0x03 +#define RTSX_SSC_DEPTH_500K 0x04 +#define RTSX_SSC_DEPTH_250K 0x05 + +#define RTSX_SD_CARD 0 +#define RTSX_MS_CARD 1 + +struct platform_device; + +struct rtsx_slot { + struct platform_device *p_dev; + void (*card_event)(struct platform_device *p_dev); +}; + +#endif diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h new file mode 100644 index 000000000000..060b721fcbfb --- /dev/null +++ b/include/linux/mfd/rtsx_pci.h @@ -0,0 +1,794 @@ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + * Author: + * Wei WANG + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + */ + +#ifndef __RTSX_PCI_H +#define __RTSX_PCI_H + +#include +#include + +#include "rtsx_common.h" + +#define MAX_RW_REG_CNT 1024 + +/* PCI Operation Register Address */ +#define RTSX_HCBAR 0x00 +#define RTSX_HCBCTLR 0x04 +#define RTSX_HDBAR 0x08 +#define RTSX_HDBCTLR 0x0C +#define RTSX_HAIMR 0x10 +#define RTSX_BIPR 0x14 +#define RTSX_BIER 0x18 + +/* Host command buffer control register */ +#define STOP_CMD (0x01 << 28) + +/* Host data buffer control register */ +#define SDMA_MODE 0x00 +#define ADMA_MODE (0x02 << 26) +#define STOP_DMA (0x01 << 28) +#define TRIG_DMA (0x01 << 31) + +/* Host access internal memory register */ +#define HAIMR_TRANS_START (0x01 << 31) +#define HAIMR_READ 0x00 +#define HAIMR_WRITE (0x01 << 30) +#define HAIMR_READ_START (HAIMR_TRANS_START | HAIMR_READ) +#define HAIMR_WRITE_START (HAIMR_TRANS_START | HAIMR_WRITE) +#define HAIMR_TRANS_END (HAIMR_TRANS_START) + +/* Bus interrupt pending register */ +#define CMD_DONE_INT (1 << 31) +#define DATA_DONE_INT (1 << 30) +#define TRANS_OK_INT (1 << 29) +#define TRANS_FAIL_INT (1 << 28) +#define XD_INT (1 << 27) +#define MS_INT (1 << 26) +#define SD_INT (1 << 25) +#define GPIO0_INT (1 << 24) +#define OC_INT (1 << 23) +#define SD_WRITE_PROTECT (1 << 19) +#define XD_EXIST (1 << 18) +#define MS_EXIST (1 << 17) +#define SD_EXIST (1 << 16) +#define DELINK_INT GPIO0_INT +#define MS_OC_INT (1 << 23) +#define SD_OC_INT (1 << 22) + +#define CARD_INT (XD_INT | MS_INT | SD_INT) +#define NEED_COMPLETE_INT (DATA_DONE_INT | TRANS_OK_INT | TRANS_FAIL_INT) +#define RTSX_INT (CMD_DONE_INT | NEED_COMPLETE_INT | \ + CARD_INT | GPIO0_INT | OC_INT) + +#define CARD_EXIST (XD_EXIST | MS_EXIST | SD_EXIST) + +/* Bus interrupt enable register */ +#define CMD_DONE_INT_EN (1 << 31) +#define DATA_DONE_INT_EN (1 << 30) +#define TRANS_OK_INT_EN (1 << 29) +#define TRANS_FAIL_INT_EN (1 << 28) +#define XD_INT_EN (1 << 27) +#define MS_INT_EN (1 << 26) +#define SD_INT_EN (1 << 25) +#define GPIO0_INT_EN (1 << 24) +#define OC_INT_EN (1 << 23) +#define DELINK_INT_EN GPIO0_INT_EN +#define MS_OC_INT_EN (1 << 23) +#define SD_OC_INT_EN (1 << 22) + +#define READ_REG_CMD 0 +#define WRITE_REG_CMD 1 +#define CHECK_REG_CMD 2 + +/* + * macros for easy use + */ +#define rtsx_pci_writel(pcr, reg, value) \ + iowrite32(value, (pcr)->remap_addr + reg) +#define rtsx_pci_readl(pcr, reg) \ + ioread32((pcr)->remap_addr + reg) +#define rtsx_pci_writew(pcr, reg, value) \ + iowrite16(value, (pcr)->remap_addr + reg) +#define rtsx_pci_readw(pcr, reg) \ + ioread16((pcr)->remap_addr + reg) +#define rtsx_pci_writeb(pcr, reg, value) \ + iowrite8(value, (pcr)->remap_addr + reg) +#define rtsx_pci_readb(pcr, reg) \ + ioread8((pcr)->remap_addr + reg) + +#define rtsx_pci_read_config_byte(pcr, where, val) \ + pci_read_config_byte((pcr)->pci, where, val) + +#define rtsx_pci_write_config_byte(pcr, where, val) \ + pci_write_config_byte((pcr)->pci, where, val) + +#define rtsx_pci_read_config_dword(pcr, where, val) \ + pci_read_config_dword((pcr)->pci, where, val) + +#define rtsx_pci_write_config_dword(pcr, where, val) \ + pci_write_config_dword((pcr)->pci, where, val) + +#define STATE_TRANS_NONE 0 +#define STATE_TRANS_CMD 1 +#define STATE_TRANS_BUF 2 +#define STATE_TRANS_SG 3 + +#define TRANS_NOT_READY 0 +#define TRANS_RESULT_OK 1 +#define TRANS_RESULT_FAIL 2 +#define TRANS_NO_DEVICE 3 + +#define RTSX_RESV_BUF_LEN 4096 +#define HOST_CMDS_BUF_LEN 1024 +#define HOST_SG_TBL_BUF_LEN (RTSX_RESV_BUF_LEN - HOST_CMDS_BUF_LEN) +#define HOST_SG_TBL_ITEMS (HOST_SG_TBL_BUF_LEN / 8) +#define MAX_SG_ITEM_LEN 0x80000 + +#define HOST_TO_DEVICE 0 +#define DEVICE_TO_HOST 1 + +#define MAX_PHASE 31 +#define RX_TUNING_CNT 3 + +/* SG descriptor */ +#define SG_INT 0x04 +#define SG_END 0x02 +#define SG_VALID 0x01 + +#define SG_NO_OP 0x00 +#define SG_TRANS_DATA (0x02 << 4) +#define SG_LINK_DESC (0x03 << 4) + +/* SD bank voltage */ +#define SD_IO_3V3 0 +#define SD_IO_1V8 1 + + +/* Card Clock Enable Register */ +#define SD_CLK_EN 0x04 +#define MS_CLK_EN 0x08 + +/* Card Select Register */ +#define SD_MOD_SEL 2 +#define MS_MOD_SEL 3 + +/* Card Output Enable Register */ +#define SD_OUTPUT_EN 0x04 +#define MS_OUTPUT_EN 0x08 + +/* CARD_SHARE_MODE */ +#define CARD_SHARE_MASK 0x0F +#define CARD_SHARE_MULTI_LUN 0x00 +#define CARD_SHARE_NORMAL 0x00 +#define CARD_SHARE_48_SD 0x04 +#define CARD_SHARE_48_MS 0x08 +/* CARD_SHARE_MODE for barossa */ +#define CARD_SHARE_BAROSSA_SD 0x01 +#define CARD_SHARE_BAROSSA_MS 0x02 + +/* SD30_DRIVE_SEL */ +#define DRIVER_TYPE_A 0x05 +#define DRIVER_TYPE_B 0x03 +#define DRIVER_TYPE_C 0x02 +#define DRIVER_TYPE_D 0x01 + +/* FPDCTL */ +#define SSC_POWER_DOWN 0x01 +#define SD_OC_POWER_DOWN 0x02 +#define ALL_POWER_DOWN 0x07 +#define OC_POWER_DOWN 0x06 + +/* CLK_CTL */ +#define CHANGE_CLK 0x01 + +/* LDO_CTL */ +#define BPP_LDO_POWB 0x03 +#define BPP_LDO_ON 0x00 +#define BPP_LDO_SUSPEND 0x02 +#define BPP_LDO_OFF 0x03 + +/* CD_PAD_CTL */ +#define CD_DISABLE_MASK 0x07 +#define MS_CD_DISABLE 0x04 +#define SD_CD_DISABLE 0x02 +#define XD_CD_DISABLE 0x01 +#define CD_DISABLE 0x07 +#define CD_ENABLE 0x00 +#define MS_CD_EN_ONLY 0x03 +#define SD_CD_EN_ONLY 0x05 +#define XD_CD_EN_ONLY 0x06 +#define FORCE_CD_LOW_MASK 0x38 +#define FORCE_CD_XD_LOW 0x08 +#define FORCE_CD_SD_LOW 0x10 +#define FORCE_CD_MS_LOW 0x20 +#define CD_AUTO_DISABLE 0x40 + +/* SD_STAT1 */ +#define SD_CRC7_ERR 0x80 +#define SD_CRC16_ERR 0x40 +#define SD_CRC_WRITE_ERR 0x20 +#define SD_CRC_WRITE_ERR_MASK 0x1C +#define GET_CRC_TIME_OUT 0x02 +#define SD_TUNING_COMPARE_ERR 0x01 + +/* SD_STAT2 */ +#define SD_RSP_80CLK_TIMEOUT 0x01 + +/* SD_BUS_STAT */ +#define SD_CLK_TOGGLE_EN 0x80 +#define SD_CLK_FORCE_STOP 0x40 +#define SD_DAT3_STATUS 0x10 +#define SD_DAT2_STATUS 0x08 +#define SD_DAT1_STATUS 0x04 +#define SD_DAT0_STATUS 0x02 +#define SD_CMD_STATUS 0x01 + +/* SD_PAD_CTL */ +#define SD_IO_USING_1V8 0x80 +#define SD_IO_USING_3V3 0x7F +#define TYPE_A_DRIVING 0x00 +#define TYPE_B_DRIVING 0x01 +#define TYPE_C_DRIVING 0x02 +#define TYPE_D_DRIVING 0x03 + +/* SD_SAMPLE_POINT_CTL */ +#define DDR_FIX_RX_DAT 0x00 +#define DDR_VAR_RX_DAT 0x80 +#define DDR_FIX_RX_DAT_EDGE 0x00 +#define DDR_FIX_RX_DAT_14_DELAY 0x40 +#define DDR_FIX_RX_CMD 0x00 +#define DDR_VAR_RX_CMD 0x20 +#define DDR_FIX_RX_CMD_POS_EDGE 0x00 +#define DDR_FIX_RX_CMD_14_DELAY 0x10 +#define SD20_RX_POS_EDGE 0x00 +#define SD20_RX_14_DELAY 0x08 +#define SD20_RX_SEL_MASK 0x08 + +/* SD_PUSH_POINT_CTL */ +#define DDR_FIX_TX_CMD_DAT 0x00 +#define DDR_VAR_TX_CMD_DAT 0x80 +#define DDR_FIX_TX_DAT_14_TSU 0x00 +#define DDR_FIX_TX_DAT_12_TSU 0x40 +#define DDR_FIX_TX_CMD_NEG_EDGE 0x00 +#define DDR_FIX_TX_CMD_14_AHEAD 0x20 +#define SD20_TX_NEG_EDGE 0x00 +#define SD20_TX_14_AHEAD 0x10 +#define SD20_TX_SEL_MASK 0x10 +#define DDR_VAR_SDCLK_POL_SWAP 0x01 + +/* SD_TRANSFER */ +#define SD_TRANSFER_START 0x80 +#define SD_TRANSFER_END 0x40 +#define SD_STAT_IDLE 0x20 +#define SD_TRANSFER_ERR 0x10 +/* SD Transfer Mode definition */ +#define SD_TM_NORMAL_WRITE 0x00 +#define SD_TM_AUTO_WRITE_3 0x01 +#define SD_TM_AUTO_WRITE_4 0x02 +#define SD_TM_AUTO_READ_3 0x05 +#define SD_TM_AUTO_READ_4 0x06 +#define SD_TM_CMD_RSP 0x08 +#define SD_TM_AUTO_WRITE_1 0x09 +#define SD_TM_AUTO_WRITE_2 0x0A +#define SD_TM_NORMAL_READ 0x0C +#define SD_TM_AUTO_READ_1 0x0D +#define SD_TM_AUTO_READ_2 0x0E +#define SD_TM_AUTO_TUNING 0x0F + +/* SD_VPTX_CTL / SD_VPRX_CTL */ +#define PHASE_CHANGE 0x80 +#define PHASE_NOT_RESET 0x40 + +/* SD_DCMPS_TX_CTL / SD_DCMPS_RX_CTL */ +#define DCMPS_CHANGE 0x80 +#define DCMPS_CHANGE_DONE 0x40 +#define DCMPS_ERROR 0x20 +#define DCMPS_CURRENT_PHASE 0x1F + +/* SD Configure 1 Register */ +#define SD_CLK_DIVIDE_0 0x00 +#define SD_CLK_DIVIDE_256 0xC0 +#define SD_CLK_DIVIDE_128 0x80 +#define SD_BUS_WIDTH_1BIT 0x00 +#define SD_BUS_WIDTH_4BIT 0x01 +#define SD_BUS_WIDTH_8BIT 0x02 +#define SD_ASYNC_FIFO_NOT_RST 0x10 +#define SD_20_MODE 0x00 +#define SD_DDR_MODE 0x04 +#define SD_30_MODE 0x08 + +#define SD_CLK_DIVIDE_MASK 0xC0 + +/* SD_CMD_STATE */ +#define SD_CMD_IDLE 0x80 + +/* SD_DATA_STATE */ +#define SD_DATA_IDLE 0x80 + +/* DCM_DRP_CTL */ +#define DCM_RESET 0x08 +#define DCM_LOCKED 0x04 +#define DCM_208M 0x00 +#define DCM_TX 0x01 +#define DCM_RX 0x02 + +/* DCM_DRP_TRIG */ +#define DRP_START 0x80 +#define DRP_DONE 0x40 + +/* DCM_DRP_CFG */ +#define DRP_WRITE 0x80 +#define DRP_READ 0x00 +#define DCM_WRITE_ADDRESS_50 0x50 +#define DCM_WRITE_ADDRESS_51 0x51 +#define DCM_READ_ADDRESS_00 0x00 +#define DCM_READ_ADDRESS_51 0x51 + +/* IRQSTAT0 */ +#define DMA_DONE_INT 0x80 +#define SUSPEND_INT 0x40 +#define LINK_RDY_INT 0x20 +#define LINK_DOWN_INT 0x10 + +/* DMACTL */ +#define DMA_RST 0x80 +#define DMA_BUSY 0x04 +#define DMA_DIR_TO_CARD 0x00 +#define DMA_DIR_FROM_CARD 0x02 +#define DMA_EN 0x01 +#define DMA_128 (0 << 4) +#define DMA_256 (1 << 4) +#define DMA_512 (2 << 4) +#define DMA_1024 (3 << 4) +#define DMA_PACK_SIZE_MASK 0x30 + +/* SSC_CTL1 */ +#define SSC_RSTB 0x80 +#define SSC_8X_EN 0x40 +#define SSC_FIX_FRAC 0x20 +#define SSC_SEL_1M 0x00 +#define SSC_SEL_2M 0x08 +#define SSC_SEL_4M 0x10 +#define SSC_SEL_8M 0x18 + +/* SSC_CTL2 */ +#define SSC_DEPTH_MASK 0x07 +#define SSC_DEPTH_DISALBE 0x00 +#define SSC_DEPTH_4M 0x01 +#define SSC_DEPTH_2M 0x02 +#define SSC_DEPTH_1M 0x03 +#define SSC_DEPTH_500K 0x04 +#define SSC_DEPTH_250K 0x05 + +/* System Clock Control Register */ +#define CLK_LOW_FREQ 0x01 + +/* System Clock Divider Register */ +#define CLK_DIV_1 0x01 +#define CLK_DIV_2 0x02 +#define CLK_DIV_4 0x03 +#define CLK_DIV_8 0x04 + +/* MS_CFG */ +#define SAMPLE_TIME_RISING 0x00 +#define SAMPLE_TIME_FALLING 0x80 +#define PUSH_TIME_DEFAULT 0x00 +#define PUSH_TIME_ODD 0x40 +#define NO_EXTEND_TOGGLE 0x00 +#define EXTEND_TOGGLE_CHK 0x20 +#define MS_BUS_WIDTH_1 0x00 +#define MS_BUS_WIDTH_4 0x10 +#define MS_BUS_WIDTH_8 0x18 +#define MS_2K_SECTOR_MODE 0x04 +#define MS_512_SECTOR_MODE 0x00 +#define MS_TOGGLE_TIMEOUT_EN 0x00 +#define MS_TOGGLE_TIMEOUT_DISEN 0x01 +#define MS_NO_CHECK_INT 0x02 + +/* MS_TRANS_CFG */ +#define WAIT_INT 0x80 +#define NO_WAIT_INT 0x00 +#define NO_AUTO_READ_INT_REG 0x00 +#define AUTO_READ_INT_REG 0x40 +#define MS_CRC16_ERR 0x20 +#define MS_RDY_TIMEOUT 0x10 +#define MS_INT_CMDNK 0x08 +#define MS_INT_BREQ 0x04 +#define MS_INT_ERR 0x02 +#define MS_INT_CED 0x01 + +/* MS_TRANSFER */ +#define MS_TRANSFER_START 0x80 +#define MS_TRANSFER_END 0x40 +#define MS_TRANSFER_ERR 0x20 +#define MS_BS_STATE 0x10 +#define MS_TM_READ_BYTES 0x00 +#define MS_TM_NORMAL_READ 0x01 +#define MS_TM_WRITE_BYTES 0x04 +#define MS_TM_NORMAL_WRITE 0x05 +#define MS_TM_AUTO_READ 0x08 +#define MS_TM_AUTO_WRITE 0x0C + +/* SD Configure 2 Register */ +#define SD_CALCULATE_CRC7 0x00 +#define SD_NO_CALCULATE_CRC7 0x80 +#define SD_CHECK_CRC16 0x00 +#define SD_NO_CHECK_CRC16 0x40 +#define SD_NO_CHECK_WAIT_CRC_TO 0x20 +#define SD_WAIT_BUSY_END 0x08 +#define SD_NO_WAIT_BUSY_END 0x00 +#define SD_CHECK_CRC7 0x00 +#define SD_NO_CHECK_CRC7 0x04 +#define SD_RSP_LEN_0 0x00 +#define SD_RSP_LEN_6 0x01 +#define SD_RSP_LEN_17 0x02 +/* SD/MMC Response Type Definition */ +#define SD_RSP_TYPE_R0 0x04 +#define SD_RSP_TYPE_R1 0x01 +#define SD_RSP_TYPE_R1b 0x09 +#define SD_RSP_TYPE_R2 0x02 +#define SD_RSP_TYPE_R3 0x05 +#define SD_RSP_TYPE_R4 0x05 +#define SD_RSP_TYPE_R5 0x01 +#define SD_RSP_TYPE_R6 0x01 +#define SD_RSP_TYPE_R7 0x01 + +/* SD_CONFIURE3 */ +#define SD_RSP_80CLK_TIMEOUT_EN 0x01 + +/* Card Transfer Reset Register */ +#define SPI_STOP 0x01 +#define XD_STOP 0x02 +#define SD_STOP 0x04 +#define MS_STOP 0x08 +#define SPI_CLR_ERR 0x10 +#define XD_CLR_ERR 0x20 +#define SD_CLR_ERR 0x40 +#define MS_CLR_ERR 0x80 + +/* Card Data Source Register */ +#define PINGPONG_BUFFER 0x01 +#define RING_BUFFER 0x00 + +/* Card Power Control Register */ +#define PMOS_STRG_MASK 0x10 +#define PMOS_STRG_800mA 0x10 +#define PMOS_STRG_400mA 0x00 +#define SD_POWER_OFF 0x03 +#define SD_PARTIAL_POWER_ON 0x01 +#define SD_POWER_ON 0x00 +#define SD_POWER_MASK 0x03 +#define MS_POWER_OFF 0x0C +#define MS_PARTIAL_POWER_ON 0x04 +#define MS_POWER_ON 0x00 +#define MS_POWER_MASK 0x0C +#define BPP_POWER_OFF 0x0F +#define BPP_POWER_5_PERCENT_ON 0x0E +#define BPP_POWER_10_PERCENT_ON 0x0C +#define BPP_POWER_15_PERCENT_ON 0x08 +#define BPP_POWER_ON 0x00 +#define BPP_POWER_MASK 0x0F + +/* PWR_GATE_CTRL */ +#define PWR_GATE_EN 0x01 +#define LDO3318_PWR_MASK 0x06 +#define LDO_ON 0x00 +#define LDO_SUSPEND 0x04 +#define LDO_OFF 0x06 + +/* CARD_CLK_SOURCE */ +#define CRC_FIX_CLK (0x00 << 0) +#define CRC_VAR_CLK0 (0x01 << 0) +#define CRC_VAR_CLK1 (0x02 << 0) +#define SD30_FIX_CLK (0x00 << 2) +#define SD30_VAR_CLK0 (0x01 << 2) +#define SD30_VAR_CLK1 (0x02 << 2) +#define SAMPLE_FIX_CLK (0x00 << 4) +#define SAMPLE_VAR_CLK0 (0x01 << 4) +#define SAMPLE_VAR_CLK1 (0x02 << 4) + +#define MS_CFG 0xFD40 +#define MS_TPC 0xFD41 +#define MS_TRANS_CFG 0xFD42 +#define MS_TRANSFER 0xFD43 +#define MS_INT_REG 0xFD44 +#define MS_BYTE_CNT 0xFD45 +#define MS_SECTOR_CNT_L 0xFD46 +#define MS_SECTOR_CNT_H 0xFD47 +#define MS_DBUS_H 0xFD48 + +#define SD_CFG1 0xFDA0 +#define SD_CFG2 0xFDA1 +#define SD_CFG3 0xFDA2 +#define SD_STAT1 0xFDA3 +#define SD_STAT2 0xFDA4 +#define SD_BUS_STAT 0xFDA5 +#define SD_PAD_CTL 0xFDA6 +#define SD_SAMPLE_POINT_CTL 0xFDA7 +#define SD_PUSH_POINT_CTL 0xFDA8 +#define SD_CMD0 0xFDA9 +#define SD_CMD1 0xFDAA +#define SD_CMD2 0xFDAB +#define SD_CMD3 0xFDAC +#define SD_CMD4 0xFDAD +#define SD_CMD5 0xFDAE +#define SD_BYTE_CNT_L 0xFDAF +#define SD_BYTE_CNT_H 0xFDB0 +#define SD_BLOCK_CNT_L 0xFDB1 +#define SD_BLOCK_CNT_H 0xFDB2 +#define SD_TRANSFER 0xFDB3 +#define SD_CMD_STATE 0xFDB5 +#define SD_DATA_STATE 0xFDB6 + +#define SRCTL 0xFC13 + +#define DCM_DRP_CTL 0xFC23 +#define DCM_DRP_TRIG 0xFC24 +#define DCM_DRP_CFG 0xFC25 +#define DCM_DRP_WR_DATA_L 0xFC26 +#define DCM_DRP_WR_DATA_H 0xFC27 +#define DCM_DRP_RD_DATA_L 0xFC28 +#define DCM_DRP_RD_DATA_H 0xFC29 +#define SD_VPCLK0_CTL 0xFC2A +#define SD_VPCLK1_CTL 0xFC2B +#define SD_DCMPS0_CTL 0xFC2C +#define SD_DCMPS1_CTL 0xFC2D +#define SD_VPTX_CTL SD_VPCLK0_CTL +#define SD_VPRX_CTL SD_VPCLK1_CTL +#define SD_DCMPS_TX_CTL SD_DCMPS0_CTL +#define SD_DCMPS_RX_CTL SD_DCMPS1_CTL +#define CARD_CLK_SOURCE 0xFC2E + +#define CARD_PWR_CTL 0xFD50 +#define CARD_CLK_SWITCH 0xFD51 +#define CARD_SHARE_MODE 0xFD52 +#define CARD_DRIVE_SEL 0xFD53 +#define CARD_STOP 0xFD54 +#define CARD_OE 0xFD55 +#define CARD_AUTO_BLINK 0xFD56 +#define CARD_GPIO_DIR 0xFD57 +#define CARD_GPIO 0xFD58 +#define CARD_DATA_SOURCE 0xFD5B +#define CARD_SELECT 0xFD5C +#define SD30_DRIVE_SEL 0xFD5E +#define CARD_CLK_EN 0xFD69 +#define SDIO_CTRL 0xFD6B +#define CD_PAD_CTL 0xFD73 + +#define FPDCTL 0xFC00 +#define PDINFO 0xFC01 + +#define CLK_CTL 0xFC02 +#define CLK_DIV 0xFC03 +#define CLK_SEL 0xFC04 + +#define SSC_DIV_N_0 0xFC0F +#define SSC_DIV_N_1 0xFC10 +#define SSC_CTL1 0xFC11 +#define SSC_CTL2 0xFC12 + +#define RCCTL 0xFC14 + +#define FPGA_PULL_CTL 0xFC1D +#define OLT_LED_CTL 0xFC1E +#define GPIO_CTL 0xFC1F + +#define LDO_CTL 0xFC1E +#define SYS_VER 0xFC32 + +#define CARD_PULL_CTL1 0xFD60 +#define CARD_PULL_CTL2 0xFD61 +#define CARD_PULL_CTL3 0xFD62 +#define CARD_PULL_CTL4 0xFD63 +#define CARD_PULL_CTL5 0xFD64 +#define CARD_PULL_CTL6 0xFD65 + +/* PCI Express Related Registers */ +#define IRQEN0 0xFE20 +#define IRQSTAT0 0xFE21 +#define IRQEN1 0xFE22 +#define IRQSTAT1 0xFE23 +#define TLPRIEN 0xFE24 +#define TLPRISTAT 0xFE25 +#define TLPTIEN 0xFE26 +#define TLPTISTAT 0xFE27 +#define DMATC0 0xFE28 +#define DMATC1 0xFE29 +#define DMATC2 0xFE2A +#define DMATC3 0xFE2B +#define DMACTL 0xFE2C +#define BCTL 0xFE2D +#define RBBC0 0xFE2E +#define RBBC1 0xFE2F +#define RBDAT 0xFE30 +#define RBCTL 0xFE34 +#define CFGADDR0 0xFE35 +#define CFGADDR1 0xFE36 +#define CFGDATA0 0xFE37 +#define CFGDATA1 0xFE38 +#define CFGDATA2 0xFE39 +#define CFGDATA3 0xFE3A +#define CFGRWCTL 0xFE3B +#define PHYRWCTL 0xFE3C +#define PHYDATA0 0xFE3D +#define PHYDATA1 0xFE3E +#define PHYADDR 0xFE3F +#define MSGRXDATA0 0xFE40 +#define MSGRXDATA1 0xFE41 +#define MSGRXDATA2 0xFE42 +#define MSGRXDATA3 0xFE43 +#define MSGTXDATA0 0xFE44 +#define MSGTXDATA1 0xFE45 +#define MSGTXDATA2 0xFE46 +#define MSGTXDATA3 0xFE47 +#define MSGTXCTL 0xFE48 +#define PETXCFG 0xFE49 + +#define CDRESUMECTL 0xFE52 +#define WAKE_SEL_CTL 0xFE54 +#define PME_FORCE_CTL 0xFE56 +#define ASPM_FORCE_CTL 0xFE57 +#define PM_CLK_FORCE_CTL 0xFE58 +#define PERST_GLITCH_WIDTH 0xFE5C +#define CHANGE_LINK_STATE 0xFE5B +#define RESET_LOAD_REG 0xFE5E +#define EFUSE_CONTENT 0xFE5F +#define HOST_SLEEP_STATE 0xFE60 +#define SDIO_CFG 0xFE70 + +#define NFTS_TX_CTRL 0xFE72 + +#define PWR_GATE_CTRL 0xFE75 +#define PWD_SUSPEND_EN 0xFE76 +#define LDO_PWR_SEL 0xFE78 + +#define DUMMY_REG_RESET_0 0xFE90 + +/* Memory mapping */ +#define SRAM_BASE 0xE600 +#define RBUF_BASE 0xF400 +#define PPBUF_BASE1 0xF800 +#define PPBUF_BASE2 0xFA00 +#define IMAGE_FLAG_ADDR0 0xCE80 +#define IMAGE_FLAG_ADDR1 0xCE81 + +#define rtsx_pci_init_cmd(pcr) ((pcr)->ci = 0) + +struct rtsx_pcr; + +struct pcr_handle { + struct rtsx_pcr *pcr; +}; + +struct pcr_ops { + int (*extra_init_hw)(struct rtsx_pcr *pcr); + int (*optimize_phy)(struct rtsx_pcr *pcr); + int (*turn_on_led)(struct rtsx_pcr *pcr); + int (*turn_off_led)(struct rtsx_pcr *pcr); + int (*enable_auto_blink)(struct rtsx_pcr *pcr); + int (*disable_auto_blink)(struct rtsx_pcr *pcr); + int (*card_power_on)(struct rtsx_pcr *pcr, int card); + int (*card_power_off)(struct rtsx_pcr *pcr, int card); + unsigned int (*cd_deglitch)(struct rtsx_pcr *pcr); +}; + +enum PDEV_STAT {PDEV_STAT_IDLE, PDEV_STAT_RUN}; + +struct rtsx_pcr { + struct pci_dev *pci; + unsigned int id; + + /* pci resources */ + unsigned long addr; + void __iomem *remap_addr; + int irq; + + /* host reserved buffer */ + void *rtsx_resv_buf; + dma_addr_t rtsx_resv_buf_addr; + + void *host_cmds_ptr; + dma_addr_t host_cmds_addr; + int ci; + + void *host_sg_tbl_ptr; + dma_addr_t host_sg_tbl_addr; + int sgi; + + u32 bier; + char trans_result; + + unsigned int card_inserted; + unsigned int card_removed; + + struct delayed_work carddet_work; + struct delayed_work idle_work; + + spinlock_t lock; + struct mutex pcr_mutex; + struct completion *done; + struct completion *finish_me; + + unsigned int cur_clock; + bool ms_pmos; + bool remove_pci; + bool msi_en; + +#define EXTRA_CAPS_SD_SDR50 (1 << 0) +#define EXTRA_CAPS_SD_SDR104 (1 << 1) +#define EXTRA_CAPS_SD_DDR50 (1 << 2) +#define EXTRA_CAPS_MMC_HSDDR (1 << 3) +#define EXTRA_CAPS_MMC_HS200 (1 << 4) +#define EXTRA_CAPS_MMC_8BIT (1 << 5) + u32 extra_caps; + +#define IC_VER_A 0 +#define IC_VER_B 1 +#define IC_VER_C 2 +#define IC_VER_D 3 + u8 ic_version; + + const u32 *sd_pull_ctl_enable_tbl; + const u32 *sd_pull_ctl_disable_tbl; + const u32 *ms_pull_ctl_enable_tbl; + const u32 *ms_pull_ctl_disable_tbl; + + const struct pcr_ops *ops; + enum PDEV_STAT state; + + int num_slots; + struct rtsx_slot *slots; +}; + +#define CHK_PCI_PID(pcr, pid) ((pcr)->pci->device == (pid)) +#define PCI_VID(pcr) ((pcr)->pci->vendor) +#define PCI_PID(pcr) ((pcr)->pci->device) + +void rtsx_pci_start_run(struct rtsx_pcr *pcr); +int rtsx_pci_write_register(struct rtsx_pcr *pcr, u16 addr, u8 mask, u8 data); +int rtsx_pci_read_register(struct rtsx_pcr *pcr, u16 addr, u8 *data); +int rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val); +int rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val); +void rtsx_pci_stop_cmd(struct rtsx_pcr *pcr); +void rtsx_pci_add_cmd(struct rtsx_pcr *pcr, + u8 cmd_type, u16 reg_addr, u8 mask, u8 data); +void rtsx_pci_send_cmd_no_wait(struct rtsx_pcr *pcr); +int rtsx_pci_send_cmd(struct rtsx_pcr *pcr, int timeout); +int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist, + int num_sg, bool read, int timeout); +int rtsx_pci_read_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len); +int rtsx_pci_write_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len); +int rtsx_pci_card_pull_ctl_enable(struct rtsx_pcr *pcr, int card); +int rtsx_pci_card_pull_ctl_disable(struct rtsx_pcr *pcr, int card); +int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, + u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk); +int rtsx_pci_card_power_on(struct rtsx_pcr *pcr, int card); +int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card); +unsigned int rtsx_pci_card_exist(struct rtsx_pcr *pcr); +void rtsx_pci_complete_unfinished_transfer(struct rtsx_pcr *pcr); + +static inline u8 *rtsx_pci_get_cmd_data(struct rtsx_pcr *pcr) +{ + return (u8 *)(pcr->host_cmds_ptr); +} + +#endif From ff984e57d36e8ac468849a144a36f1c11f88b61c Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Mon, 29 Oct 2012 13:49:38 +0800 Subject: [PATCH 012/119] mmc: Add realtek pcie sdmmc host driver Realtek PCI-E SD/MMC card host driver is used to access SD/MMC card, with the help of Realtek PCI-E card reader MFD driver. Signed-off-by: Wei WANG Reviewed-by: Arnd Bergmann Tested-by: Borislav Petkov Signed-off-by: Samuel Ortiz --- drivers/mmc/host/Kconfig | 7 + drivers/mmc/host/Makefile | 2 + drivers/mmc/host/rtsx_pci_sdmmc.c | 1348 +++++++++++++++++++++++++++++ 3 files changed, 1357 insertions(+) create mode 100644 drivers/mmc/host/rtsx_pci_sdmmc.c diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 9bf10e7bbfaf..dfa6d56ff0ff 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -621,3 +621,10 @@ config MMC_USHC Note: These controllers only support SDIO cards and do not support MMC or SD memory cards. + +config MMC_REALTEK_PCI + tristate "Realtek PCI-E SD/MMC Card Interface Driver" + depends on MFD_RTSX_PCI + help + Say Y here to include driver code to support SD/MMC card interface + of Realtek PCI-E card reader diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 17ad0a7ba40b..8aa592d14c4e 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -46,6 +46,8 @@ obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_VUB300) += vub300.o obj-$(CONFIG_MMC_USHC) += ushc.o +obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o + obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c new file mode 100644 index 000000000000..12eff6f8cab7 --- /dev/null +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -0,0 +1,1348 @@ +/* Realtek PCI-Express SD/MMC Card Interface driver + * + * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + * Author: + * Wei WANG + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SD Tuning Data Structure + * Record continuous timing phase path + */ +struct timing_phase_path { + int start; + int end; + int mid; + int len; +}; + +struct realtek_pci_sdmmc { + struct platform_device *pdev; + struct rtsx_pcr *pcr; + struct mmc_host *mmc; + struct mmc_request *mrq; + + struct mutex host_mutex; + + u8 ssc_depth; + unsigned int clock; + bool vpclk; + bool double_clk; + bool eject; + bool initial_mode; + bool ddr_mode; +}; + +static inline struct device *sdmmc_dev(struct realtek_pci_sdmmc *host) +{ + return &(host->pdev->dev); +} + +static inline void sd_clear_error(struct realtek_pci_sdmmc *host) +{ + rtsx_pci_write_register(host->pcr, CARD_STOP, + SD_STOP | SD_CLR_ERR, SD_STOP | SD_CLR_ERR); +} + +#ifdef DEBUG +static void sd_print_debug_regs(struct realtek_pci_sdmmc *host) +{ + struct rtsx_pcr *pcr = host->pcr; + u16 i; + u8 *ptr; + + /* Print SD host internal registers */ + rtsx_pci_init_cmd(pcr); + for (i = 0xFDA0; i <= 0xFDAE; i++) + rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0); + for (i = 0xFD52; i <= 0xFD69; i++) + rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0); + rtsx_pci_send_cmd(pcr, 100); + + ptr = rtsx_pci_get_cmd_data(pcr); + for (i = 0xFDA0; i <= 0xFDAE; i++) + dev_dbg(sdmmc_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); + for (i = 0xFD52; i <= 0xFD69; i++) + dev_dbg(sdmmc_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); +} +#else +#define sd_print_debug_regs(host) +#endif /* DEBUG */ + +static int sd_read_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt, + u8 *buf, int buf_len, int timeout) +{ + struct rtsx_pcr *pcr = host->pcr; + int err, i; + u8 trans_mode; + + dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD%d\n", __func__, cmd[0] - 0x40); + + if (!buf) + buf_len = 0; + + if ((cmd[0] & 0x3F) == MMC_SEND_TUNING_BLOCK) + trans_mode = SD_TM_AUTO_TUNING; + else + trans_mode = SD_TM_NORMAL_READ; + + rtsx_pci_init_cmd(pcr); + + for (i = 0; i < 5; i++) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0 + i, 0xFF, cmd[i]); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, + 0xFF, (u8)(byte_cnt >> 8)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, + SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | + SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6); + if (trans_mode != SD_TM_AUTO_TUNING) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, + 0xFF, trans_mode | SD_TRANSFER_START); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, + SD_TRANSFER_END, SD_TRANSFER_END); + + err = rtsx_pci_send_cmd(pcr, timeout); + if (err < 0) { + sd_print_debug_regs(host); + dev_dbg(sdmmc_dev(host), + "rtsx_pci_send_cmd fail (err = %d)\n", err); + return err; + } + + if (buf && buf_len) { + err = rtsx_pci_read_ppbuf(pcr, buf, buf_len); + if (err < 0) { + dev_dbg(sdmmc_dev(host), + "rtsx_pci_read_ppbuf fail (err = %d)\n", err); + return err; + } + } + + return 0; +} + +static int sd_write_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt, + u8 *buf, int buf_len, int timeout) +{ + struct rtsx_pcr *pcr = host->pcr; + int err, i; + u8 trans_mode; + + if (!buf) + buf_len = 0; + + if (buf && buf_len) { + err = rtsx_pci_write_ppbuf(pcr, buf, buf_len); + if (err < 0) { + dev_dbg(sdmmc_dev(host), + "rtsx_pci_write_ppbuf fail (err = %d)\n", err); + return err; + } + } + + trans_mode = cmd ? SD_TM_AUTO_WRITE_2 : SD_TM_AUTO_WRITE_3; + rtsx_pci_init_cmd(pcr); + + if (cmd) { + dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d\n", __func__, + cmd[0] - 0x40); + + for (i = 0; i < 5; i++) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD_CMD0 + i, 0xFF, cmd[i]); + } + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, + 0xFF, (u8)(byte_cnt >> 8)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, + SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | + SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF, + trans_mode | SD_TRANSFER_START); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, + SD_TRANSFER_END, SD_TRANSFER_END); + + err = rtsx_pci_send_cmd(pcr, timeout); + if (err < 0) { + sd_print_debug_regs(host); + dev_dbg(sdmmc_dev(host), + "rtsx_pci_send_cmd fail (err = %d)\n", err); + return err; + } + + return 0; +} + +static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, + struct mmc_command *cmd) +{ + struct rtsx_pcr *pcr = host->pcr; + u8 cmd_idx = (u8)cmd->opcode; + u32 arg = cmd->arg; + int err = 0; + int timeout = 100; + int i; + u8 *ptr; + int stat_idx = 0; + u8 rsp_type; + int rsp_len = 5; + + dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", + __func__, cmd_idx, arg); + + /* Response type: + * R0 + * R1, R5, R6, R7 + * R1b + * R2 + * R3, R4 + */ + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + rsp_type = SD_RSP_TYPE_R0; + rsp_len = 0; + break; + case MMC_RSP_R1: + rsp_type = SD_RSP_TYPE_R1; + break; + case MMC_RSP_R1B: + rsp_type = SD_RSP_TYPE_R1b; + break; + case MMC_RSP_R2: + rsp_type = SD_RSP_TYPE_R2; + rsp_len = 16; + break; + case MMC_RSP_R3: + rsp_type = SD_RSP_TYPE_R3; + break; + default: + dev_dbg(sdmmc_dev(host), "cmd->flag is not valid\n"); + err = -EINVAL; + goto out; + } + + if (rsp_type == SD_RSP_TYPE_R1b) + timeout = 3000; + + if (cmd->opcode == SD_SWITCH_VOLTAGE) { + err = rtsx_pci_write_register(pcr, SD_BUS_STAT, + 0xFF, SD_CLK_TOGGLE_EN); + if (err < 0) + goto out; + } + + rtsx_pci_init_cmd(pcr); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0, 0xFF, 0x40 | cmd_idx); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD1, 0xFF, (u8)(arg >> 24)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD2, 0xFF, (u8)(arg >> 16)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD3, 0xFF, (u8)(arg >> 8)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD4, 0xFF, (u8)arg); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, rsp_type); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, + 0x01, PINGPONG_BUFFER); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, + 0xFF, SD_TM_CMD_RSP | SD_TRANSFER_START); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, + SD_TRANSFER_END | SD_STAT_IDLE, + SD_TRANSFER_END | SD_STAT_IDLE); + + if (rsp_type == SD_RSP_TYPE_R2) { + /* Read data from ping-pong buffer */ + for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++) + rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0); + stat_idx = 16; + } else if (rsp_type != SD_RSP_TYPE_R0) { + /* Read data from SD_CMDx registers */ + for (i = SD_CMD0; i <= SD_CMD4; i++) + rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0); + stat_idx = 5; + } + + rtsx_pci_add_cmd(pcr, READ_REG_CMD, SD_STAT1, 0, 0); + + err = rtsx_pci_send_cmd(pcr, timeout); + if (err < 0) { + sd_print_debug_regs(host); + sd_clear_error(host); + dev_dbg(sdmmc_dev(host), + "rtsx_pci_send_cmd error (err = %d)\n", err); + goto out; + } + + if (rsp_type == SD_RSP_TYPE_R0) { + err = 0; + goto out; + } + + /* Eliminate returned value of CHECK_REG_CMD */ + ptr = rtsx_pci_get_cmd_data(pcr) + 1; + + /* Check (Start,Transmission) bit of Response */ + if ((ptr[0] & 0xC0) != 0) { + err = -EILSEQ; + dev_dbg(sdmmc_dev(host), "Invalid response bit\n"); + goto out; + } + + /* Check CRC7 */ + if (!(rsp_type & SD_NO_CHECK_CRC7)) { + if (ptr[stat_idx] & SD_CRC7_ERR) { + err = -EILSEQ; + dev_dbg(sdmmc_dev(host), "CRC7 error\n"); + goto out; + } + } + + if (rsp_type == SD_RSP_TYPE_R2) { + for (i = 0; i < 4; i++) { + cmd->resp[i] = get_unaligned_be32(ptr + 1 + i * 4); + dev_dbg(sdmmc_dev(host), "cmd->resp[%d] = 0x%08x\n", + i, cmd->resp[i]); + } + } else { + cmd->resp[0] = get_unaligned_be32(ptr + 1); + dev_dbg(sdmmc_dev(host), "cmd->resp[0] = 0x%08x\n", + cmd->resp[0]); + } + +out: + cmd->error = err; +} + +static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) +{ + struct rtsx_pcr *pcr = host->pcr; + struct mmc_host *mmc = host->mmc; + struct mmc_card *card = mmc->card; + struct mmc_data *data = mrq->data; + int uhs = mmc_sd_card_uhs(card); + int read = (data->flags & MMC_DATA_READ) ? 1 : 0; + u8 cfg2, trans_mode; + int err; + size_t data_len = data->blksz * data->blocks; + + if (read) { + cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | + SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0; + trans_mode = SD_TM_AUTO_READ_3; + } else { + cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 | + SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 | SD_RSP_LEN_0; + trans_mode = SD_TM_AUTO_WRITE_3; + } + + if (!uhs) + cfg2 |= SD_NO_CHECK_WAIT_CRC_TO; + + rtsx_pci_init_cmd(pcr); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, 0x00); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, 0x02); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, + 0xFF, (u8)data->blocks); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, + 0xFF, (u8)(data->blocks >> 8)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + CARD_DATA_SOURCE, 0x01, RING_BUFFER); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0, + DMA_DONE_INT, DMA_DONE_INT); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3, + 0xFF, (u8)(data_len >> 24)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2, + 0xFF, (u8)(data_len >> 16)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1, + 0xFF, (u8)(data_len >> 8)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)data_len); + if (read) { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, + 0x03 | DMA_PACK_SIZE_MASK, + DMA_DIR_FROM_CARD | DMA_EN | DMA_512); + } else { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, + 0x03 | DMA_PACK_SIZE_MASK, + DMA_DIR_TO_CARD | DMA_EN | DMA_512); + } + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, + 0x01, RING_BUFFER); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF, + trans_mode | SD_TRANSFER_START); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, + SD_TRANSFER_END, SD_TRANSFER_END); + + rtsx_pci_send_cmd_no_wait(pcr); + + err = rtsx_pci_transfer_data(pcr, data->sg, data->sg_len, read, 10000); + if (err < 0) { + sd_clear_error(host); + return err; + } + + return 0; +} + +static inline void sd_enable_initial_mode(struct realtek_pci_sdmmc *host) +{ + rtsx_pci_write_register(host->pcr, SD_CFG1, + SD_CLK_DIVIDE_MASK, SD_CLK_DIVIDE_128); +} + +static inline void sd_disable_initial_mode(struct realtek_pci_sdmmc *host) +{ + rtsx_pci_write_register(host->pcr, SD_CFG1, + SD_CLK_DIVIDE_MASK, SD_CLK_DIVIDE_0); +} + +static void sd_normal_rw(struct realtek_pci_sdmmc *host, + struct mmc_request *mrq) +{ + struct mmc_command *cmd = mrq->cmd; + struct mmc_data *data = mrq->data; + u8 _cmd[5], *buf; + + _cmd[0] = 0x40 | (u8)cmd->opcode; + put_unaligned_be32(cmd->arg, (u32 *)(&_cmd[1])); + + buf = kzalloc(data->blksz, GFP_NOIO); + if (!buf) { + cmd->error = -ENOMEM; + return; + } + + if (data->flags & MMC_DATA_READ) { + if (host->initial_mode) + sd_disable_initial_mode(host); + + cmd->error = sd_read_data(host, _cmd, (u16)data->blksz, buf, + data->blksz, 200); + + if (host->initial_mode) + sd_enable_initial_mode(host); + + sg_copy_from_buffer(data->sg, data->sg_len, buf, data->blksz); + } else { + sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz); + + cmd->error = sd_write_data(host, _cmd, (u16)data->blksz, buf, + data->blksz, 200); + } + + kfree(buf); +} + +static int sd_change_phase(struct realtek_pci_sdmmc *host, u8 sample_point) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + + dev_dbg(sdmmc_dev(host), "%s: sample_point = %d\n", + __func__, sample_point); + + rtsx_pci_init_cmd(pcr); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CHANGE_CLK, CHANGE_CLK); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPRX_CTL, 0x1F, sample_point); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, PHASE_NOT_RESET, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, + PHASE_NOT_RESET, PHASE_NOT_RESET); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CHANGE_CLK, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1, SD_ASYNC_FIFO_NOT_RST, 0); + + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + return 0; +} + +static u8 sd_search_final_phase(struct realtek_pci_sdmmc *host, u32 phase_map) +{ + struct timing_phase_path path[MAX_PHASE + 1]; + int i, j, cont_path_cnt; + int new_block, max_len, final_path_idx; + u8 final_phase = 0xFF; + + /* Parse phase_map, take it as a bit-ring */ + cont_path_cnt = 0; + new_block = 1; + j = 0; + for (i = 0; i < MAX_PHASE + 1; i++) { + if (phase_map & (1 << i)) { + if (new_block) { + new_block = 0; + j = cont_path_cnt++; + path[j].start = i; + path[j].end = i; + } else { + path[j].end = i; + } + } else { + new_block = 1; + if (cont_path_cnt) { + /* Calculate path length and middle point */ + int idx = cont_path_cnt - 1; + path[idx].len = + path[idx].end - path[idx].start + 1; + path[idx].mid = + path[idx].start + path[idx].len / 2; + } + } + } + + if (cont_path_cnt == 0) { + dev_dbg(sdmmc_dev(host), "No continuous phase path\n"); + goto finish; + } else { + /* Calculate last continuous path length and middle point */ + int idx = cont_path_cnt - 1; + path[idx].len = path[idx].end - path[idx].start + 1; + path[idx].mid = path[idx].start + path[idx].len / 2; + } + + /* Connect the first and last continuous paths if they are adjacent */ + if (!path[0].start && (path[cont_path_cnt - 1].end == MAX_PHASE)) { + /* Using negative index */ + path[0].start = path[cont_path_cnt - 1].start - MAX_PHASE - 1; + path[0].len += path[cont_path_cnt - 1].len; + path[0].mid = path[0].start + path[0].len / 2; + /* Convert negative middle point index to positive one */ + if (path[0].mid < 0) + path[0].mid += MAX_PHASE + 1; + cont_path_cnt--; + } + + /* Choose the longest continuous phase path */ + max_len = 0; + final_phase = 0; + final_path_idx = 0; + for (i = 0; i < cont_path_cnt; i++) { + if (path[i].len > max_len) { + max_len = path[i].len; + final_phase = (u8)path[i].mid; + final_path_idx = i; + } + + dev_dbg(sdmmc_dev(host), "path[%d].start = %d\n", + i, path[i].start); + dev_dbg(sdmmc_dev(host), "path[%d].end = %d\n", + i, path[i].end); + dev_dbg(sdmmc_dev(host), "path[%d].len = %d\n", + i, path[i].len); + dev_dbg(sdmmc_dev(host), "path[%d].mid = %d\n", + i, path[i].mid); + } + +finish: + dev_dbg(sdmmc_dev(host), "Final chosen phase: %d\n", final_phase); + return final_phase; +} + +static void sd_wait_data_idle(struct realtek_pci_sdmmc *host) +{ + int err, i; + u8 val = 0; + + for (i = 0; i < 100; i++) { + err = rtsx_pci_read_register(host->pcr, SD_DATA_STATE, &val); + if (val & SD_DATA_IDLE) + return; + + udelay(100); + } +} + +static int sd_tuning_rx_cmd(struct realtek_pci_sdmmc *host, + u8 opcode, u8 sample_point) +{ + int err; + u8 cmd[5] = {0}; + + err = sd_change_phase(host, sample_point); + if (err < 0) + return err; + + cmd[0] = 0x40 | opcode; + err = sd_read_data(host, cmd, 0x40, NULL, 0, 100); + if (err < 0) { + /* Wait till SD DATA IDLE */ + sd_wait_data_idle(host); + sd_clear_error(host); + return err; + } + + return 0; +} + +static int sd_tuning_phase(struct realtek_pci_sdmmc *host, + u8 opcode, u32 *phase_map) +{ + int err, i; + u32 raw_phase_map = 0; + + for (i = MAX_PHASE; i >= 0; i--) { + err = sd_tuning_rx_cmd(host, opcode, (u8)i); + if (err == 0) + raw_phase_map |= 1 << i; + } + + if (phase_map) + *phase_map = raw_phase_map; + + return 0; +} + +static int sd_tuning_rx(struct realtek_pci_sdmmc *host, u8 opcode) +{ + int err, i; + u32 raw_phase_map[RX_TUNING_CNT] = {0}, phase_map; + u8 final_phase; + + for (i = 0; i < RX_TUNING_CNT; i++) { + err = sd_tuning_phase(host, opcode, &(raw_phase_map[i])); + if (err < 0) + return err; + + if (raw_phase_map[i] == 0) + break; + } + + phase_map = 0xFFFFFFFF; + for (i = 0; i < RX_TUNING_CNT; i++) { + dev_dbg(sdmmc_dev(host), "RX raw_phase_map[%d] = 0x%08x\n", + i, raw_phase_map[i]); + phase_map &= raw_phase_map[i]; + } + dev_dbg(sdmmc_dev(host), "RX phase_map = 0x%08x\n", phase_map); + + if (phase_map) { + final_phase = sd_search_final_phase(host, phase_map); + if (final_phase == 0xFF) + return -EINVAL; + + err = sd_change_phase(host, final_phase); + if (err < 0) + return err; + } else { + return -EINVAL; + } + + return 0; +} + +static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct realtek_pci_sdmmc *host = mmc_priv(mmc); + struct rtsx_pcr *pcr = host->pcr; + struct mmc_command *cmd = mrq->cmd; + struct mmc_data *data = mrq->data; + unsigned int data_size = 0; + + if (host->eject) { + cmd->error = -ENOMEDIUM; + goto finish; + } + + mutex_lock(&pcr->pcr_mutex); + + rtsx_pci_start_run(pcr); + + rtsx_pci_switch_clock(pcr, host->clock, host->ssc_depth, + host->initial_mode, host->double_clk, host->vpclk); + rtsx_pci_write_register(pcr, CARD_SELECT, 0x07, SD_MOD_SEL); + rtsx_pci_write_register(pcr, CARD_SHARE_MODE, + CARD_SHARE_MASK, CARD_SHARE_48_SD); + + mutex_lock(&host->host_mutex); + host->mrq = mrq; + mutex_unlock(&host->host_mutex); + + if (mrq->data) + data_size = data->blocks * data->blksz; + + if (!data_size || mmc_op_multi(cmd->opcode) || + (cmd->opcode == MMC_READ_SINGLE_BLOCK) || + (cmd->opcode == MMC_WRITE_BLOCK)) { + sd_send_cmd_get_rsp(host, cmd); + + if (!cmd->error && data_size) { + sd_rw_multi(host, mrq); + + if (mmc_op_multi(cmd->opcode) && mrq->stop) + sd_send_cmd_get_rsp(host, mrq->stop); + } + } else { + sd_normal_rw(host, mrq); + } + + if (mrq->data) { + if (cmd->error || data->error) + data->bytes_xfered = 0; + else + data->bytes_xfered = data->blocks * data->blksz; + } + + mutex_unlock(&pcr->pcr_mutex); + +finish: + if (cmd->error) + dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", cmd->error); + + mutex_lock(&host->host_mutex); + host->mrq = NULL; + mutex_unlock(&host->host_mutex); + + mmc_request_done(mmc, mrq); +} + +static int sd_set_bus_width(struct realtek_pci_sdmmc *host, + unsigned char bus_width) +{ + int err = 0; + u8 width[] = { + [MMC_BUS_WIDTH_1] = SD_BUS_WIDTH_1BIT, + [MMC_BUS_WIDTH_4] = SD_BUS_WIDTH_4BIT, + [MMC_BUS_WIDTH_8] = SD_BUS_WIDTH_8BIT, + }; + + if (bus_width <= MMC_BUS_WIDTH_8) + err = rtsx_pci_write_register(host->pcr, SD_CFG1, + 0x03, width[bus_width]); + + return err; +} + +static int sd_power_on(struct realtek_pci_sdmmc *host) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE, + CARD_SHARE_MASK, CARD_SHARE_48_SD); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, + SD_CLK_EN, SD_CLK_EN); + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + err = rtsx_pci_card_pull_ctl_enable(pcr, RTSX_SD_CARD); + if (err < 0) + return err; + + err = rtsx_pci_card_power_on(pcr, RTSX_SD_CARD); + if (err < 0) + return err; + + err = rtsx_pci_write_register(pcr, CARD_OE, SD_OUTPUT_EN, SD_OUTPUT_EN); + if (err < 0) + return err; + + return 0; +} + +static int sd_power_off(struct realtek_pci_sdmmc *host) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + + rtsx_pci_init_cmd(pcr); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, SD_CLK_EN, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_OE, SD_OUTPUT_EN, 0); + + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + err = rtsx_pci_card_power_off(pcr, RTSX_SD_CARD); + if (err < 0) + return err; + + return rtsx_pci_card_pull_ctl_disable(pcr, RTSX_SD_CARD); +} + +static int sd_set_power_mode(struct realtek_pci_sdmmc *host, + unsigned char power_mode) +{ + int err; + + if (power_mode == MMC_POWER_OFF) + err = sd_power_off(host); + else + err = sd_power_on(host); + + return err; +} + +static int sd_set_timing(struct realtek_pci_sdmmc *host, + unsigned char timing, bool *ddr_mode) +{ + struct rtsx_pcr *pcr = host->pcr; + int err = 0; + + *ddr_mode = false; + + rtsx_pci_init_cmd(pcr); + + switch (timing) { + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_UHS_SDR50: + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1, + 0x0C | SD_ASYNC_FIFO_NOT_RST, + SD_30_MODE | SD_ASYNC_FIFO_NOT_RST); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, + CLK_LOW_FREQ, CLK_LOW_FREQ); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF, + CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0); + break; + + case MMC_TIMING_UHS_DDR50: + *ddr_mode = true; + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1, + 0x0C | SD_ASYNC_FIFO_NOT_RST, + SD_DDR_MODE | SD_ASYNC_FIFO_NOT_RST); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, + CLK_LOW_FREQ, CLK_LOW_FREQ); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF, + CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_PUSH_POINT_CTL, + DDR_VAR_TX_CMD_DAT, DDR_VAR_TX_CMD_DAT); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL, + DDR_VAR_RX_DAT | DDR_VAR_RX_CMD, + DDR_VAR_RX_DAT | DDR_VAR_RX_CMD); + break; + + case MMC_TIMING_MMC_HS: + case MMC_TIMING_SD_HS: + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1, + 0x0C, SD_20_MODE); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, + CLK_LOW_FREQ, CLK_LOW_FREQ); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF, + CRC_FIX_CLK | SD30_VAR_CLK0 | SAMPLE_VAR_CLK1); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_PUSH_POINT_CTL, + SD20_TX_SEL_MASK, SD20_TX_14_AHEAD); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL, + SD20_RX_SEL_MASK, SD20_RX_14_DELAY); + break; + + default: + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD_CFG1, 0x0C, SD_20_MODE); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, + CLK_LOW_FREQ, CLK_LOW_FREQ); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF, + CRC_FIX_CLK | SD30_VAR_CLK0 | SAMPLE_VAR_CLK1); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD_PUSH_POINT_CTL, 0xFF, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL, + SD20_RX_SEL_MASK, SD20_RX_POS_EDGE); + break; + } + + err = rtsx_pci_send_cmd(pcr, 100); + + return err; +} + +static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct realtek_pci_sdmmc *host = mmc_priv(mmc); + struct rtsx_pcr *pcr = host->pcr; + + if (host->eject) + return; + + mutex_lock(&pcr->pcr_mutex); + + rtsx_pci_start_run(pcr); + + sd_set_bus_width(host, ios->bus_width); + sd_set_power_mode(host, ios->power_mode); + sd_set_timing(host, ios->timing, &host->ddr_mode); + + host->vpclk = false; + host->double_clk = true; + + switch (ios->timing) { + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_UHS_SDR50: + host->ssc_depth = RTSX_SSC_DEPTH_2M; + host->vpclk = true; + host->double_clk = false; + break; + case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_UHS_SDR25: + host->ssc_depth = RTSX_SSC_DEPTH_1M; + break; + default: + host->ssc_depth = RTSX_SSC_DEPTH_500K; + break; + } + + host->initial_mode = (ios->clock <= 1000000) ? true : false; + + host->clock = ios->clock; + rtsx_pci_switch_clock(pcr, ios->clock, host->ssc_depth, + host->initial_mode, host->double_clk, host->vpclk); + + mutex_unlock(&pcr->pcr_mutex); +} + +static int sdmmc_get_ro(struct mmc_host *mmc) +{ + struct realtek_pci_sdmmc *host = mmc_priv(mmc); + struct rtsx_pcr *pcr = host->pcr; + int ro = 0; + u32 val; + + if (host->eject) + return -ENOMEDIUM; + + mutex_lock(&pcr->pcr_mutex); + + rtsx_pci_start_run(pcr); + + /* Check SD mechanical write-protect switch */ + val = rtsx_pci_readl(pcr, RTSX_BIPR); + dev_dbg(sdmmc_dev(host), "%s: RTSX_BIPR = 0x%08x\n", __func__, val); + if (val & SD_WRITE_PROTECT) + ro = 1; + + mutex_unlock(&pcr->pcr_mutex); + + return ro; +} + +static int sdmmc_get_cd(struct mmc_host *mmc) +{ + struct realtek_pci_sdmmc *host = mmc_priv(mmc); + struct rtsx_pcr *pcr = host->pcr; + int cd = 0; + u32 val; + + if (host->eject) + return -ENOMEDIUM; + + mutex_lock(&pcr->pcr_mutex); + + rtsx_pci_start_run(pcr); + + /* Check SD card detect */ + val = rtsx_pci_card_exist(pcr); + dev_dbg(sdmmc_dev(host), "%s: RTSX_BIPR = 0x%08x\n", __func__, val); + if (val & SD_EXIST) + cd = 1; + + mutex_unlock(&pcr->pcr_mutex); + + return cd; +} + +static int sd_wait_voltage_stable_1(struct realtek_pci_sdmmc *host) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + u8 stat; + + /* Reference to Signal Voltage Switch Sequence in SD spec. + * Wait for a period of time so that the card can drive SD_CMD and + * SD_DAT[3:0] to low after sending back CMD11 response. + */ + mdelay(1); + + /* SD_CMD, SD_DAT[3:0] should be driven to low by card; + * If either one of SD_CMD,SD_DAT[3:0] is not low, + * abort the voltage switch sequence; + */ + err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat); + if (err < 0) + return err; + + if (stat & (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | + SD_DAT1_STATUS | SD_DAT0_STATUS)) + return -EINVAL; + + /* Stop toggle SD clock */ + err = rtsx_pci_write_register(pcr, SD_BUS_STAT, + 0xFF, SD_CLK_FORCE_STOP); + if (err < 0) + return err; + + return 0; +} + +static int sd_wait_voltage_stable_2(struct realtek_pci_sdmmc *host) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + u8 stat, mask, val; + + /* Wait 1.8V output of voltage regulator in card stable */ + msleep(50); + + /* Toggle SD clock again */ + err = rtsx_pci_write_register(pcr, SD_BUS_STAT, 0xFF, SD_CLK_TOGGLE_EN); + if (err < 0) + return err; + + /* Wait for a period of time so that the card can drive + * SD_DAT[3:0] to high at 1.8V + */ + msleep(20); + + /* SD_CMD, SD_DAT[3:0] should be pulled high by host */ + err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat); + if (err < 0) + return err; + + mask = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | + SD_DAT1_STATUS | SD_DAT0_STATUS; + val = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | + SD_DAT1_STATUS | SD_DAT0_STATUS; + if ((stat & mask) != val) { + dev_dbg(sdmmc_dev(host), + "%s: SD_BUS_STAT = 0x%x\n", __func__, stat); + rtsx_pci_write_register(pcr, SD_BUS_STAT, + SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); + rtsx_pci_write_register(pcr, CARD_CLK_EN, 0xFF, 0); + return -EINVAL; + } + + return 0; +} + +static int sd_change_bank_voltage(struct realtek_pci_sdmmc *host, u8 voltage) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + + if (voltage == SD_IO_3V3) { + err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); + if (err < 0) + return err; + } else if (voltage == SD_IO_1V8) { + err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24); + if (err < 0) + return err; + } else { + return -EINVAL; + } + + return 0; +} + +static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct realtek_pci_sdmmc *host = mmc_priv(mmc); + struct rtsx_pcr *pcr = host->pcr; + int err = 0; + u8 voltage; + + dev_dbg(sdmmc_dev(host), "%s: signal_voltage = %d\n", + __func__, ios->signal_voltage); + + if (host->eject) + return -ENOMEDIUM; + + mutex_lock(&pcr->pcr_mutex); + + rtsx_pci_start_run(pcr); + + if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) + voltage = SD_IO_3V3; + else + voltage = SD_IO_1V8; + + if (voltage == SD_IO_1V8) { + err = rtsx_pci_write_register(pcr, + SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B); + if (err < 0) + goto out; + + err = sd_wait_voltage_stable_1(host); + if (err < 0) + goto out; + } + + err = sd_change_bank_voltage(host, voltage); + if (err < 0) + goto out; + + if (voltage == SD_IO_1V8) { + err = sd_wait_voltage_stable_2(host); + if (err < 0) + goto out; + } + + /* Stop toggle SD clock in idle */ + err = rtsx_pci_write_register(pcr, SD_BUS_STAT, + SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); + +out: + mutex_unlock(&pcr->pcr_mutex); + + return err; +} + +static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct realtek_pci_sdmmc *host = mmc_priv(mmc); + struct rtsx_pcr *pcr = host->pcr; + int err = 0; + + if (host->eject) + return -ENOMEDIUM; + + mutex_lock(&pcr->pcr_mutex); + + rtsx_pci_start_run(pcr); + + if (!host->ddr_mode) + err = sd_tuning_rx(host, MMC_SEND_TUNING_BLOCK); + + mutex_unlock(&pcr->pcr_mutex); + + return err; +} + +static const struct mmc_host_ops realtek_pci_sdmmc_ops = { + .request = sdmmc_request, + .set_ios = sdmmc_set_ios, + .get_ro = sdmmc_get_ro, + .get_cd = sdmmc_get_cd, + .start_signal_voltage_switch = sdmmc_switch_voltage, + .execute_tuning = sdmmc_execute_tuning, +}; + +#ifdef CONFIG_PM +static int rtsx_pci_sdmmc_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); + struct mmc_host *mmc = host->mmc; + int err; + + dev_dbg(sdmmc_dev(host), "--> %s\n", __func__); + + err = mmc_suspend_host(mmc); + if (err) + return err; + + return 0; +} + +static int rtsx_pci_sdmmc_resume(struct platform_device *pdev) +{ + struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); + struct mmc_host *mmc = host->mmc; + + dev_dbg(sdmmc_dev(host), "--> %s\n", __func__); + + return mmc_resume_host(mmc); +} +#else /* CONFIG_PM */ +#define rtsx_pci_sdmmc_suspend NULL +#define rtsx_pci_sdmmc_resume NULL +#endif /* CONFIG_PM */ + +static void init_extra_caps(struct realtek_pci_sdmmc *host) +{ + struct mmc_host *mmc = host->mmc; + struct rtsx_pcr *pcr = host->pcr; + + dev_dbg(sdmmc_dev(host), "pcr->extra_caps = 0x%x\n", pcr->extra_caps); + + if (pcr->extra_caps & EXTRA_CAPS_SD_SDR50) + mmc->caps |= MMC_CAP_UHS_SDR50; + if (pcr->extra_caps & EXTRA_CAPS_SD_SDR104) + mmc->caps |= MMC_CAP_UHS_SDR104; + if (pcr->extra_caps & EXTRA_CAPS_SD_DDR50) + mmc->caps |= MMC_CAP_UHS_DDR50; + if (pcr->extra_caps & EXTRA_CAPS_MMC_HSDDR) + mmc->caps |= MMC_CAP_1_8V_DDR; + if (pcr->extra_caps & EXTRA_CAPS_MMC_8BIT) + mmc->caps |= MMC_CAP_8_BIT_DATA; +} + +static void realtek_init_host(struct realtek_pci_sdmmc *host) +{ + struct mmc_host *mmc = host->mmc; + + mmc->f_min = 250000; + mmc->f_max = 208000000; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | + MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST | + MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25; + mmc->max_current_330 = 400; + mmc->max_current_180 = 800; + mmc->ops = &realtek_pci_sdmmc_ops; + + init_extra_caps(host); + + mmc->max_segs = 256; + mmc->max_seg_size = 65536; + mmc->max_blk_size = 512; + mmc->max_blk_count = 65535; + mmc->max_req_size = 524288; +} + +static void rtsx_pci_sdmmc_card_event(struct platform_device *pdev) +{ + struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); + + mmc_detect_change(host->mmc, 0); +} + +static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct realtek_pci_sdmmc *host; + struct rtsx_pcr *pcr; + struct pcr_handle *handle = pdev->dev.platform_data; + + if (!handle) + return -ENXIO; + + pcr = handle->pcr; + if (!pcr) + return -ENXIO; + + dev_dbg(&(pdev->dev), ": Realtek PCI-E SDMMC controller found\n"); + + mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + host->pcr = pcr; + host->mmc = mmc; + host->pdev = pdev; + platform_set_drvdata(pdev, host); + pcr->slots[RTSX_SD_CARD].p_dev = pdev; + pcr->slots[RTSX_SD_CARD].card_event = rtsx_pci_sdmmc_card_event; + + mutex_init(&host->host_mutex); + + realtek_init_host(host); + + mmc_add_host(mmc); + + return 0; +} + +static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev) +{ + struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); + struct rtsx_pcr *pcr; + struct mmc_host *mmc; + + if (!host) + return 0; + + pcr = host->pcr; + pcr->slots[RTSX_SD_CARD].p_dev = NULL; + pcr->slots[RTSX_SD_CARD].card_event = NULL; + mmc = host->mmc; + host->eject = true; + + mutex_lock(&host->host_mutex); + if (host->mrq) { + dev_dbg(&(pdev->dev), + "%s: Controller removed during transfer\n", + mmc_hostname(mmc)); + + rtsx_pci_complete_unfinished_transfer(pcr); + + host->mrq->cmd->error = -ENOMEDIUM; + if (host->mrq->stop) + host->mrq->stop->error = -ENOMEDIUM; + mmc_request_done(mmc, host->mrq); + } + mutex_unlock(&host->host_mutex); + + mmc_remove_host(mmc); + mmc_free_host(mmc); + + platform_set_drvdata(pdev, NULL); + + dev_dbg(&(pdev->dev), + ": Realtek PCI-E SDMMC controller has been removed\n"); + + return 0; +} + +static struct platform_device_id rtsx_pci_sdmmc_ids[] = { + { + .name = DRV_NAME_RTSX_PCI_SDMMC, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, rtsx_pci_sdmmc_ids); + +static struct platform_driver rtsx_pci_sdmmc_driver = { + .probe = rtsx_pci_sdmmc_drv_probe, + .remove = rtsx_pci_sdmmc_drv_remove, + .id_table = rtsx_pci_sdmmc_ids, + .suspend = rtsx_pci_sdmmc_suspend, + .resume = rtsx_pci_sdmmc_resume, + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME_RTSX_PCI_SDMMC, + }, +}; +module_platform_driver(rtsx_pci_sdmmc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Wei WANG "); +MODULE_DESCRIPTION("Realtek PCI-E SD/MMC Card Host Driver"); From 13467710601adbb8192576d56ad2ae6114e562ce Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Mon, 29 Oct 2012 13:49:44 +0800 Subject: [PATCH 013/119] memstick: Add realtek pcie memstick host driver Realtek PCI-E Memstick card host driver is used to access Memstick card, with the help of Realtek PCI-E card reader MFD driver. Signed-off-by: Wei WANG Acked-by: Alex Dubov Signed-off-by: Samuel Ortiz --- drivers/memstick/host/Kconfig | 10 + drivers/memstick/host/Makefile | 1 + drivers/memstick/host/rtsx_pci_ms.c | 641 ++++++++++++++++++++++++++++ 3 files changed, 652 insertions(+) create mode 100644 drivers/memstick/host/rtsx_pci_ms.c diff --git a/drivers/memstick/host/Kconfig b/drivers/memstick/host/Kconfig index cc0997a05171..4f7a17fd1aa7 100644 --- a/drivers/memstick/host/Kconfig +++ b/drivers/memstick/host/Kconfig @@ -42,3 +42,13 @@ config MEMSTICK_R592 To compile this driver as a module, choose M here: the module will be called r592. + +config MEMSTICK_REALTEK_PCI + tristate "Realtek PCI-E Memstick Card Interface Driver" + depends on MFD_RTSX_PCI + help + Say Y here to include driver code to support Memstick card interface + of Realtek PCI-E card reader + + To compile this driver as a module, choose M here: the module will + be called rtsx_pci_ms. diff --git a/drivers/memstick/host/Makefile b/drivers/memstick/host/Makefile index 31ba8d378e46..af3459d7686e 100644 --- a/drivers/memstick/host/Makefile +++ b/drivers/memstick/host/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_MEMSTICK_TIFM_MS) += tifm_ms.o obj-$(CONFIG_MEMSTICK_JMICRON_38X) += jmb38x_ms.o obj-$(CONFIG_MEMSTICK_R592) += r592.o +obj-$(CONFIG_MEMSTICK_REALTEK_PCI) += rtsx_pci_ms.o diff --git a/drivers/memstick/host/rtsx_pci_ms.c b/drivers/memstick/host/rtsx_pci_ms.c new file mode 100644 index 000000000000..f5ddb82dadb7 --- /dev/null +++ b/drivers/memstick/host/rtsx_pci_ms.c @@ -0,0 +1,641 @@ +/* Realtek PCI-Express Memstick Card Interface driver + * + * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + * Author: + * Wei WANG + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + */ + +#include +#include +#include +#include +#include +#include +#include + +struct realtek_pci_ms { + struct platform_device *pdev; + struct rtsx_pcr *pcr; + struct memstick_host *msh; + struct memstick_request *req; + + struct mutex host_mutex; + struct work_struct handle_req; + + u8 ssc_depth; + unsigned int clock; + unsigned char ifmode; + bool eject; +}; + +static inline struct device *ms_dev(struct realtek_pci_ms *host) +{ + return &(host->pdev->dev); +} + +static inline void ms_clear_error(struct realtek_pci_ms *host) +{ + rtsx_pci_write_register(host->pcr, CARD_STOP, + MS_STOP | MS_CLR_ERR, MS_STOP | MS_CLR_ERR); +} + +#ifdef DEBUG + +static void ms_print_debug_regs(struct realtek_pci_ms *host) +{ + struct rtsx_pcr *pcr = host->pcr; + u16 i; + u8 *ptr; + + /* Print MS host internal registers */ + rtsx_pci_init_cmd(pcr); + for (i = 0xFD40; i <= 0xFD44; i++) + rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0); + for (i = 0xFD52; i <= 0xFD69; i++) + rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0); + rtsx_pci_send_cmd(pcr, 100); + + ptr = rtsx_pci_get_cmd_data(pcr); + for (i = 0xFD40; i <= 0xFD44; i++) + dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); + for (i = 0xFD52; i <= 0xFD69; i++) + dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); +} + +#else + +#define ms_print_debug_regs(host) + +#endif + +static int ms_power_on(struct realtek_pci_ms *host) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, 0x07, MS_MOD_SEL); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE, + CARD_SHARE_MASK, CARD_SHARE_48_MS); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, + MS_CLK_EN, MS_CLK_EN); + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + err = rtsx_pci_card_pull_ctl_enable(pcr, RTSX_MS_CARD); + if (err < 0) + return err; + + err = rtsx_pci_card_power_on(pcr, RTSX_MS_CARD); + if (err < 0) + return err; + + /* Wait ms power stable */ + msleep(150); + + err = rtsx_pci_write_register(pcr, CARD_OE, + MS_OUTPUT_EN, MS_OUTPUT_EN); + if (err < 0) + return err; + + return 0; +} + +static int ms_power_off(struct realtek_pci_ms *host) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + + rtsx_pci_init_cmd(pcr); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, MS_CLK_EN, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_OE, MS_OUTPUT_EN, 0); + + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + err = rtsx_pci_card_power_off(pcr, RTSX_MS_CARD); + if (err < 0) + return err; + + return rtsx_pci_card_pull_ctl_disable(pcr, RTSX_MS_CARD); +} + +static int ms_transfer_data(struct realtek_pci_ms *host, unsigned char data_dir, + u8 tpc, u8 cfg, struct scatterlist *sg) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + unsigned int length = sg->length; + u16 sec_cnt = (u16)(length / 512); + u8 val, trans_mode, dma_dir; + + dev_dbg(ms_dev(host), "%s: tpc = 0x%02x, data_dir = %s, length = %d\n", + __func__, tpc, (data_dir == READ) ? "READ" : "WRITE", + length); + + if (data_dir == READ) { + dma_dir = DMA_DIR_FROM_CARD; + trans_mode = MS_TM_AUTO_READ; + } else { + dma_dir = DMA_DIR_TO_CARD; + trans_mode = MS_TM_AUTO_WRITE; + } + + rtsx_pci_init_cmd(pcr); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_SECTOR_CNT_H, + 0xFF, (u8)(sec_cnt >> 8)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_SECTOR_CNT_L, + 0xFF, (u8)sec_cnt); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0, + DMA_DONE_INT, DMA_DONE_INT); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3, 0xFF, (u8)(length >> 24)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2, 0xFF, (u8)(length >> 16)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1, 0xFF, (u8)(length >> 8)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)length); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, + 0x03 | DMA_PACK_SIZE_MASK, dma_dir | DMA_EN | DMA_512); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, + 0x01, RING_BUFFER); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER, + 0xFF, MS_TRANSFER_START | trans_mode); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER, + MS_TRANSFER_END, MS_TRANSFER_END); + + rtsx_pci_send_cmd_no_wait(pcr); + + err = rtsx_pci_transfer_data(pcr, sg, 1, data_dir == READ, 10000); + if (err < 0) { + ms_clear_error(host); + return err; + } + + rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val); + if (val & (MS_INT_CMDNK | MS_INT_ERR | MS_CRC16_ERR | MS_RDY_TIMEOUT)) + return -EIO; + + return 0; +} + +static int ms_write_bytes(struct realtek_pci_ms *host, u8 tpc, + u8 cfg, u8 cnt, u8 *data, u8 *int_reg) +{ + struct rtsx_pcr *pcr = host->pcr; + int err, i; + + dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc); + + if (!data) + return -EINVAL; + + rtsx_pci_init_cmd(pcr); + + for (i = 0; i < cnt; i++) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + PPBUF_BASE2 + i, 0xFF, data[i]); + if (cnt % 2) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + PPBUF_BASE2 + i, 0xFF, 0xFF); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, + 0x01, PINGPONG_BUFFER); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER, + 0xFF, MS_TRANSFER_START | MS_TM_WRITE_BYTES); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER, + MS_TRANSFER_END, MS_TRANSFER_END); + if (int_reg) + rtsx_pci_add_cmd(pcr, READ_REG_CMD, MS_TRANS_CFG, 0, 0); + + err = rtsx_pci_send_cmd(pcr, 5000); + if (err < 0) { + u8 val; + + rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val); + dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val); + + if (int_reg) + *int_reg = val & 0x0F; + + ms_print_debug_regs(host); + + ms_clear_error(host); + + if (!(tpc & 0x08)) { + if (val & MS_CRC16_ERR) + return -EIO; + } else { + if (!(val & 0x80)) { + if (val & (MS_INT_ERR | MS_INT_CMDNK)) + return -EIO; + } + } + + return -ETIMEDOUT; + } + + if (int_reg) { + u8 *ptr = rtsx_pci_get_cmd_data(pcr) + 1; + *int_reg = *ptr & 0x0F; + } + + return 0; +} + +static int ms_read_bytes(struct realtek_pci_ms *host, u8 tpc, + u8 cfg, u8 cnt, u8 *data, u8 *int_reg) +{ + struct rtsx_pcr *pcr = host->pcr; + int err, i; + u8 *ptr; + + dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc); + + if (!data) + return -EINVAL; + + rtsx_pci_init_cmd(pcr); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, + 0x01, PINGPONG_BUFFER); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER, + 0xFF, MS_TRANSFER_START | MS_TM_READ_BYTES); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER, + MS_TRANSFER_END, MS_TRANSFER_END); + for (i = 0; i < cnt - 1; i++) + rtsx_pci_add_cmd(pcr, READ_REG_CMD, PPBUF_BASE2 + i, 0, 0); + if (cnt % 2) + rtsx_pci_add_cmd(pcr, READ_REG_CMD, PPBUF_BASE2 + cnt, 0, 0); + else + rtsx_pci_add_cmd(pcr, READ_REG_CMD, + PPBUF_BASE2 + cnt - 1, 0, 0); + if (int_reg) + rtsx_pci_add_cmd(pcr, READ_REG_CMD, MS_TRANS_CFG, 0, 0); + + err = rtsx_pci_send_cmd(pcr, 5000); + if (err < 0) { + u8 val; + + rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val); + dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val); + + if (int_reg) + *int_reg = val & 0x0F; + + ms_print_debug_regs(host); + + ms_clear_error(host); + + if (!(tpc & 0x08)) { + if (val & MS_CRC16_ERR) + return -EIO; + } else { + if (!(val & 0x80)) { + if (val & (MS_INT_ERR | MS_INT_CMDNK)) + return -EIO; + } + } + + return -ETIMEDOUT; + } + + ptr = rtsx_pci_get_cmd_data(pcr) + 1; + for (i = 0; i < cnt; i++) + data[i] = *ptr++; + + if (int_reg) + *int_reg = *ptr & 0x0F; + + return 0; +} + +static int rtsx_pci_ms_issue_cmd(struct realtek_pci_ms *host) +{ + struct memstick_request *req = host->req; + int err = 0; + u8 cfg = 0, int_reg; + + dev_dbg(ms_dev(host), "%s\n", __func__); + + if (req->need_card_int) { + if (host->ifmode != MEMSTICK_SERIAL) + cfg = WAIT_INT; + } + + if (req->long_data) { + err = ms_transfer_data(host, req->data_dir, + req->tpc, cfg, &(req->sg)); + } else { + if (req->data_dir == READ) { + err = ms_read_bytes(host, req->tpc, cfg, + req->data_len, req->data, &int_reg); + } else { + err = ms_write_bytes(host, req->tpc, cfg, + req->data_len, req->data, &int_reg); + } + } + if (err < 0) + return err; + + if (req->need_card_int && (host->ifmode == MEMSTICK_SERIAL)) { + err = ms_read_bytes(host, MS_TPC_GET_INT, + NO_WAIT_INT, 1, &int_reg, NULL); + if (err < 0) + return err; + } + + if (req->need_card_int) { + dev_dbg(ms_dev(host), "int_reg: 0x%02x\n", int_reg); + + if (int_reg & MS_INT_CMDNK) + req->int_reg |= MEMSTICK_INT_CMDNAK; + if (int_reg & MS_INT_BREQ) + req->int_reg |= MEMSTICK_INT_BREQ; + if (int_reg & MS_INT_ERR) + req->int_reg |= MEMSTICK_INT_ERR; + if (int_reg & MS_INT_CED) + req->int_reg |= MEMSTICK_INT_CED; + } + + return 0; +} + +static void rtsx_pci_ms_handle_req(struct work_struct *work) +{ + struct realtek_pci_ms *host = container_of(work, + struct realtek_pci_ms, handle_req); + struct rtsx_pcr *pcr = host->pcr; + struct memstick_host *msh = host->msh; + int rc; + + mutex_lock(&pcr->pcr_mutex); + + rtsx_pci_start_run(pcr); + + rtsx_pci_switch_clock(host->pcr, host->clock, host->ssc_depth, + false, true, false); + rtsx_pci_write_register(pcr, CARD_SELECT, 0x07, MS_MOD_SEL); + rtsx_pci_write_register(pcr, CARD_SHARE_MODE, + CARD_SHARE_MASK, CARD_SHARE_48_MS); + + if (!host->req) { + do { + rc = memstick_next_req(msh, &host->req); + dev_dbg(ms_dev(host), "next req %d\n", rc); + + if (!rc) + host->req->error = rtsx_pci_ms_issue_cmd(host); + } while (!rc); + } + + mutex_unlock(&pcr->pcr_mutex); +} + +static void rtsx_pci_ms_request(struct memstick_host *msh) +{ + struct realtek_pci_ms *host = memstick_priv(msh); + + dev_dbg(ms_dev(host), "--> %s\n", __func__); + + schedule_work(&host->handle_req); +} + +static int rtsx_pci_ms_set_param(struct memstick_host *msh, + enum memstick_param param, int value) +{ + struct realtek_pci_ms *host = memstick_priv(msh); + struct rtsx_pcr *pcr = host->pcr; + unsigned int clock = 0; + u8 ssc_depth = 0; + int err; + + dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n", + __func__, param, value); + + switch (param) { + case MEMSTICK_POWER: + if (value == MEMSTICK_POWER_ON) + err = ms_power_on(host); + else if (value == MEMSTICK_POWER_OFF) + err = ms_power_off(host); + else + return -EINVAL; + break; + + case MEMSTICK_INTERFACE: + if (value == MEMSTICK_SERIAL) { + clock = 19000000; + ssc_depth = RTSX_SSC_DEPTH_500K; + + err = rtsx_pci_write_register(pcr, MS_CFG, + 0x18, MS_BUS_WIDTH_1); + if (err < 0) + return err; + } else if (value == MEMSTICK_PAR4) { + clock = 39000000; + ssc_depth = RTSX_SSC_DEPTH_1M; + + err = rtsx_pci_write_register(pcr, MS_CFG, + 0x58, MS_BUS_WIDTH_4 | PUSH_TIME_ODD); + if (err < 0) + return err; + } else { + return -EINVAL; + } + + err = rtsx_pci_switch_clock(pcr, clock, + ssc_depth, false, true, false); + if (err < 0) + return err; + + host->ssc_depth = ssc_depth; + host->clock = clock; + host->ifmode = value; + break; + } + + return 0; +} + +#ifdef CONFIG_PM + +static int rtsx_pci_ms_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct realtek_pci_ms *host = platform_get_drvdata(pdev); + struct memstick_host *msh = host->msh; + + dev_dbg(ms_dev(host), "--> %s\n", __func__); + + memstick_suspend_host(msh); + return 0; +} + +static int rtsx_pci_ms_resume(struct platform_device *pdev) +{ + struct realtek_pci_ms *host = platform_get_drvdata(pdev); + struct memstick_host *msh = host->msh; + + dev_dbg(ms_dev(host), "--> %s\n", __func__); + + memstick_resume_host(msh); + return 0; +} + +#else /* CONFIG_PM */ + +#define rtsx_pci_ms_suspend NULL +#define rtsx_pci_ms_resume NULL + +#endif /* CONFIG_PM */ + +static void rtsx_pci_ms_card_event(struct platform_device *pdev) +{ + struct realtek_pci_ms *host = platform_get_drvdata(pdev); + + memstick_detect_change(host->msh); +} + +static int rtsx_pci_ms_drv_probe(struct platform_device *pdev) +{ + struct memstick_host *msh; + struct realtek_pci_ms *host; + struct rtsx_pcr *pcr; + struct pcr_handle *handle = pdev->dev.platform_data; + int rc; + + if (!handle) + return -ENXIO; + + pcr = handle->pcr; + if (!pcr) + return -ENXIO; + + dev_dbg(&(pdev->dev), + ": Realtek PCI-E Memstick controller found\n"); + + msh = memstick_alloc_host(sizeof(*host), &pdev->dev); + if (!msh) + return -ENOMEM; + + host = memstick_priv(msh); + host->pcr = pcr; + host->msh = msh; + host->pdev = pdev; + platform_set_drvdata(pdev, host); + pcr->slots[RTSX_MS_CARD].p_dev = pdev; + pcr->slots[RTSX_MS_CARD].card_event = rtsx_pci_ms_card_event; + + mutex_init(&host->host_mutex); + + INIT_WORK(&host->handle_req, rtsx_pci_ms_handle_req); + msh->request = rtsx_pci_ms_request; + msh->set_param = rtsx_pci_ms_set_param; + msh->caps = MEMSTICK_CAP_PAR4; + + rc = memstick_add_host(msh); + if (rc) { + memstick_free_host(msh); + return rc; + } + + return 0; +} + +static int rtsx_pci_ms_drv_remove(struct platform_device *pdev) +{ + struct realtek_pci_ms *host = platform_get_drvdata(pdev); + struct rtsx_pcr *pcr; + struct memstick_host *msh; + int rc; + + if (!host) + return 0; + + pcr = host->pcr; + pcr->slots[RTSX_MS_CARD].p_dev = NULL; + pcr->slots[RTSX_MS_CARD].card_event = NULL; + msh = host->msh; + host->eject = true; + + mutex_lock(&host->host_mutex); + if (host->req) { + dev_dbg(&(pdev->dev), + "%s: Controller removed during transfer\n", + dev_name(&msh->dev)); + + rtsx_pci_complete_unfinished_transfer(pcr); + + host->req->error = -ENOMEDIUM; + do { + rc = memstick_next_req(msh, &host->req); + if (!rc) + host->req->error = -ENOMEDIUM; + } while (!rc); + } + mutex_unlock(&host->host_mutex); + + memstick_remove_host(msh); + memstick_free_host(msh); + + platform_set_drvdata(pdev, NULL); + + dev_dbg(&(pdev->dev), + ": Realtek PCI-E Memstick controller has been removed\n"); + + return 0; +} + +static struct platform_device_id rtsx_pci_ms_ids[] = { + { + .name = DRV_NAME_RTSX_PCI_MS, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, rtsx_pci_ms_ids); + +static struct platform_driver rtsx_pci_ms_driver = { + .probe = rtsx_pci_ms_drv_probe, + .remove = rtsx_pci_ms_drv_remove, + .id_table = rtsx_pci_ms_ids, + .suspend = rtsx_pci_ms_suspend, + .resume = rtsx_pci_ms_resume, + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME_RTSX_PCI_MS, + }, +}; +module_platform_driver(rtsx_pci_ms_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Wei WANG "); +MODULE_DESCRIPTION("Realtek PCI-E Memstick Card Host Driver"); From 45e48aa6cabab6b03ac62caded4d86f157997b13 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 8 Nov 2012 10:38:56 +0100 Subject: [PATCH 014/119] mfd: Select MFD_CORE for rtsx The realtek driver use the MFD core API and thus must select MFD_CORE. Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index d0cb4d4bc2cc..3f2187ae8c5d 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -66,6 +66,7 @@ config MFD_SM501_GPIO config MFD_RTSX_PCI tristate "Support for Realtek PCI-E card reader" depends on PCI + select MFD_CORE help This supports for Realtek PCI-Express card reader including rts5209, rts5229, rtl8411, etc. Realtek card reader supports access to many From aec17ea1ee78923f1287c18e9905c19e4dfd9c64 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 9 Nov 2012 10:19:54 +0100 Subject: [PATCH 015/119] mfd: Explicitely include slab.h to rtsx This fixes the following build error on some architectures (parisc at least): drivers/mfd/rtsx_pcr.c: In function 'rtsx_pci_init_chip': drivers/mfd/rtsx_pcr.c:985:2: error: implicit declaration of function 'kcalloc' [-Werror=implicit-function-declaration] drivers/mfd/rtsx_pcr.c:985:13: warning: assignment makes pointer from integer without a cast [enabled by default] Signed-off-by: Samuel Ortiz --- drivers/mfd/rtsx_pcr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 56d4377c62c2..3a44efa29203 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include From 8ae754ebd5edffa0b2a2bafa4879a9ace01d5477 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 5 Nov 2012 16:10:29 +0100 Subject: [PATCH 016/119] mfd: ab8500-core: Remove unused ab8500-gpio IRQ ranges The IRQ ranges provided in ab8500-core to be passed on to the ab8500-gpio driver are not only redundant, but they are also causing a warning in the boot log. These IRQ ranges, like any other MFD related IRQ resource are passed though MFD core for automatic conversion to virtual IRQs; however, MFD core does not support IRQ mapping of IRQ ranges. Let's just remove them. Acked-by: Arnd Bergmann Tested-by: Linus Walleij Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 1667c77b5cde..e7197fe7059a 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -591,38 +591,6 @@ int ab8500_suspend(struct ab8500 *ab8500) return 0; } -/* AB8500 GPIO Resources */ -static struct resource __devinitdata ab8500_gpio_resources[] = { - { - .name = "GPIO_INT6", - .start = AB8500_INT_GPIO6R, - .end = AB8500_INT_GPIO41F, - .flags = IORESOURCE_IRQ, - } -}; - -/* AB9540 GPIO Resources */ -static struct resource __devinitdata ab9540_gpio_resources[] = { - { - .name = "GPIO_INT6", - .start = AB8500_INT_GPIO6R, - .end = AB8500_INT_GPIO41F, - .flags = IORESOURCE_IRQ, - }, - { - .name = "GPIO_INT14", - .start = AB9540_INT_GPIO50R, - .end = AB9540_INT_GPIO54R, - .flags = IORESOURCE_IRQ, - }, - { - .name = "GPIO_INT15", - .start = AB9540_INT_GPIO50F, - .end = AB9540_INT_GPIO54F, - .flags = IORESOURCE_IRQ, - } -}; - static struct resource __devinitdata ab8500_gpadc_resources[] = { { .name = "HW_CONV_END", @@ -1065,8 +1033,6 @@ static struct mfd_cell __devinitdata ab8500_devs[] = { { .name = "ab8500-gpio", .of_compatible = "stericsson,ab8500-gpio", - .num_resources = ARRAY_SIZE(ab8500_gpio_resources), - .resources = ab8500_gpio_resources, }, { .name = "ab8500-usb", @@ -1083,8 +1049,6 @@ static struct mfd_cell __devinitdata ab8500_devs[] = { static struct mfd_cell __devinitdata ab9540_devs[] = { { .name = "ab8500-gpio", - .num_resources = ARRAY_SIZE(ab9540_gpio_resources), - .resources = ab9540_gpio_resources, }, { .name = "ab9540-usb", From 7da0cbfc54c82eec793ff3d1b23b7a25406c6dba Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 5 Nov 2012 16:10:30 +0100 Subject: [PATCH 017/119] mfd: Prevent STMPE from abusing mfd_add_devices' irq_base parameter Originally IRQ incrementers were provided in some template resource structures for keypad and touchscreen devices. These were passed as IORESOURCE_IRQs to MFD core in the usual way. The true device IRQs were instead added to the irq_base when mfd_add_devices was invoked. This is clearly an abuse of the call, and does not scale when IRQ Domains are brought into play. Before we can provide the STMPE with its own IRQ Domain we must first fix this. This patche keeps most of the driver's structure, keeping the template strategy. However, instead of providing the IRQ as an increment to irq_base, we dynamically populate the IORESOURCE_IRQ with the correct device IRQ. Acked-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/stmpe.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index c94f521f392c..ad13cb00a749 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -310,14 +310,10 @@ static struct mfd_cell stmpe_gpio_cell_noirq = { static struct resource stmpe_keypad_resources[] = { { .name = "KEYPAD", - .start = 0, - .end = 0, .flags = IORESOURCE_IRQ, }, { .name = "KEYPAD_OVER", - .start = 1, - .end = 1, .flags = IORESOURCE_IRQ, }, }; @@ -397,14 +393,10 @@ static struct stmpe_variant_info stmpe801_noirq = { static struct resource stmpe_ts_resources[] = { { .name = "TOUCH_DET", - .start = 0, - .end = 0, .flags = IORESOURCE_IRQ, }, { .name = "FIFO_TH", - .start = 1, - .end = 1, .flags = IORESOURCE_IRQ, }, }; @@ -959,10 +951,10 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe) } static int __devinit stmpe_add_device(struct stmpe *stmpe, - struct mfd_cell *cell, int irq) + struct mfd_cell *cell) { return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1, - NULL, stmpe->irq_base + irq, NULL); + NULL, stmpe->irq_base, NULL); } static int __devinit stmpe_devices_init(struct stmpe *stmpe) @@ -970,7 +962,7 @@ static int __devinit stmpe_devices_init(struct stmpe *stmpe) struct stmpe_variant_info *variant = stmpe->variant; unsigned int platform_blocks = stmpe->pdata->blocks; int ret = -EINVAL; - int i; + int i, j; for (i = 0; i < variant->num_blocks; i++) { struct stmpe_variant_block *block = &variant->blocks[i]; @@ -978,8 +970,17 @@ static int __devinit stmpe_devices_init(struct stmpe *stmpe) if (!(platform_blocks & block->block)) continue; + for (j = 0; j < block->cell->num_resources; j++) { + struct resource *res = + (struct resource *) &block->cell->resources[j]; + + /* Dynamically fill in a variant's IRQ. */ + if (res->flags & IORESOURCE_IRQ) + res->start = res->end = block->irq + j; + } + platform_blocks &= ~block->block; - ret = stmpe_add_device(stmpe, block->cell, block->irq); + ret = stmpe_add_device(stmpe, block->cell); if (ret) return ret; } From 76f93992e4c44f30be797d5c99d6f369ed001747 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 5 Nov 2012 16:10:31 +0100 Subject: [PATCH 018/119] mfd: Provide the STMPE driver with its own IRQ domain The STMPE driver is yet another IRQ controller which requires its own IRQ domain. So, we provide it with one. Acked-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/stmpe.c | 82 ++++++++++++++++++++++++--------------- include/linux/mfd/stmpe.h | 2 + 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index ad13cb00a749..5c8d8f260df2 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -757,7 +758,9 @@ static irqreturn_t stmpe_irq(int irq, void *data) int i; if (variant->id_val == STMPE801_ID) { - handle_nested_irq(stmpe->irq_base); + int base = irq_create_mapping(stmpe->domain, 0); + + handle_nested_irq(base); return IRQ_HANDLED; } @@ -778,8 +781,9 @@ static irqreturn_t stmpe_irq(int irq, void *data) while (status) { int bit = __ffs(status); int line = bank * 8 + bit; + int nestedirq = irq_create_mapping(stmpe->domain, line); - handle_nested_irq(stmpe->irq_base + line); + handle_nested_irq(nestedirq); status &= ~(1 << bit); } @@ -820,7 +824,7 @@ static void stmpe_irq_sync_unlock(struct irq_data *data) static void stmpe_irq_mask(struct irq_data *data) { struct stmpe *stmpe = irq_data_get_irq_chip_data(data); - int offset = data->irq - stmpe->irq_base; + int offset = data->hwirq; int regoffset = offset / 8; int mask = 1 << (offset % 8); @@ -830,7 +834,7 @@ static void stmpe_irq_mask(struct irq_data *data) static void stmpe_irq_unmask(struct irq_data *data) { struct stmpe *stmpe = irq_data_get_irq_chip_data(data); - int offset = data->irq - stmpe->irq_base; + int offset = data->hwirq; int regoffset = offset / 8; int mask = 1 << (offset % 8); @@ -845,43 +849,62 @@ static struct irq_chip stmpe_irq_chip = { .irq_unmask = stmpe_irq_unmask, }; -static int __devinit stmpe_irq_init(struct stmpe *stmpe) +static int stmpe_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hwirq) { + struct stmpe *stmpe = d->host_data; struct irq_chip *chip = NULL; - int num_irqs = stmpe->variant->num_irqs; - int base = stmpe->irq_base; - int irq; if (stmpe->variant->id_val != STMPE801_ID) chip = &stmpe_irq_chip; - for (irq = base; irq < base + num_irqs; irq++) { - irq_set_chip_data(irq, stmpe); - irq_set_chip_and_handler(irq, chip, handle_edge_irq); - irq_set_nested_thread(irq, 1); + irq_set_chip_data(virq, stmpe); + irq_set_chip_and_handler(virq, chip, handle_edge_irq); + irq_set_nested_thread(virq, 1); #ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); + set_irq_flags(virq, IRQF_VALID); #else - irq_set_noprobe(irq); + irq_set_noprobe(virq); #endif - } return 0; } -static void stmpe_irq_remove(struct stmpe *stmpe) +static void stmpe_irq_unmap(struct irq_domain *d, unsigned int virq) { - int num_irqs = stmpe->variant->num_irqs; - int base = stmpe->irq_base; - int irq; - - for (irq = base; irq < base + num_irqs; irq++) { #ifdef CONFIG_ARM - set_irq_flags(irq, 0); + set_irq_flags(virq, 0); #endif - irq_set_chip_and_handler(irq, NULL, NULL); - irq_set_chip_data(irq, NULL); + irq_set_chip_and_handler(virq, NULL, NULL); + irq_set_chip_data(virq, NULL); +} + +static struct irq_domain_ops stmpe_irq_ops = { + .map = stmpe_irq_map, + .unmap = stmpe_irq_unmap, + .xlate = irq_domain_xlate_twocell, +}; + +static int __devinit stmpe_irq_init(struct stmpe *stmpe) +{ + int base = stmpe->irq_base; + int num_irqs = stmpe->variant->num_irqs; + + if (base) { + stmpe->domain = irq_domain_add_legacy( + NULL, num_irqs, base, 0, &stmpe_irq_ops, stmpe); } + else { + stmpe->domain = irq_domain_add_linear( + NULL, num_irqs, &stmpe_irq_ops, stmpe); + } + + if (!stmpe->domain) { + dev_err(stmpe->dev, "Failed to create irqdomain\n"); + return -ENOSYS; + } + + return 0; } static int __devinit stmpe_chip_init(struct stmpe *stmpe) @@ -954,7 +977,7 @@ static int __devinit stmpe_add_device(struct stmpe *stmpe, struct mfd_cell *cell) { return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1, - NULL, stmpe->irq_base, NULL); + NULL, stmpe->irq_base, stmpe->domain); } static int __devinit stmpe_devices_init(struct stmpe *stmpe) @@ -1067,7 +1090,7 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum) if (ret) { dev_err(stmpe->dev, "failed to request IRQ: %d\n", ret); - goto out_removeirq; + goto free_gpio; } } @@ -1083,9 +1106,6 @@ out_removedevs: mfd_remove_devices(stmpe->dev); if (stmpe->irq >= 0) free_irq(stmpe->irq, stmpe); -out_removeirq: - if (stmpe->irq >= 0) - stmpe_irq_remove(stmpe); free_gpio: if (pdata->irq_over_gpio) gpio_free(pdata->irq_gpio); @@ -1098,10 +1118,8 @@ int stmpe_remove(struct stmpe *stmpe) { mfd_remove_devices(stmpe->dev); - if (stmpe->irq >= 0) { + if (stmpe->irq >= 0) free_irq(stmpe->irq, stmpe); - stmpe_irq_remove(stmpe); - } if (stmpe->pdata->irq_over_gpio) gpio_free(stmpe->pdata->irq_gpio); diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index f8d5b4d5843f..15dac790365f 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -62,6 +62,7 @@ struct stmpe_client_info; * @lock: lock protecting I/O operations * @irq_lock: IRQ bus lock * @dev: device, mostly for dev_dbg() + * @irq_domain: IRQ domain * @client: client - i2c or spi * @ci: client specific information * @partnum: part number @@ -79,6 +80,7 @@ struct stmpe { struct mutex lock; struct mutex irq_lock; struct device *dev; + struct irq_domain *domain; void *client; struct stmpe_client_info *ci; enum stmpe_partnum partnum; From 5204e51d30ceb9715e3d690efe84c50e2e9d557d Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 5 Nov 2012 16:10:32 +0100 Subject: [PATCH 019/119] mfd: Correct copy and paste mistake in stmpe When specifying IRQ numbers for the stmpe1601, IRQ defines for the stmpe24xx were used instead. Fortunately, the defined numbers are the same, hence why it survived testing. This fix is merely an aesthetic one. Acked-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/stmpe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 5c8d8f260df2..e50ebdfeed0f 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -519,12 +519,12 @@ static const u8 stmpe1601_regs[] = { static struct stmpe_variant_block stmpe1601_blocks[] = { { .cell = &stmpe_gpio_cell, - .irq = STMPE24XX_IRQ_GPIOC, + .irq = STMPE1601_IRQ_GPIOC, .block = STMPE_BLOCK_GPIO, }, { .cell = &stmpe_keypad_cell, - .irq = STMPE24XX_IRQ_KEYPAD, + .irq = STMPE1601_IRQ_KEYPAD, .block = STMPE_BLOCK_KEYPAD, }, }; From 909582caae384418723ccdb00e848f4140257195 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 5 Nov 2012 16:10:33 +0100 Subject: [PATCH 020/119] mfd: Enable the STMPE MFD for Device Tree This patch allows the STMPE Multi-Functional Device to be correctly initialised when booting with Device Tree support enabled. Its children are specified by the addition of subordinate devices to the STMPE node in the Device Tree file. Acked-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/stmpe.c | 48 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index e50ebdfeed0f..ba157d45192d 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -885,18 +885,19 @@ static struct irq_domain_ops stmpe_irq_ops = { .xlate = irq_domain_xlate_twocell, }; -static int __devinit stmpe_irq_init(struct stmpe *stmpe) +static int __devinit stmpe_irq_init(struct stmpe *stmpe, + struct device_node *np) { int base = stmpe->irq_base; int num_irqs = stmpe->variant->num_irqs; if (base) { stmpe->domain = irq_domain_add_legacy( - NULL, num_irqs, base, 0, &stmpe_irq_ops, stmpe); + np, num_irqs, base, 0, &stmpe_irq_ops, stmpe); } else { stmpe->domain = irq_domain_add_linear( - NULL, num_irqs, &stmpe_irq_ops, stmpe); + np, num_irqs, &stmpe_irq_ops, stmpe); } if (!stmpe->domain) { @@ -1016,15 +1017,50 @@ static int __devinit stmpe_devices_init(struct stmpe *stmpe) return ret; } +void __devinit stmpe_of_probe(struct stmpe_platform_data *pdata, + struct device_node *np) +{ + struct device_node *child; + + of_property_read_u32(np, "st,autosleep-timeout", + &pdata->autosleep_timeout); + + pdata->autosleep = (pdata->autosleep_timeout) ? true : false; + + for_each_child_of_node(np, child) { + if (!strcmp(child->name, "stmpe_gpio")) { + pdata->blocks |= STMPE_BLOCK_GPIO; + } + if (!strcmp(child->name, "stmpe_keypad")) { + pdata->blocks |= STMPE_BLOCK_KEYPAD; + } + if (!strcmp(child->name, "stmpe_touchscreen")) { + pdata->blocks |= STMPE_BLOCK_TOUCHSCREEN; + } + if (!strcmp(child->name, "stmpe_adc")) { + pdata->blocks |= STMPE_BLOCK_ADC; + } + } +} + /* Called from client specific probe routines */ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum) { struct stmpe_platform_data *pdata = dev_get_platdata(ci->dev); + struct device_node *np = ci->dev->of_node; struct stmpe *stmpe; int ret; - if (!pdata) - return -EINVAL; + if (!pdata) { + if (np) { + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + stmpe_of_probe(pdata, np); + } else + return -EINVAL; + } stmpe = kzalloc(sizeof(struct stmpe), GFP_KERNEL); if (!stmpe) @@ -1080,7 +1116,7 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum) goto free_gpio; if (stmpe->irq >= 0) { - ret = stmpe_irq_init(stmpe); + ret = stmpe_irq_init(stmpe, np); if (ret) goto free_gpio; From 84e6de813b2d1bdb127943d3c8edf1c1afaa90da Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 5 Nov 2012 16:10:34 +0100 Subject: [PATCH 021/119] Documentation: Describe bindings for STMPE Multi-Functional Device driver Here we add the required documentation for the STMPE Multi-Functional Device (MFD) Device Tree bindings. It describes all of the bindings currently supported by the driver. Acked-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- .../devicetree/bindings/mfd/stmpe.txt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/stmpe.txt diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt new file mode 100644 index 000000000000..8f0aeda2e326 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/stmpe.txt @@ -0,0 +1,25 @@ +* STMPE Multi-Functional Device + +Required properties: + - compatible : "st,stmpe[811|1601|2401|2403]" + - reg : I2C address of the device + +Optional properties: + - interrupts : The interrupt outputs from the controller + - interrupt-controller : Marks the device node as an interrupt controller + - interrupt-parent : Specifies which IRQ controller we're connected to + - i2c-client-wake : Marks the input device as wakable + - st,autosleep-timeout : Valid entries (ms); 4, 16, 32, 64, 128, 256, 512 and 1024 + +Example: + + stmpe1601: stmpe1601@40 { + compatible = "st,stmpe1601"; + reg = <0x40>; + interrupts = <26 0x4>; + interrupt-parent = <&gpio6>; + interrupt-controller; + + i2c-client-wake; + st,autosleep-timeout = <1024>; + }; From 8c4203cb5814f53169dc4ff084a65dcf509b6f60 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 5 Nov 2012 16:10:35 +0100 Subject: [PATCH 022/119] mfd: ab8500-core: Use devm_* memory/IRQ and allocation/free routines It is better to use devm_* calls, as they allow for easier and more automatic clean-up. Resources are device allocated, so when a device is freed, so are all associated resources. Acked-by: Linus Walleij Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 62 +++++++++++++++------------------------ 1 file changed, 24 insertions(+), 38 deletions(-) diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index e7197fe7059a..2a69dc2c1f84 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1233,7 +1233,7 @@ static int __devinit ab8500_probe(struct platform_device *pdev) int i; u8 value; - ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL); + ab8500 = devm_kzalloc(&pdev->dev, sizeof *ab8500, GFP_KERNEL); if (!ab8500) return -ENOMEM; @@ -1243,10 +1243,8 @@ static int __devinit ab8500_probe(struct platform_device *pdev) ab8500->dev = &pdev->dev; resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!resource) { - ret = -ENODEV; - goto out_free_ab8500; - } + if (!resource) + return -ENODEV; ab8500->irq = resource->start; @@ -1269,7 +1267,7 @@ static int __devinit ab8500_probe(struct platform_device *pdev) ret = get_register_interruptible(ab8500, AB8500_MISC, AB8500_IC_NAME_REG, &value); if (ret < 0) - goto out_free_ab8500; + return ret; ab8500->version = value; } @@ -1277,7 +1275,7 @@ static int __devinit ab8500_probe(struct platform_device *pdev) ret = get_register_interruptible(ab8500, AB8500_MISC, AB8500_REV_REG, &value); if (ret < 0) - goto out_free_ab8500; + return ret; ab8500->chip_id = value; @@ -1294,14 +1292,13 @@ static int __devinit ab8500_probe(struct platform_device *pdev) ab8500->mask_size = AB8500_NUM_IRQ_REGS; ab8500->irq_reg_offset = ab8500_irq_regoffset; } - ab8500->mask = kzalloc(ab8500->mask_size, GFP_KERNEL); + ab8500->mask = devm_kzalloc(&pdev->dev, ab8500->mask_size, GFP_KERNEL); if (!ab8500->mask) return -ENOMEM; - ab8500->oldmask = kzalloc(ab8500->mask_size, GFP_KERNEL); - if (!ab8500->oldmask) { - ret = -ENOMEM; - goto out_freemask; - } + ab8500->oldmask = devm_kzalloc(&pdev->dev, ab8500->mask_size, GFP_KERNEL); + if (!ab8500->oldmask) + return -ENOMEM; + /* * ab8500 has switched off due to (SWITCH_OFF_STATUS): * 0x01 Swoff bit programming @@ -1355,37 +1352,37 @@ static int __devinit ab8500_probe(struct platform_device *pdev) ret = abx500_register_ops(ab8500->dev, &ab8500_ops); if (ret) - goto out_freeoldmask; + return ret; for (i = 0; i < ab8500->mask_size; i++) ab8500->mask[i] = ab8500->oldmask[i] = 0xff; ret = ab8500_irq_init(ab8500, np); if (ret) - goto out_freeoldmask; + return ret; /* Activate this feature only in ab9540 */ /* till tests are done on ab8500 1p2 or later*/ if (is_ab9540(ab8500)) { - ret = request_threaded_irq(ab8500->irq, NULL, - ab8500_hierarchical_irq, - IRQF_ONESHOT | IRQF_NO_SUSPEND, - "ab8500", ab8500); + ret = devm_request_threaded_irq(&pdev->dev, ab8500->irq, NULL, + ab8500_hierarchical_irq, + IRQF_ONESHOT | IRQF_NO_SUSPEND, + "ab8500", ab8500); } else { - ret = request_threaded_irq(ab8500->irq, NULL, - ab8500_irq, - IRQF_ONESHOT | IRQF_NO_SUSPEND, - "ab8500", ab8500); + ret = devm_request_threaded_irq(&pdev->dev, ab8500->irq, NULL, + ab8500_irq, + IRQF_ONESHOT | IRQF_NO_SUSPEND, + "ab8500", ab8500); if (ret) - goto out_freeoldmask; + return ret; } ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs, ARRAY_SIZE(abx500_common_devs), NULL, ab8500->irq_base, ab8500->domain); if (ret) - goto out_freeirq; + return ret; if (is_ab9540(ab8500)) ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs, @@ -1396,14 +1393,14 @@ static int __devinit ab8500_probe(struct platform_device *pdev) ARRAY_SIZE(ab8500_devs), NULL, ab8500->irq_base, ab8500->domain); if (ret) - goto out_freeirq; + return ret; if (is_ab9540(ab8500) || is_ab8505(ab8500)) ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs, ARRAY_SIZE(ab9540_ab8505_devs), NULL, ab8500->irq_base, ab8500->domain); if (ret) - goto out_freeirq; + return ret; if (!no_bm) { /* Add battery management devices */ @@ -1424,17 +1421,6 @@ static int __devinit ab8500_probe(struct platform_device *pdev) dev_err(ab8500->dev, "error creating sysfs entries\n"); return ret; - -out_freeirq: - free_irq(ab8500->irq, ab8500); -out_freeoldmask: - kfree(ab8500->oldmask); -out_freemask: - kfree(ab8500->mask); -out_free_ab8500: - kfree(ab8500); - - return ret; } static int __devexit ab8500_remove(struct platform_device *pdev) From b851c06c241d4a2e43ee6007705765a9b1b3abb7 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 5 Nov 2012 16:10:36 +0100 Subject: [PATCH 023/119] mfd: Differentiate between u8500 and u9540 TCDM address mapping The TCDM mappings are quite different from u8500 to u9540. If these aren't correctly specified for a given board, it will fail to boot. Here we add the correct TCDM base for the u9540. Please note that although this patch allows us to boot the u9540, it doesn't provide us with full enablement. For that, another patch-set will follow which completely re-vamps the way the PRCMU is passed TCDM mappings. Acked-by: Linus Walleij Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 00b8b0f3dfb6..c56ceddf8581 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2711,7 +2711,7 @@ static int db8500_irq_init(struct device_node *np) void __init db8500_prcmu_early_init(void) { - if (cpu_is_u8500v2()) { + if (cpu_is_u8500v2() || cpu_is_u9540()) { void *tcpm_base = ioremap_nocache(U8500_PRCMU_TCPM_BASE, SZ_4K); if (tcpm_base != NULL) { @@ -2729,7 +2729,11 @@ void __init db8500_prcmu_early_init(void) iounmap(tcpm_base); } - tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE); + if (cpu_is_u9540()) + tcdm_base = ioremap_nocache(U8500_PRCMU_TCDM_BASE, + SZ_4K + SZ_8K) + SZ_8K; + else + tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE); } else { pr_err("prcmu: Unsupported chip version\n"); BUG(); From 90a38d999739f35f4fc925c875e6ee518546b66c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 15 Oct 2012 22:44:45 +0200 Subject: [PATCH 024/119] mfd: Remove Unicode Byte Order Marks from da9055 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Older gcc (< 4.4) doesn't like files starting with Unicode BOMs: include/linux/mfd/da9055/core.h:1: error: stray ‘\357’ in program include/linux/mfd/da9055/core.h:1: error: stray ‘\273’ in program include/linux/mfd/da9055/core.h:1: error: stray ‘\277’ in program include/linux/mfd/da9055/pdata.h:1: error: stray ‘\357’ in program include/linux/mfd/da9055/pdata.h:1: error: stray ‘\273’ in program include/linux/mfd/da9055/pdata.h:1: error: stray ‘\277’ in program include/linux/mfd/da9055/reg.h:1: error: stray ‘\357’ in program include/linux/mfd/da9055/reg.h:1: error: stray ‘\273’ in program include/linux/mfd/da9055/reg.h:1: error: stray ‘\277’ in program Remove the BOMs, the rest of the files is plain ASCII anyway. Output of "file" before: include/linux/mfd/da9055/core.h: UTF-8 Unicode (with BOM) C program text include/linux/mfd/da9055/pdata.h: UTF-8 Unicode (with BOM) C program text include/linux/mfd/da9055/reg.h: UTF-8 Unicode (with BOM) C program text Output of "file" after: include/linux/mfd/da9055/core.h: ASCII C program text include/linux/mfd/da9055/pdata.h: ASCII C program text include/linux/mfd/da9055/reg.h: ASCII C program text Signed-off-by: Geert Uytterhoeven Signed-off-by: Samuel Ortiz --- include/linux/mfd/da9055/core.h | 2 +- include/linux/mfd/da9055/pdata.h | 2 +- include/linux/mfd/da9055/reg.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/mfd/da9055/core.h b/include/linux/mfd/da9055/core.h index c96ad682c59e..956afa445998 100644 --- a/include/linux/mfd/da9055/core.h +++ b/include/linux/mfd/da9055/core.h @@ -1,4 +1,4 @@ -/* +/* * da9055 declarations for DA9055 PMICs. * * Copyright(c) 2012 Dialog Semiconductor Ltd. diff --git a/include/linux/mfd/da9055/pdata.h b/include/linux/mfd/da9055/pdata.h index 147293b4471d..b9b204edb4e9 100644 --- a/include/linux/mfd/da9055/pdata.h +++ b/include/linux/mfd/da9055/pdata.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2012 Dialog Semiconductor Ltd. +/* Copyright (C) 2012 Dialog Semiconductor Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/include/linux/mfd/da9055/reg.h b/include/linux/mfd/da9055/reg.h index df237ee54803..2b592e072dbf 100644 --- a/include/linux/mfd/da9055/reg.h +++ b/include/linux/mfd/da9055/reg.h @@ -1,4 +1,4 @@ -/* +/* * DA9055 declarations for DA9055 PMICs. * * Copyright(c) 2012 Dialog Semiconductor Ltd. From a32415202f709f39a059f776da28248c499e0bb5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 11 Oct 2012 13:55:28 +0200 Subject: [PATCH 025/119] mfd: twl6040: Fix typo for power on failure Fix old copy paste bug: automatic power-down failed -> automatic power-up failed Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl6040-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 3f2a1cf02fc0..b220aa241911 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -258,7 +258,7 @@ int twl6040_power(struct twl6040 *twl6040, int on) ret = twl6040_power_up_completion(twl6040, naudint); if (ret) { dev_err(twl6040->dev, - "automatic power-down failed\n"); + "automatic power-up failed\n"); twl6040->power_count = 0; goto out; } From eae9a9c85b106163b293bf8b0fec44943e98677c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 11 Oct 2012 13:55:29 +0200 Subject: [PATCH 026/119] mfd: twl6040: Remove unused parameter for twl6040_power_up_completion() naudint parameter has not been used, remove it. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl6040-core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index b220aa241911..a46d987b9c8b 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -218,8 +218,7 @@ static irqreturn_t twl6040_naudint_handler(int irq, void *data) return IRQ_HANDLED; } -static int twl6040_power_up_completion(struct twl6040 *twl6040, - int naudint) +static int twl6040_power_up_completion(struct twl6040 *twl6040) { int time_left; u8 intid; @@ -241,7 +240,6 @@ static int twl6040_power_up_completion(struct twl6040 *twl6040, int twl6040_power(struct twl6040 *twl6040, int on) { int audpwron = twl6040->audpwron; - int naudint = twl6040->irq; int ret = 0; mutex_lock(&twl6040->mutex); @@ -255,7 +253,7 @@ int twl6040_power(struct twl6040 *twl6040, int on) /* use AUDPWRON line */ gpio_set_value(audpwron, 1); /* wait for power-up completion */ - ret = twl6040_power_up_completion(twl6040, naudint); + ret = twl6040_power_up_completion(twl6040); if (ret) { dev_err(twl6040->dev, "automatic power-up failed\n"); From f9be134357c0200ce48f4cd56b4ec50d3d2e777e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 11 Oct 2012 13:55:30 +0200 Subject: [PATCH 027/119] mfd: twl6040: Restructure power up and down code Rearrange the code path for power up and down sequence. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl6040-core.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index a46d987b9c8b..6d9db275b0f1 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -104,7 +104,7 @@ int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask) EXPORT_SYMBOL(twl6040_clear_bits); /* twl6040 codec manual power-up sequence */ -static int twl6040_power_up(struct twl6040 *twl6040) +static int twl6040_power_up_manual(struct twl6040 *twl6040) { u8 ldoctl, ncpctl, lppllctl; int ret; @@ -158,11 +158,12 @@ ncp_err: ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA); twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); + dev_err(twl6040->dev, "manual power-up failed\n"); return ret; } /* twl6040 manual power-down sequence */ -static void twl6040_power_down(struct twl6040 *twl6040) +static void twl6040_power_down_manual(struct twl6040 *twl6040) { u8 ncpctl, ldoctl, lppllctl; @@ -218,18 +219,22 @@ static irqreturn_t twl6040_naudint_handler(int irq, void *data) return IRQ_HANDLED; } -static int twl6040_power_up_completion(struct twl6040 *twl6040) +static int twl6040_power_up_automatic(struct twl6040 *twl6040) { int time_left; - u8 intid; + + gpio_set_value(twl6040->audpwron, 1); time_left = wait_for_completion_timeout(&twl6040->ready, msecs_to_jiffies(144)); if (!time_left) { + u8 intid; + + dev_warn(twl6040->dev, "timeout waiting for READYINT\n"); intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); if (!(intid & TWL6040_READYINT)) { - dev_err(twl6040->dev, - "timeout waiting for READYINT\n"); + dev_err(twl6040->dev, "automatic power-up failed\n"); + gpio_set_value(twl6040->audpwron, 0); return -ETIMEDOUT; } } @@ -239,7 +244,6 @@ static int twl6040_power_up_completion(struct twl6040 *twl6040) int twl6040_power(struct twl6040 *twl6040, int on) { - int audpwron = twl6040->audpwron; int ret = 0; mutex_lock(&twl6040->mutex); @@ -249,23 +253,17 @@ int twl6040_power(struct twl6040 *twl6040, int on) if (twl6040->power_count++) goto out; - if (gpio_is_valid(audpwron)) { - /* use AUDPWRON line */ - gpio_set_value(audpwron, 1); - /* wait for power-up completion */ - ret = twl6040_power_up_completion(twl6040); + if (gpio_is_valid(twl6040->audpwron)) { + /* use automatic power-up sequence */ + ret = twl6040_power_up_automatic(twl6040); if (ret) { - dev_err(twl6040->dev, - "automatic power-up failed\n"); twl6040->power_count = 0; goto out; } } else { /* use manual power-up sequence */ - ret = twl6040_power_up(twl6040); + ret = twl6040_power_up_manual(twl6040); if (ret) { - dev_err(twl6040->dev, - "manual power-up failed\n"); twl6040->power_count = 0; goto out; } @@ -286,15 +284,15 @@ int twl6040_power(struct twl6040 *twl6040, int on) if (--twl6040->power_count) goto out; - if (gpio_is_valid(audpwron)) { + if (gpio_is_valid(twl6040->audpwron)) { /* use AUDPWRON line */ - gpio_set_value(audpwron, 0); + gpio_set_value(twl6040->audpwron, 0); /* power-down sequence latency */ usleep_range(500, 700); } else { /* use manual power-down sequence */ - twl6040_power_down(twl6040); + twl6040_power_down_manual(twl6040); } twl6040->sysclk = 0; twl6040->mclk = 0; From 1ac96265a6f35080083e85b0f58182cdc9c07d0e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 11 Oct 2012 13:55:31 +0200 Subject: [PATCH 028/119] mfd: twl6040: Correct Ready and Thermal interrupt handling Create new irq handler for thermal events in order to be able to handle the event and clean up the code regarding to interrupt handling: Use proper function names for the irq handlers No need to read the INTD register anymore. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl6040-core.c | 52 +++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 6d9db275b0f1..5817bc6d09dc 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -193,27 +193,27 @@ static void twl6040_power_down_manual(struct twl6040 *twl6040) twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); } -static irqreturn_t twl6040_naudint_handler(int irq, void *data) +static irqreturn_t twl6040_readyint_handler(int irq, void *data) { struct twl6040 *twl6040 = data; - u8 intid, status; - intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); + complete(&twl6040->ready); - if (intid & TWL6040_READYINT) - complete(&twl6040->ready); + return IRQ_HANDLED; +} - if (intid & TWL6040_THINT) { - status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS); - if (status & TWL6040_TSHUTDET) { - dev_warn(twl6040->dev, - "Thermal shutdown, powering-off"); - twl6040_power(twl6040, 0); - } else { - dev_warn(twl6040->dev, - "Leaving thermal shutdown, powering-on"); - twl6040_power(twl6040, 1); - } +static irqreturn_t twl6040_thint_handler(int irq, void *data) +{ + struct twl6040 *twl6040 = data; + u8 status; + + status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS); + if (status & TWL6040_TSHUTDET) { + dev_warn(twl6040->dev, "Thermal shutdown, powering-off"); + twl6040_power(twl6040, 0); + } else { + dev_warn(twl6040->dev, "Leaving thermal shutdown, powering-on"); + twl6040_power(twl6040, 1); } return IRQ_HANDLED; @@ -580,12 +580,19 @@ static int __devinit twl6040_probe(struct i2c_client *client, goto irq_init_err; ret = request_threaded_irq(twl6040->irq_base + TWL6040_IRQ_READY, - NULL, twl6040_naudint_handler, IRQF_ONESHOT, + NULL, twl6040_readyint_handler, IRQF_ONESHOT, "twl6040_irq_ready", twl6040); if (ret) { - dev_err(twl6040->dev, "READY IRQ request failed: %d\n", - ret); - goto irq_err; + dev_err(twl6040->dev, "READY IRQ request failed: %d\n", ret); + goto readyirq_err; + } + + ret = request_threaded_irq(twl6040->irq_base + TWL6040_IRQ_TH, + NULL, twl6040_thint_handler, IRQF_ONESHOT, + "twl6040_irq_th", twl6040); + if (ret) { + dev_err(twl6040->dev, "Thermal IRQ request failed: %d\n", ret); + goto thirq_err; } /* dual-access registers controlled by I2C only */ @@ -650,8 +657,10 @@ static int __devinit twl6040_probe(struct i2c_client *client, return 0; mfd_err: + free_irq(twl6040->irq_base + TWL6040_IRQ_TH, twl6040); +thirq_err: free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040); -irq_err: +readyirq_err: twl6040_irq_exit(twl6040); irq_init_err: if (gpio_is_valid(twl6040->audpwron)) @@ -677,6 +686,7 @@ static int __devexit twl6040_remove(struct i2c_client *client) gpio_free(twl6040->audpwron); free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040); + free_irq(twl6040->irq_base + TWL6040_IRQ_TH, twl6040); twl6040_irq_exit(twl6040); mfd_remove_devices(&client->dev); From ab7edb149c7548541ee588b8372c2041b6f1cbc8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 11 Oct 2012 13:55:32 +0200 Subject: [PATCH 029/119] mfd: twl6040: Convert to use regmap_irq With regmap_irq it is possible to remove the twl6040-irq.c file and simplify the code. Signed-off-by: Peter Ujfalusi Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 4 +- drivers/mfd/Makefile | 2 +- drivers/mfd/twl6040-core.c | 55 +++++++--- drivers/mfd/twl6040-irq.c | 205 ------------------------------------ include/linux/mfd/twl6040.h | 10 +- 5 files changed, 48 insertions(+), 228 deletions(-) delete mode 100644 drivers/mfd/twl6040-irq.c diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3f2187ae8c5d..34242cada125 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -321,10 +321,10 @@ config MFD_TWL4030_AUDIO config TWL6040_CORE bool "Support for TWL6040 audio codec" - depends on I2C=y && GENERIC_HARDIRQS + depends on I2C=y select MFD_CORE select REGMAP_I2C - select IRQ_DOMAIN + select REGMAP_IRQ default n help Say yes here if you want support for Texas Instruments TWL6040 audio diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index a4093a44304a..05bebf66ccd2 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -67,7 +67,7 @@ obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o -obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o +obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 5817bc6d09dc..e5f7b795afff 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -499,6 +499,25 @@ static struct regmap_config twl6040_regmap_config = { .readable_reg = twl6040_readable_reg, }; +static const struct regmap_irq twl6040_irqs[] = { + { .reg_offset = 0, .mask = TWL6040_THINT, }, + { .reg_offset = 0, .mask = TWL6040_PLUGINT | TWL6040_UNPLUGINT, }, + { .reg_offset = 0, .mask = TWL6040_HOOKINT, }, + { .reg_offset = 0, .mask = TWL6040_HFINT, }, + { .reg_offset = 0, .mask = TWL6040_VIBINT, }, + { .reg_offset = 0, .mask = TWL6040_READYINT, }, +}; + +static struct regmap_irq_chip twl6040_irq_chip = { + .name = "twl6040", + .irqs = twl6040_irqs, + .num_irqs = ARRAY_SIZE(twl6040_irqs), + + .num_regs = 1, + .status_base = TWL6040_REG_INTID, + .mask_base = TWL6040_REG_INTMR, +}; + static int __devinit twl6040_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -574,21 +593,27 @@ static int __devinit twl6040_probe(struct i2c_client *client, goto gpio_err; } - /* codec interrupt */ - ret = twl6040_irq_init(twl6040); - if (ret) + ret = regmap_add_irq_chip(twl6040->regmap, twl6040->irq, + IRQF_ONESHOT, 0, &twl6040_irq_chip, + &twl6040->irq_data); + if (ret < 0) goto irq_init_err; - ret = request_threaded_irq(twl6040->irq_base + TWL6040_IRQ_READY, - NULL, twl6040_readyint_handler, IRQF_ONESHOT, + twl6040->irq_ready = regmap_irq_get_virq(twl6040->irq_data, + TWL6040_IRQ_READY); + twl6040->irq_th = regmap_irq_get_virq(twl6040->irq_data, + TWL6040_IRQ_TH); + + ret = request_threaded_irq(twl6040->irq_ready, NULL, + twl6040_readyint_handler, IRQF_ONESHOT, "twl6040_irq_ready", twl6040); if (ret) { dev_err(twl6040->dev, "READY IRQ request failed: %d\n", ret); goto readyirq_err; } - ret = request_threaded_irq(twl6040->irq_base + TWL6040_IRQ_TH, - NULL, twl6040_thint_handler, IRQF_ONESHOT, + ret = request_threaded_irq(twl6040->irq_th, NULL, + twl6040_thint_handler, IRQF_ONESHOT, "twl6040_irq_th", twl6040); if (ret) { dev_err(twl6040->dev, "Thermal IRQ request failed: %d\n", ret); @@ -604,7 +629,7 @@ static int __devinit twl6040_probe(struct i2c_client *client, * The ASoC codec can work without pdata, pass the platform_data only if * it has been provided. */ - irq = twl6040->irq_base + TWL6040_IRQ_PLUG; + irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_PLUG); cell = &twl6040->cells[children]; cell->name = "twl6040-codec"; twl6040_codec_rsrc[0].start = irq; @@ -618,7 +643,7 @@ static int __devinit twl6040_probe(struct i2c_client *client, children++; if (twl6040_has_vibra(pdata, node)) { - irq = twl6040->irq_base + TWL6040_IRQ_VIB; + irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_VIB); cell = &twl6040->cells[children]; cell->name = "twl6040-vibra"; @@ -657,11 +682,11 @@ static int __devinit twl6040_probe(struct i2c_client *client, return 0; mfd_err: - free_irq(twl6040->irq_base + TWL6040_IRQ_TH, twl6040); + free_irq(twl6040->irq_th, twl6040); thirq_err: - free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040); + free_irq(twl6040->irq_ready, twl6040); readyirq_err: - twl6040_irq_exit(twl6040); + regmap_del_irq_chip(twl6040->irq, twl6040->irq_data); irq_init_err: if (gpio_is_valid(twl6040->audpwron)) gpio_free(twl6040->audpwron); @@ -685,9 +710,9 @@ static int __devexit twl6040_remove(struct i2c_client *client) if (gpio_is_valid(twl6040->audpwron)) gpio_free(twl6040->audpwron); - free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040); - free_irq(twl6040->irq_base + TWL6040_IRQ_TH, twl6040); - twl6040_irq_exit(twl6040); + free_irq(twl6040->irq_ready, twl6040); + free_irq(twl6040->irq_th, twl6040); + regmap_del_irq_chip(twl6040->irq, twl6040->irq_data); mfd_remove_devices(&client->dev); i2c_set_clientdata(client, NULL); diff --git a/drivers/mfd/twl6040-irq.c b/drivers/mfd/twl6040-irq.c deleted file mode 100644 index 4b42543da228..000000000000 --- a/drivers/mfd/twl6040-irq.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Interrupt controller support for TWL6040 - * - * Author: Misael Lopez Cruz - * - * Copyright: (C) 2011 Texas Instruments, Inc. - * - * 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. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct twl6040_irq_data { - int mask; - int status; -}; - -static struct twl6040_irq_data twl6040_irqs[] = { - { - .mask = TWL6040_THMSK, - .status = TWL6040_THINT, - }, - { - .mask = TWL6040_PLUGMSK, - .status = TWL6040_PLUGINT | TWL6040_UNPLUGINT, - }, - { - .mask = TWL6040_HOOKMSK, - .status = TWL6040_HOOKINT, - }, - { - .mask = TWL6040_HFMSK, - .status = TWL6040_HFINT, - }, - { - .mask = TWL6040_VIBMSK, - .status = TWL6040_VIBINT, - }, - { - .mask = TWL6040_READYMSK, - .status = TWL6040_READYINT, - }, -}; - -static inline -struct twl6040_irq_data *irq_to_twl6040_irq(struct twl6040 *twl6040, - int irq) -{ - return &twl6040_irqs[irq - twl6040->irq_base]; -} - -static void twl6040_irq_lock(struct irq_data *data) -{ - struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data); - - mutex_lock(&twl6040->irq_mutex); -} - -static void twl6040_irq_sync_unlock(struct irq_data *data) -{ - struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data); - - /* write back to hardware any change in irq mask */ - if (twl6040->irq_masks_cur != twl6040->irq_masks_cache) { - twl6040->irq_masks_cache = twl6040->irq_masks_cur; - twl6040_reg_write(twl6040, TWL6040_REG_INTMR, - twl6040->irq_masks_cur); - } - - mutex_unlock(&twl6040->irq_mutex); -} - -static void twl6040_irq_enable(struct irq_data *data) -{ - struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data); - struct twl6040_irq_data *irq_data = irq_to_twl6040_irq(twl6040, - data->irq); - - twl6040->irq_masks_cur &= ~irq_data->mask; -} - -static void twl6040_irq_disable(struct irq_data *data) -{ - struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data); - struct twl6040_irq_data *irq_data = irq_to_twl6040_irq(twl6040, - data->irq); - - twl6040->irq_masks_cur |= irq_data->mask; -} - -static struct irq_chip twl6040_irq_chip = { - .name = "twl6040", - .irq_bus_lock = twl6040_irq_lock, - .irq_bus_sync_unlock = twl6040_irq_sync_unlock, - .irq_enable = twl6040_irq_enable, - .irq_disable = twl6040_irq_disable, -}; - -static irqreturn_t twl6040_irq_thread(int irq, void *data) -{ - struct twl6040 *twl6040 = data; - u8 intid; - int i; - - intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); - - /* apply masking and report (backwards to handle READYINT first) */ - for (i = ARRAY_SIZE(twl6040_irqs) - 1; i >= 0; i--) { - if (twl6040->irq_masks_cur & twl6040_irqs[i].mask) - intid &= ~twl6040_irqs[i].status; - if (intid & twl6040_irqs[i].status) - handle_nested_irq(twl6040->irq_base + i); - } - - /* ack unmasked irqs */ - twl6040_reg_write(twl6040, TWL6040_REG_INTID, intid); - - return IRQ_HANDLED; -} - -int twl6040_irq_init(struct twl6040 *twl6040) -{ - struct device_node *node = twl6040->dev->of_node; - int i, nr_irqs, irq_base, ret; - u8 val; - - mutex_init(&twl6040->irq_mutex); - - /* mask the individual interrupt sources */ - twl6040->irq_masks_cur = TWL6040_ALLINT_MSK; - twl6040->irq_masks_cache = TWL6040_ALLINT_MSK; - twl6040_reg_write(twl6040, TWL6040_REG_INTMR, TWL6040_ALLINT_MSK); - - nr_irqs = ARRAY_SIZE(twl6040_irqs); - - irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); - if (IS_ERR_VALUE(irq_base)) { - dev_err(twl6040->dev, "Fail to allocate IRQ descs\n"); - return irq_base; - } - twl6040->irq_base = irq_base; - - irq_domain_add_legacy(node, ARRAY_SIZE(twl6040_irqs), irq_base, 0, - &irq_domain_simple_ops, NULL); - - /* Register them with genirq */ - for (i = irq_base; i < irq_base + nr_irqs; i++) { - irq_set_chip_data(i, twl6040); - irq_set_chip_and_handler(i, &twl6040_irq_chip, - handle_level_irq); - irq_set_nested_thread(i, 1); - - /* ARM needs us to explicitly flag the IRQ as valid - * and will set them noprobe when we do so. */ -#ifdef CONFIG_ARM - set_irq_flags(i, IRQF_VALID); -#else - irq_set_noprobe(i); -#endif - } - - ret = request_threaded_irq(twl6040->irq, NULL, twl6040_irq_thread, - IRQF_ONESHOT, "twl6040", twl6040); - if (ret) { - dev_err(twl6040->dev, "failed to request IRQ %d: %d\n", - twl6040->irq, ret); - return ret; - } - - /* reset interrupts */ - val = twl6040_reg_read(twl6040, TWL6040_REG_INTID); - - /* interrupts cleared on write */ - twl6040_clear_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_INTCLRMODE); - - return 0; -} -EXPORT_SYMBOL(twl6040_irq_init); - -void twl6040_irq_exit(struct twl6040 *twl6040) -{ - free_irq(twl6040->irq, twl6040); -} -EXPORT_SYMBOL(twl6040_irq_exit); diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index a8eff4ad9be5..94ac944d12f0 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -207,10 +207,12 @@ struct twl6040_platform_data { }; struct regmap; +struct regmap_irq_chips_data; struct twl6040 { struct device *dev; struct regmap *regmap; + struct regmap_irq_chip_data *irq_data; struct regulator_bulk_data supplies[2]; /* supplies for vio, v2v1 */ struct mutex mutex; struct mutex irq_mutex; @@ -228,9 +230,8 @@ struct twl6040 { unsigned int mclk; unsigned int irq; - unsigned int irq_base; - u8 irq_masks_cur; - u8 irq_masks_cache; + unsigned int irq_ready; + unsigned int irq_th; }; int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg); @@ -245,8 +246,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, unsigned int freq_in, unsigned int freq_out); int twl6040_get_pll(struct twl6040 *twl6040); unsigned int twl6040_get_sysclk(struct twl6040 *twl6040); -int twl6040_irq_init(struct twl6040 *twl6040); -void twl6040_irq_exit(struct twl6040 *twl6040); + /* Get the combined status of the vibra control register */ int twl6040_get_vibralr_status(struct twl6040 *twl6040); From 210afeecd41b97db8ad096a778243cdcec9f1189 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 11 Oct 2012 13:55:33 +0200 Subject: [PATCH 030/119] mfd: twl6040: Rename the core driver After the regmap_irq conversion there is no need to call the driver as twl6040-core.c since there is only one c file remained. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/Makefile | 2 +- drivers/mfd/{twl6040-core.c => twl6040.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename drivers/mfd/{twl6040-core.c => twl6040.c} (100%) diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 05bebf66ccd2..8a68fc7ea870 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -67,7 +67,7 @@ obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o -obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o +obj-$(CONFIG_TWL6040_CORE) += twl6040.o obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040.c similarity index 100% rename from drivers/mfd/twl6040-core.c rename to drivers/mfd/twl6040.c From 605511a848ae3ac4b2ce272ae6cbf8930b29ebb3 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 13 Nov 2012 19:18:05 +0530 Subject: [PATCH 031/119] mfd: Convert tps6586x to irq_domain Allocate the irq base if it base is not porvided i.e. in case of device tree invocation of this driver. Convert the tps6586x driver to irq domain, using a legacy IRQ mapping if an irq_base is specified in platform data or dynamically allocated and otherwise using a linear mapping. Signed-off-by: Laxman Dewangan Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/tps6586x.c | 91 ++++++++++++++++++++++++++---------- include/linux/mfd/tps6586x.h | 1 + 2 files changed, 67 insertions(+), 25 deletions(-) diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index 467464368773..2cdf1e6c00c8 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -17,12 +17,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -116,6 +118,7 @@ struct tps6586x { int irq_base; u32 irq_en; u8 mask_reg[5]; + struct irq_domain *irq_domain; }; static inline struct tps6586x *dev_to_tps6586x(struct device *dev) @@ -184,6 +187,14 @@ int tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask) } EXPORT_SYMBOL_GPL(tps6586x_update); +int tps6586x_irq_get_virq(struct device *dev, int irq) +{ + struct tps6586x *tps6586x = dev_to_tps6586x(dev); + + return irq_create_mapping(tps6586x->irq_domain, irq); +} +EXPORT_SYMBOL_GPL(tps6586x_irq_get_virq); + static int __remove_subdev(struct device *dev, void *unused) { platform_device_unregister(to_platform_device(dev)); @@ -205,7 +216,7 @@ static void tps6586x_irq_lock(struct irq_data *data) static void tps6586x_irq_enable(struct irq_data *irq_data) { struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data); - unsigned int __irq = irq_data->irq - tps6586x->irq_base; + unsigned int __irq = irq_data->hwirq; const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq]; tps6586x->mask_reg[data->mask_reg] &= ~data->mask_mask; @@ -216,7 +227,7 @@ static void tps6586x_irq_disable(struct irq_data *irq_data) { struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data); - unsigned int __irq = irq_data->irq - tps6586x->irq_base; + unsigned int __irq = irq_data->hwirq; const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq]; tps6586x->mask_reg[data->mask_reg] |= data->mask_mask; @@ -239,6 +250,39 @@ static void tps6586x_irq_sync_unlock(struct irq_data *data) mutex_unlock(&tps6586x->irq_lock); } +static struct irq_chip tps6586x_irq_chip = { + .name = "tps6586x", + .irq_bus_lock = tps6586x_irq_lock, + .irq_bus_sync_unlock = tps6586x_irq_sync_unlock, + .irq_disable = tps6586x_irq_disable, + .irq_enable = tps6586x_irq_enable, +}; + +static int tps6586x_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct tps6586x *tps6586x = h->host_data; + + irq_set_chip_data(virq, tps6586x); + irq_set_chip_and_handler(virq, &tps6586x_irq_chip, handle_simple_irq); + irq_set_nested_thread(virq, 1); + + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(virq, IRQF_VALID); +#else + irq_set_noprobe(virq); +#endif + + return 0; +} + +static struct irq_domain_ops tps6586x_domain_ops = { + .map = tps6586x_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + static irqreturn_t tps6586x_irq(int irq, void *data) { struct tps6586x *tps6586x = data; @@ -259,7 +303,8 @@ static irqreturn_t tps6586x_irq(int irq, void *data) int i = __ffs(acks); if (tps6586x->irq_en & (1 << i)) - handle_nested_irq(tps6586x->irq_base + i); + handle_nested_irq( + irq_find_mapping(tps6586x->irq_domain, i)); acks &= ~(1 << i); } @@ -272,11 +317,8 @@ static int __devinit tps6586x_irq_init(struct tps6586x *tps6586x, int irq, { int i, ret; u8 tmp[4]; - - if (!irq_base) { - dev_warn(tps6586x->dev, "No interrupt support on IRQ base\n"); - return -EINVAL; - } + int new_irq_base; + int irq_num = ARRAY_SIZE(tps6586x_irqs); mutex_init(&tps6586x->irq_lock); for (i = 0; i < 5; i++) { @@ -286,25 +328,24 @@ static int __devinit tps6586x_irq_init(struct tps6586x *tps6586x, int irq, tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1, sizeof(tmp), tmp); - tps6586x->irq_base = irq_base; - - tps6586x->irq_chip.name = "tps6586x"; - tps6586x->irq_chip.irq_enable = tps6586x_irq_enable; - tps6586x->irq_chip.irq_disable = tps6586x_irq_disable; - tps6586x->irq_chip.irq_bus_lock = tps6586x_irq_lock; - tps6586x->irq_chip.irq_bus_sync_unlock = tps6586x_irq_sync_unlock; - - for (i = 0; i < ARRAY_SIZE(tps6586x_irqs); i++) { - int __irq = i + tps6586x->irq_base; - irq_set_chip_data(__irq, tps6586x); - irq_set_chip_and_handler(__irq, &tps6586x->irq_chip, - handle_simple_irq); - irq_set_nested_thread(__irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(__irq, IRQF_VALID); -#endif + if (irq_base > 0) { + new_irq_base = irq_alloc_descs(irq_base, 0, irq_num, -1); + if (new_irq_base < 0) { + dev_err(tps6586x->dev, + "Failed to alloc IRQs: %d\n", new_irq_base); + return new_irq_base; + } + } else { + new_irq_base = 0; } + tps6586x->irq_domain = irq_domain_add_simple(tps6586x->dev->of_node, + irq_num, new_irq_base, &tps6586x_domain_ops, + tps6586x); + if (!tps6586x->irq_domain) { + dev_err(tps6586x->dev, "Failed to create IRQ domain\n"); + return -ENOMEM; + } ret = request_threaded_irq(irq, NULL, tps6586x_irq, IRQF_ONESHOT, "tps6586x", tps6586x); diff --git a/include/linux/mfd/tps6586x.h b/include/linux/mfd/tps6586x.h index 2dd123194958..ebd8b08a386b 100644 --- a/include/linux/mfd/tps6586x.h +++ b/include/linux/mfd/tps6586x.h @@ -93,5 +93,6 @@ extern int tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask); extern int tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask); extern int tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask); +extern int tps6586x_irq_get_virq(struct device *dev, int irq); #endif /*__LINUX_MFD_TPS6586X_H */ From 5b8b1fe2da98dbc4a10b9ad6d0f9f84e2815d4e4 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 13 Nov 2012 19:18:06 +0530 Subject: [PATCH 032/119] mfd: Add irq io-resource for tps6586x rtc sub driver Add IRQ IORESOURCE for rtc sub driver of this device. The rtc driver can get the irq by calling platform_get_irq(). Signed-off-by: Laxman Dewangan Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/tps6586x.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index 2cdf1e6c00c8..c11539a80fc5 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -96,12 +96,22 @@ static const struct tps6586x_irq_data tps6586x_irqs[] = { [TPS6586X_INT_RTC_ALM2] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 1), }; +static struct resource tps6586x_rtc_resources[] = { + { + .start = TPS6586X_INT_RTC_ALM1, + .end = TPS6586X_INT_RTC_ALM1, + .flags = IORESOURCE_IRQ, + }, +}; + static struct mfd_cell tps6586x_cell[] = { { .name = "tps6586x-gpio", }, { .name = "tps6586x-rtc", + .num_resources = ARRAY_SIZE(tps6586x_rtc_resources), + .resources = &tps6586x_rtc_resources[0], }, { .name = "tps6586x-onkey", @@ -562,7 +572,7 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client, ret = mfd_add_devices(tps6586x->dev, -1, tps6586x_cell, ARRAY_SIZE(tps6586x_cell), - NULL, 0, NULL); + NULL, 0, tps6586x->irq_domain); if (ret < 0) { dev_err(&client->dev, "mfd_add_devices failed: %d\n", ret); goto err_mfd_add; From fe39f2f4dc8481d3aa4561ba649b0c0a4429a49e Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 13 Nov 2012 19:18:07 +0530 Subject: [PATCH 033/119] mfd: Implement tps6586x gpio_to_irq The TPS6586x adds the interrupt of this device using linear mapping on irq domain. Hence, implement gpio_to_irq to get the irq number corresponding to TPS6586x GPIOs which is created dynamically. Signed-off-by: Laxman Dewangan Reviewed-by: Mark Brown Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/gpio/gpio-tps6586x.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpio/gpio-tps6586x.c b/drivers/gpio/gpio-tps6586x.c index 2526b3bb0fae..62e9e1cb3bc1 100644 --- a/drivers/gpio/gpio-tps6586x.c +++ b/drivers/gpio/gpio-tps6586x.c @@ -80,6 +80,14 @@ static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset, val, mask); } +static int tps6586x_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc); + + return tps6586x_irq_get_virq(tps6586x_gpio->parent, + TPS6586X_INT_PLDO_0 + offset); +} + static int __devinit tps6586x_gpio_probe(struct platform_device *pdev) { struct tps6586x_platform_data *pdata; @@ -106,6 +114,7 @@ static int __devinit tps6586x_gpio_probe(struct platform_device *pdev) tps6586x_gpio->gpio_chip.direction_output = tps6586x_gpio_output; tps6586x_gpio->gpio_chip.set = tps6586x_gpio_set; tps6586x_gpio->gpio_chip.get = tps6586x_gpio_get; + tps6586x_gpio->gpio_chip.to_irq = tps6586x_gpio_to_irq; #ifdef CONFIG_OF_GPIO tps6586x_gpio->gpio_chip.of_node = pdev->dev.parent->of_node; From 10ecb80e8cb450f5b10c9aff168842c9a3c949ef Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 13 Nov 2012 19:33:56 +0530 Subject: [PATCH 034/119] mfd: tps65910: Initialize mfd devices after all initialization done Add sub devices of tps65910 after all initialization like interrupt, clock etc. is done. This will make sure that require data gets initialized properly before sub devices probe's get called. Signed-off-by: Laxman Dewangan Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65910.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index 0d79ce2b5014..27fbbe510101 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -279,14 +279,6 @@ static __devinit int tps65910_i2c_probe(struct i2c_client *i2c, return ret; } - ret = mfd_add_devices(tps65910->dev, -1, - tps65910s, ARRAY_SIZE(tps65910s), - NULL, 0, NULL); - if (ret < 0) { - dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret); - return ret; - } - init_data->irq = pmic_plat_data->irq; init_data->irq_base = pmic_plat_data->irq_base; @@ -299,6 +291,14 @@ static __devinit int tps65910_i2c_probe(struct i2c_client *i2c, pm_power_off = tps65910_power_off; } + ret = mfd_add_devices(tps65910->dev, -1, + tps65910s, ARRAY_SIZE(tps65910s), + NULL, 0, NULL); + if (ret < 0) { + dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret); + return ret; + } + return ret; } From 43c1af0f4861b721def8c67ed6af2a69a4efcca3 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 13 Nov 2012 19:33:57 +0530 Subject: [PATCH 035/119] mfd: tps65910: Use regmap irq framework for interrupt support Implement irq support of tps65910 with regmap irq framework in place of implementing locally. This reduces the code size significantly and easy to maintain. Signed-off-by: Laxman Dewangan Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65910-irq.c | 371 +++++++++++++++++------------------ include/linux/mfd/tps65910.h | 137 +++++++++---- 2 files changed, 275 insertions(+), 233 deletions(-) diff --git a/drivers/mfd/tps65910-irq.c b/drivers/mfd/tps65910-irq.c index 09aab3e4776d..554543a584a1 100644 --- a/drivers/mfd/tps65910-irq.c +++ b/drivers/mfd/tps65910-irq.c @@ -24,171 +24,184 @@ #include #include -/* - * This is a threaded IRQ handler so can access I2C/SPI. Since all - * interrupts are clear on read the IRQ line will be reasserted and - * the physical IRQ will be handled again if another interrupt is - * asserted while we run - in the normal course of events this is a - * rare occurrence so we save I2C/SPI reads. We're also assuming that - * it's rare to get lots of interrupts firing simultaneously so try to - * minimise I/O. - */ -static irqreturn_t tps65910_irq(int irq, void *irq_data) -{ - struct tps65910 *tps65910 = irq_data; - unsigned int reg; - u32 irq_sts; - u32 irq_mask; - int i; - tps65910_reg_read(tps65910, TPS65910_INT_STS, ®); - irq_sts = reg; - tps65910_reg_read(tps65910, TPS65910_INT_STS2, ®); - irq_sts |= reg << 8; - switch (tps65910_chip_id(tps65910)) { - case TPS65911: - tps65910_reg_read(tps65910, TPS65910_INT_STS3, ®); - irq_sts |= reg << 16; - } +static const struct regmap_irq tps65911_irqs[] = { + /* INT_STS */ + [TPS65911_IRQ_PWRHOLD_F] = { + .mask = INT_MSK_PWRHOLD_F_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_VBAT_VMHI] = { + .mask = INT_MSK_VMBHI_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_PWRON] = { + .mask = INT_MSK_PWRON_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_PWRON_LP] = { + .mask = INT_MSK_PWRON_LP_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_PWRHOLD_R] = { + .mask = INT_MSK_PWRHOLD_R_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_HOTDIE] = { + .mask = INT_MSK_HOTDIE_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_RTC_ALARM] = { + .mask = INT_MSK_RTC_ALARM_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_RTC_PERIOD] = { + .mask = INT_MSK_RTC_PERIOD_IT_MSK_MASK, + .reg_offset = 0, + }, - tps65910_reg_read(tps65910, TPS65910_INT_MSK, ®); - irq_mask = reg; - tps65910_reg_read(tps65910, TPS65910_INT_MSK2, ®); - irq_mask |= reg << 8; - switch (tps65910_chip_id(tps65910)) { - case TPS65911: - tps65910_reg_read(tps65910, TPS65910_INT_MSK3, ®); - irq_mask |= reg << 16; - } + /* INT_STS2 */ + [TPS65911_IRQ_GPIO0_R] = { + .mask = INT_MSK2_GPIO0_R_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO0_F] = { + .mask = INT_MSK2_GPIO0_F_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO1_R] = { + .mask = INT_MSK2_GPIO1_R_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO1_F] = { + .mask = INT_MSK2_GPIO1_F_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO2_R] = { + .mask = INT_MSK2_GPIO2_R_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO2_F] = { + .mask = INT_MSK2_GPIO2_F_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO3_R] = { + .mask = INT_MSK2_GPIO3_R_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO3_F] = { + .mask = INT_MSK2_GPIO3_F_IT_MSK_MASK, + .reg_offset = 1, + }, - irq_sts &= ~irq_mask; - - if (!irq_sts) - return IRQ_NONE; - - for (i = 0; i < tps65910->irq_num; i++) { - - if (!(irq_sts & (1 << i))) - continue; - - handle_nested_irq(irq_find_mapping(tps65910->domain, i)); - } - - /* Write the STS register back to clear IRQs we handled */ - reg = irq_sts & 0xFF; - irq_sts >>= 8; - tps65910_reg_write(tps65910, TPS65910_INT_STS, reg); - reg = irq_sts & 0xFF; - tps65910_reg_write(tps65910, TPS65910_INT_STS2, reg); - switch (tps65910_chip_id(tps65910)) { - case TPS65911: - reg = irq_sts >> 8; - tps65910_reg_write(tps65910, TPS65910_INT_STS3, reg); - } - - return IRQ_HANDLED; -} - -static void tps65910_irq_lock(struct irq_data *data) -{ - struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); - - mutex_lock(&tps65910->irq_lock); -} - -static void tps65910_irq_sync_unlock(struct irq_data *data) -{ - struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); - u32 reg_mask; - unsigned int reg; - - tps65910_reg_read(tps65910, TPS65910_INT_MSK, ®); - reg_mask = reg; - tps65910_reg_read(tps65910, TPS65910_INT_MSK2, ®); - reg_mask |= reg << 8; - switch (tps65910_chip_id(tps65910)) { - case TPS65911: - tps65910_reg_read(tps65910, TPS65910_INT_MSK3, ®); - reg_mask |= reg << 16; - } - - if (tps65910->irq_mask != reg_mask) { - reg = tps65910->irq_mask & 0xFF; - tps65910_reg_write(tps65910, TPS65910_INT_MSK, reg); - reg = tps65910->irq_mask >> 8 & 0xFF; - tps65910_reg_write(tps65910, TPS65910_INT_MSK2, reg); - switch (tps65910_chip_id(tps65910)) { - case TPS65911: - reg = tps65910->irq_mask >> 16; - tps65910_reg_write(tps65910, TPS65910_INT_MSK3, reg); - } - } - mutex_unlock(&tps65910->irq_lock); -} - -static void tps65910_irq_enable(struct irq_data *data) -{ - struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); - - tps65910->irq_mask &= ~(1 << data->hwirq); -} - -static void tps65910_irq_disable(struct irq_data *data) -{ - struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); - - tps65910->irq_mask |= (1 << data->hwirq); -} - -#ifdef CONFIG_PM_SLEEP -static int tps65910_irq_set_wake(struct irq_data *data, unsigned int enable) -{ - struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); - return irq_set_irq_wake(tps65910->chip_irq, enable); -} -#else -#define tps65910_irq_set_wake NULL -#endif - -static struct irq_chip tps65910_irq_chip = { - .name = "tps65910", - .irq_bus_lock = tps65910_irq_lock, - .irq_bus_sync_unlock = tps65910_irq_sync_unlock, - .irq_disable = tps65910_irq_disable, - .irq_enable = tps65910_irq_enable, - .irq_set_wake = tps65910_irq_set_wake, + /* INT_STS3 */ + [TPS65911_IRQ_GPIO4_R] = { + .mask = INT_MSK3_GPIO4_R_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_GPIO4_F] = { + .mask = INT_MSK3_GPIO4_F_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_GPIO5_R] = { + .mask = INT_MSK3_GPIO5_R_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_GPIO5_F] = { + .mask = INT_MSK3_GPIO5_F_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_WTCHDG] = { + .mask = INT_MSK3_WTCHDG_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_VMBCH2_H] = { + .mask = INT_MSK3_VMBCH2_H_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_VMBCH2_L] = { + .mask = INT_MSK3_VMBCH2_L_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_PWRDN] = { + .mask = INT_MSK3_PWRDN_IT_MSK_MASK, + .reg_offset = 2, + }, }; -static int tps65910_irq_map(struct irq_domain *h, unsigned int virq, - irq_hw_number_t hw) -{ - struct tps65910 *tps65910 = h->host_data; +static const struct regmap_irq tps65910_irqs[] = { + /* INT_STS */ + [TPS65910_IRQ_VBAT_VMBDCH] = { + .mask = TPS65910_INT_MSK_VMBDCH_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_VBAT_VMHI] = { + .mask = TPS65910_INT_MSK_VMBHI_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_PWRON] = { + .mask = TPS65910_INT_MSK_PWRON_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_PWRON_LP] = { + .mask = TPS65910_INT_MSK_PWRON_LP_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_PWRHOLD] = { + .mask = TPS65910_INT_MSK_PWRHOLD_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_HOTDIE] = { + .mask = TPS65910_INT_MSK_HOTDIE_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_RTC_ALARM] = { + .mask = TPS65910_INT_MSK_RTC_ALARM_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_RTC_PERIOD] = { + .mask = TPS65910_INT_MSK_RTC_PERIOD_IT_MSK_MASK, + .reg_offset = 0, + }, - irq_set_chip_data(virq, tps65910); - irq_set_chip_and_handler(virq, &tps65910_irq_chip, handle_edge_irq); - irq_set_nested_thread(virq, 1); + /* INT_STS2 */ + [TPS65910_IRQ_GPIO_R] = { + .mask = TPS65910_INT_MSK2_GPIO0_F_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65910_IRQ_GPIO_F] = { + .mask = TPS65910_INT_MSK2_GPIO0_R_IT_MSK_MASK, + .reg_offset = 1, + }, +}; - /* ARM needs us to explicitly flag the IRQ as valid - * and will set them noprobe when we do so. */ -#ifdef CONFIG_ARM - set_irq_flags(virq, IRQF_VALID); -#else - irq_set_noprobe(virq); -#endif +static struct regmap_irq_chip tps65911_irq_chip = { + .name = "tps65910", + .irqs = tps65911_irqs, + .num_irqs = ARRAY_SIZE(tps65911_irqs), + .num_regs = 3, + .irq_reg_stride = 2, + .status_base = TPS65910_INT_STS, + .mask_base = TPS65910_INT_MSK, + .ack_base = TPS65910_INT_MSK, +}; - return 0; -} - -static struct irq_domain_ops tps65910_domain_ops = { - .map = tps65910_irq_map, - .xlate = irq_domain_xlate_twocell, +static struct regmap_irq_chip tps65910_irq_chip = { + .name = "tps65910", + .irqs = tps65910_irqs, + .num_irqs = ARRAY_SIZE(tps65910_irqs), + .num_regs = 2, + .irq_reg_stride = 2, + .status_base = TPS65910_INT_STS, + .mask_base = TPS65910_INT_MSK, + .ack_base = TPS65910_INT_MSK, }; int tps65910_irq_init(struct tps65910 *tps65910, int irq, struct tps65910_platform_data *pdata) { - int ret; - int flags = IRQF_ONESHOT; + int ret = 0; + static struct regmap_irq_chip *tps6591x_irqs_chip; if (!irq) { dev_warn(tps65910->dev, "No interrupt support, no core IRQ\n"); @@ -200,61 +213,31 @@ int tps65910_irq_init(struct tps65910 *tps65910, int irq, return -EINVAL; } + switch (tps65910_chip_id(tps65910)) { case TPS65910: - tps65910->irq_num = TPS65910_NUM_IRQ; + tps6591x_irqs_chip = &tps65910_irq_chip; break; case TPS65911: - tps65910->irq_num = TPS65911_NUM_IRQ; + tps6591x_irqs_chip = &tps65911_irq_chip; break; } - if (pdata->irq_base > 0) { - pdata->irq_base = irq_alloc_descs(pdata->irq_base, 0, - tps65910->irq_num, -1); - if (pdata->irq_base < 0) { - dev_warn(tps65910->dev, "Failed to alloc IRQs: %d\n", - pdata->irq_base); - return pdata->irq_base; - } - } - - tps65910->irq_mask = 0xFFFFFF; - - mutex_init(&tps65910->irq_lock); tps65910->chip_irq = irq; - tps65910->irq_base = pdata->irq_base; - - if (pdata->irq_base > 0) - tps65910->domain = irq_domain_add_legacy(tps65910->dev->of_node, - tps65910->irq_num, - pdata->irq_base, - 0, - &tps65910_domain_ops, tps65910); - else - tps65910->domain = irq_domain_add_linear(tps65910->dev->of_node, - tps65910->irq_num, - &tps65910_domain_ops, tps65910); - - if (!tps65910->domain) { - dev_err(tps65910->dev, "Failed to create IRQ domain\n"); - return -ENOMEM; + ret = regmap_add_irq_chip(tps65910->regmap, tps65910->chip_irq, + IRQF_ONESHOT, pdata->irq_base, + tps6591x_irqs_chip, &tps65910->irq_data); + if (ret < 0) { + dev_warn(tps65910->dev, + "Failed to add irq_chip %d\n", ret); + return ret; } - - ret = request_threaded_irq(irq, NULL, tps65910_irq, flags, - "tps65910", tps65910); - - irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); - - if (ret != 0) - dev_err(tps65910->dev, "Failed to request IRQ: %d\n", ret); - return ret; } int tps65910_irq_exit(struct tps65910 *tps65910) { - if (tps65910->chip_irq) - free_irq(tps65910->chip_irq, tps65910); + if (tps65910->chip_irq > 0) + regmap_del_irq_chip(tps65910->chip_irq, tps65910->irq_data); return 0; } diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h index 02e894f3ff45..b564ac29590a 100644 --- a/include/linux/mfd/tps65910.h +++ b/include/linux/mfd/tps65910.h @@ -572,6 +572,49 @@ #define SPARE_SPARE_MASK 0xFF #define SPARE_SPARE_SHIFT 0 +#define TPS65910_INT_STS_RTC_PERIOD_IT_MASK 0x80 +#define TPS65910_INT_STS_RTC_PERIOD_IT_SHIFT 7 +#define TPS65910_INT_STS_RTC_ALARM_IT_MASK 0x40 +#define TPS65910_INT_STS_RTC_ALARM_IT_SHIFT 6 +#define TPS65910_INT_STS_HOTDIE_IT_MASK 0x20 +#define TPS65910_INT_STS_HOTDIE_IT_SHIFT 5 +#define TPS65910_INT_STS_PWRHOLD_F_IT_MASK 0x10 +#define TPS65910_INT_STS_PWRHOLD_F_IT_SHIFT 4 +#define TPS65910_INT_STS_PWRON_LP_IT_MASK 0x08 +#define TPS65910_INT_STS_PWRON_LP_IT_SHIFT 3 +#define TPS65910_INT_STS_PWRON_IT_MASK 0x04 +#define TPS65910_INT_STS_PWRON_IT_SHIFT 2 +#define TPS65910_INT_STS_VMBHI_IT_MASK 0x02 +#define TPS65910_INT_STS_VMBHI_IT_SHIFT 1 +#define TPS65910_INT_STS_VMBDCH_IT_MASK 0x01 +#define TPS65910_INT_STS_VMBDCH_IT_SHIFT 0 + +#define TPS65910_INT_MSK_RTC_PERIOD_IT_MSK_MASK 0x80 +#define TPS65910_INT_MSK_RTC_PERIOD_IT_MSK_SHIFT 7 +#define TPS65910_INT_MSK_RTC_ALARM_IT_MSK_MASK 0x40 +#define TPS65910_INT_MSK_RTC_ALARM_IT_MSK_SHIFT 6 +#define TPS65910_INT_MSK_HOTDIE_IT_MSK_MASK 0x20 +#define TPS65910_INT_MSK_HOTDIE_IT_MSK_SHIFT 5 +#define TPS65910_INT_MSK_PWRHOLD_IT_MSK_MASK 0x10 +#define TPS65910_INT_MSK_PWRHOLD_IT_MSK_SHIFT 4 +#define TPS65910_INT_MSK_PWRON_LP_IT_MSK_MASK 0x08 +#define TPS65910_INT_MSK_PWRON_LP_IT_MSK_SHIFT 3 +#define TPS65910_INT_MSK_PWRON_IT_MSK_MASK 0x04 +#define TPS65910_INT_MSK_PWRON_IT_MSK_SHIFT 2 +#define TPS65910_INT_MSK_VMBHI_IT_MSK_MASK 0x02 +#define TPS65910_INT_MSK_VMBHI_IT_MSK_SHIFT 1 +#define TPS65910_INT_MSK_VMBDCH_IT_MSK_MASK 0x01 +#define TPS65910_INT_MSK_VMBDCH_IT_MSK_SHIFT 0 + +#define TPS65910_INT_STS2_GPIO0_F_IT_SHIFT 2 +#define TPS65910_INT_STS2_GPIO0_F_IT_MASK 0x02 +#define TPS65910_INT_STS2_GPIO0_R_IT_SHIFT 1 +#define TPS65910_INT_STS2_GPIO0_R_IT_MASK 0x01 + +#define TPS65910_INT_MSK2_GPIO0_F_IT_MSK_SHIFT 2 +#define TPS65910_INT_MSK2_GPIO0_F_IT_MSK_MASK 0x02 +#define TPS65910_INT_MSK2_GPIO0_R_IT_MSK_SHIFT 1 +#define TPS65910_INT_MSK2_GPIO0_R_IT_MSK_MASK 0x01 /*Register INT_STS (0x80) register.RegisterDescription */ #define INT_STS_RTC_PERIOD_IT_MASK 0x80 @@ -580,16 +623,16 @@ #define INT_STS_RTC_ALARM_IT_SHIFT 6 #define INT_STS_HOTDIE_IT_MASK 0x20 #define INT_STS_HOTDIE_IT_SHIFT 5 -#define INT_STS_PWRHOLD_IT_MASK 0x10 -#define INT_STS_PWRHOLD_IT_SHIFT 4 +#define INT_STS_PWRHOLD_R_IT_MASK 0x10 +#define INT_STS_PWRHOLD_R_IT_SHIFT 4 #define INT_STS_PWRON_LP_IT_MASK 0x08 #define INT_STS_PWRON_LP_IT_SHIFT 3 #define INT_STS_PWRON_IT_MASK 0x04 #define INT_STS_PWRON_IT_SHIFT 2 #define INT_STS_VMBHI_IT_MASK 0x02 #define INT_STS_VMBHI_IT_SHIFT 1 -#define INT_STS_VMBDCH_IT_MASK 0x01 -#define INT_STS_VMBDCH_IT_SHIFT 0 +#define INT_STS_PWRHOLD_F_IT_MASK 0x01 +#define INT_STS_PWRHOLD_F_IT_SHIFT 0 /*Register INT_MSK (0x80) register.RegisterDescription */ @@ -599,16 +642,16 @@ #define INT_MSK_RTC_ALARM_IT_MSK_SHIFT 6 #define INT_MSK_HOTDIE_IT_MSK_MASK 0x20 #define INT_MSK_HOTDIE_IT_MSK_SHIFT 5 -#define INT_MSK_PWRHOLD_IT_MSK_MASK 0x10 -#define INT_MSK_PWRHOLD_IT_MSK_SHIFT 4 +#define INT_MSK_PWRHOLD_R_IT_MSK_MASK 0x10 +#define INT_MSK_PWRHOLD_R_IT_MSK_SHIFT 4 #define INT_MSK_PWRON_LP_IT_MSK_MASK 0x08 #define INT_MSK_PWRON_LP_IT_MSK_SHIFT 3 #define INT_MSK_PWRON_IT_MSK_MASK 0x04 #define INT_MSK_PWRON_IT_MSK_SHIFT 2 #define INT_MSK_VMBHI_IT_MSK_MASK 0x02 #define INT_MSK_VMBHI_IT_MSK_SHIFT 1 -#define INT_MSK_VMBDCH_IT_MSK_MASK 0x01 -#define INT_MSK_VMBDCH_IT_MSK_SHIFT 0 +#define INT_MSK_PWRHOLD_F_IT_MSK_MASK 0x01 +#define INT_MSK_PWRHOLD_F_IT_MSK_SHIFT 0 /*Register INT_STS2 (0x80) register.RegisterDescription */ @@ -650,6 +693,14 @@ /*Register INT_STS3 (0x80) register.RegisterDescription */ +#define INT_STS3_PWRDN_IT_MASK 0x80 +#define INT_STS3_PWRDN_IT_SHIFT 7 +#define INT_STS3_VMBCH2_L_IT_MASK 0x40 +#define INT_STS3_VMBCH2_L_IT_SHIFT 6 +#define INT_STS3_VMBCH2_H_IT_MASK 0x20 +#define INT_STS3_VMBCH2_H_IT_SHIFT 5 +#define INT_STS3_WTCHDG_IT_MASK 0x10 +#define INT_STS3_WTCHDG_IT_SHIFT 4 #define INT_STS3_GPIO5_F_IT_MASK 0x08 #define INT_STS3_GPIO5_F_IT_SHIFT 3 #define INT_STS3_GPIO5_R_IT_MASK 0x04 @@ -661,6 +712,14 @@ /*Register INT_MSK3 (0x80) register.RegisterDescription */ +#define INT_MSK3_PWRDN_IT_MSK_MASK 0x80 +#define INT_MSK3_PWRDN_IT_MSK_SHIFT 7 +#define INT_MSK3_VMBCH2_L_IT_MSK_MASK 0x40 +#define INT_MSK3_VMBCH2_L_IT_MSK_SHIFT 6 +#define INT_MSK3_VMBCH2_H_IT_MSK_MASK 0x20 +#define INT_MSK3_VMBCH2_H_IT_MSK_SHIFT 5 +#define INT_MSK3_WTCHDG_IT_MSK_MASK 0x10 +#define INT_MSK3_WTCHDG_IT_MSK_SHIFT 4 #define INT_MSK3_GPIO5_F_IT_MSK_MASK 0x08 #define INT_MSK3_GPIO5_F_IT_MSK_SHIFT 3 #define INT_MSK3_GPIO5_R_IT_MSK_MASK 0x04 @@ -721,34 +780,32 @@ #define TPS65910_IRQ_GPIO_F 9 #define TPS65910_NUM_IRQ 10 -#define TPS65911_IRQ_VBAT_VMBDCH 0 -#define TPS65911_IRQ_VBAT_VMBDCH2L 1 -#define TPS65911_IRQ_VBAT_VMBDCH2H 2 -#define TPS65911_IRQ_VBAT_VMHI 3 -#define TPS65911_IRQ_PWRON 4 -#define TPS65911_IRQ_PWRON_LP 5 -#define TPS65911_IRQ_PWRHOLD_F 6 -#define TPS65911_IRQ_PWRHOLD_R 7 -#define TPS65911_IRQ_HOTDIE 8 -#define TPS65911_IRQ_RTC_ALARM 9 -#define TPS65911_IRQ_RTC_PERIOD 10 -#define TPS65911_IRQ_GPIO0_R 11 -#define TPS65911_IRQ_GPIO0_F 12 -#define TPS65911_IRQ_GPIO1_R 13 -#define TPS65911_IRQ_GPIO1_F 14 -#define TPS65911_IRQ_GPIO2_R 15 -#define TPS65911_IRQ_GPIO2_F 16 -#define TPS65911_IRQ_GPIO3_R 17 -#define TPS65911_IRQ_GPIO3_F 18 -#define TPS65911_IRQ_GPIO4_R 19 -#define TPS65911_IRQ_GPIO4_F 20 -#define TPS65911_IRQ_GPIO5_R 21 -#define TPS65911_IRQ_GPIO5_F 22 -#define TPS65911_IRQ_WTCHDG 23 -#define TPS65911_IRQ_PWRDN 24 - -#define TPS65911_NUM_IRQ 25 +#define TPS65911_IRQ_PWRHOLD_F 0 +#define TPS65911_IRQ_VBAT_VMHI 1 +#define TPS65911_IRQ_PWRON 2 +#define TPS65911_IRQ_PWRON_LP 3 +#define TPS65911_IRQ_PWRHOLD_R 4 +#define TPS65911_IRQ_HOTDIE 5 +#define TPS65911_IRQ_RTC_ALARM 6 +#define TPS65911_IRQ_RTC_PERIOD 7 +#define TPS65911_IRQ_GPIO0_R 8 +#define TPS65911_IRQ_GPIO0_F 9 +#define TPS65911_IRQ_GPIO1_R 10 +#define TPS65911_IRQ_GPIO1_F 11 +#define TPS65911_IRQ_GPIO2_R 12 +#define TPS65911_IRQ_GPIO2_F 13 +#define TPS65911_IRQ_GPIO3_R 14 +#define TPS65911_IRQ_GPIO3_F 15 +#define TPS65911_IRQ_GPIO4_R 16 +#define TPS65911_IRQ_GPIO4_F 17 +#define TPS65911_IRQ_GPIO5_R 18 +#define TPS65911_IRQ_GPIO5_F 19 +#define TPS65911_IRQ_WTCHDG 20 +#define TPS65911_IRQ_VMBCH2_H 21 +#define TPS65911_IRQ_VMBCH2_L 22 +#define TPS65911_IRQ_PWRDN 23 +#define TPS65911_NUM_IRQ 24 /* GPIO Register Definitions */ #define TPS65910_GPIO_DEB BIT(2) @@ -848,11 +905,8 @@ struct tps65910 { struct tps65910_board *of_plat_data; /* IRQ Handling */ - struct mutex irq_lock; int chip_irq; - int irq_base; - int irq_num; - u32 irq_mask; + struct regmap_irq_chip_data *irq_data; struct irq_domain *domain; }; @@ -900,4 +954,9 @@ static inline int tps65910_reg_update_bits(struct tps65910 *tps65910, u8 reg, return regmap_update_bits(tps65910->regmap, reg, mask, val); } +static inline int tps65910_irq_get_virq(struct tps65910 *tps65910, int irq) +{ + return regmap_irq_get_virq(tps65910->irq_data, irq); +} + #endif /* __LINUX_MFD_TPS65910_H */ From 4aab3fadad32ff4df05832beff7c16fd6ad938aa Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 13 Nov 2012 19:33:58 +0530 Subject: [PATCH 036/119] mfd: tps65910: Move interrupt implementation code to mfd file In place of implementing the irq support in separate file, moving implementation to main mfd file. The irq files only contains the table and init steps only and does not need extra file to have this only for this purpose. Signed-off-by: Laxman Dewangan Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/Makefile | 2 +- drivers/mfd/tps65910-irq.c | 243 ----------------------------------- drivers/mfd/tps65910.c | 216 +++++++++++++++++++++++++++++++ include/linux/mfd/tps65910.h | 4 - 4 files changed, 217 insertions(+), 248 deletions(-) delete mode 100644 drivers/mfd/tps65910-irq.c diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 8a68fc7ea870..a30c49ecdd95 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -56,7 +56,7 @@ obj-$(CONFIG_TPS6105X) += tps6105x.o obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_TPS6507X) += tps6507x.o obj-$(CONFIG_MFD_TPS65217) += tps65217.o -obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o +obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65912-objs := tps65912-core.o tps65912-irq.o obj-$(CONFIG_MFD_TPS65912) += tps65912.o obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o diff --git a/drivers/mfd/tps65910-irq.c b/drivers/mfd/tps65910-irq.c deleted file mode 100644 index 554543a584a1..000000000000 --- a/drivers/mfd/tps65910-irq.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * tps65910-irq.c -- TI TPS6591x - * - * Copyright 2010 Texas Instruments Inc. - * - * Author: Graeme Gregory - * Author: Jorge Eduardo Candelaria - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -static const struct regmap_irq tps65911_irqs[] = { - /* INT_STS */ - [TPS65911_IRQ_PWRHOLD_F] = { - .mask = INT_MSK_PWRHOLD_F_IT_MSK_MASK, - .reg_offset = 0, - }, - [TPS65911_IRQ_VBAT_VMHI] = { - .mask = INT_MSK_VMBHI_IT_MSK_MASK, - .reg_offset = 0, - }, - [TPS65911_IRQ_PWRON] = { - .mask = INT_MSK_PWRON_IT_MSK_MASK, - .reg_offset = 0, - }, - [TPS65911_IRQ_PWRON_LP] = { - .mask = INT_MSK_PWRON_LP_IT_MSK_MASK, - .reg_offset = 0, - }, - [TPS65911_IRQ_PWRHOLD_R] = { - .mask = INT_MSK_PWRHOLD_R_IT_MSK_MASK, - .reg_offset = 0, - }, - [TPS65911_IRQ_HOTDIE] = { - .mask = INT_MSK_HOTDIE_IT_MSK_MASK, - .reg_offset = 0, - }, - [TPS65911_IRQ_RTC_ALARM] = { - .mask = INT_MSK_RTC_ALARM_IT_MSK_MASK, - .reg_offset = 0, - }, - [TPS65911_IRQ_RTC_PERIOD] = { - .mask = INT_MSK_RTC_PERIOD_IT_MSK_MASK, - .reg_offset = 0, - }, - - /* INT_STS2 */ - [TPS65911_IRQ_GPIO0_R] = { - .mask = INT_MSK2_GPIO0_R_IT_MSK_MASK, - .reg_offset = 1, - }, - [TPS65911_IRQ_GPIO0_F] = { - .mask = INT_MSK2_GPIO0_F_IT_MSK_MASK, - .reg_offset = 1, - }, - [TPS65911_IRQ_GPIO1_R] = { - .mask = INT_MSK2_GPIO1_R_IT_MSK_MASK, - .reg_offset = 1, - }, - [TPS65911_IRQ_GPIO1_F] = { - .mask = INT_MSK2_GPIO1_F_IT_MSK_MASK, - .reg_offset = 1, - }, - [TPS65911_IRQ_GPIO2_R] = { - .mask = INT_MSK2_GPIO2_R_IT_MSK_MASK, - .reg_offset = 1, - }, - [TPS65911_IRQ_GPIO2_F] = { - .mask = INT_MSK2_GPIO2_F_IT_MSK_MASK, - .reg_offset = 1, - }, - [TPS65911_IRQ_GPIO3_R] = { - .mask = INT_MSK2_GPIO3_R_IT_MSK_MASK, - .reg_offset = 1, - }, - [TPS65911_IRQ_GPIO3_F] = { - .mask = INT_MSK2_GPIO3_F_IT_MSK_MASK, - .reg_offset = 1, - }, - - /* INT_STS3 */ - [TPS65911_IRQ_GPIO4_R] = { - .mask = INT_MSK3_GPIO4_R_IT_MSK_MASK, - .reg_offset = 2, - }, - [TPS65911_IRQ_GPIO4_F] = { - .mask = INT_MSK3_GPIO4_F_IT_MSK_MASK, - .reg_offset = 2, - }, - [TPS65911_IRQ_GPIO5_R] = { - .mask = INT_MSK3_GPIO5_R_IT_MSK_MASK, - .reg_offset = 2, - }, - [TPS65911_IRQ_GPIO5_F] = { - .mask = INT_MSK3_GPIO5_F_IT_MSK_MASK, - .reg_offset = 2, - }, - [TPS65911_IRQ_WTCHDG] = { - .mask = INT_MSK3_WTCHDG_IT_MSK_MASK, - .reg_offset = 2, - }, - [TPS65911_IRQ_VMBCH2_H] = { - .mask = INT_MSK3_VMBCH2_H_IT_MSK_MASK, - .reg_offset = 2, - }, - [TPS65911_IRQ_VMBCH2_L] = { - .mask = INT_MSK3_VMBCH2_L_IT_MSK_MASK, - .reg_offset = 2, - }, - [TPS65911_IRQ_PWRDN] = { - .mask = INT_MSK3_PWRDN_IT_MSK_MASK, - .reg_offset = 2, - }, -}; - -static const struct regmap_irq tps65910_irqs[] = { - /* INT_STS */ - [TPS65910_IRQ_VBAT_VMBDCH] = { - .mask = TPS65910_INT_MSK_VMBDCH_IT_MSK_MASK, - .reg_offset = 0, - }, - [TPS65910_IRQ_VBAT_VMHI] = { - .mask = TPS65910_INT_MSK_VMBHI_IT_MSK_MASK, - .reg_offset = 0, - }, - [TPS65910_IRQ_PWRON] = { - .mask = TPS65910_INT_MSK_PWRON_IT_MSK_MASK, - .reg_offset = 0, - }, - [TPS65910_IRQ_PWRON_LP] = { - .mask = TPS65910_INT_MSK_PWRON_LP_IT_MSK_MASK, - .reg_offset = 0, - }, - [TPS65910_IRQ_PWRHOLD] = { - .mask = TPS65910_INT_MSK_PWRHOLD_IT_MSK_MASK, - .reg_offset = 0, - }, - [TPS65910_IRQ_HOTDIE] = { - .mask = TPS65910_INT_MSK_HOTDIE_IT_MSK_MASK, - .reg_offset = 0, - }, - [TPS65910_IRQ_RTC_ALARM] = { - .mask = TPS65910_INT_MSK_RTC_ALARM_IT_MSK_MASK, - .reg_offset = 0, - }, - [TPS65910_IRQ_RTC_PERIOD] = { - .mask = TPS65910_INT_MSK_RTC_PERIOD_IT_MSK_MASK, - .reg_offset = 0, - }, - - /* INT_STS2 */ - [TPS65910_IRQ_GPIO_R] = { - .mask = TPS65910_INT_MSK2_GPIO0_F_IT_MSK_MASK, - .reg_offset = 1, - }, - [TPS65910_IRQ_GPIO_F] = { - .mask = TPS65910_INT_MSK2_GPIO0_R_IT_MSK_MASK, - .reg_offset = 1, - }, -}; - -static struct regmap_irq_chip tps65911_irq_chip = { - .name = "tps65910", - .irqs = tps65911_irqs, - .num_irqs = ARRAY_SIZE(tps65911_irqs), - .num_regs = 3, - .irq_reg_stride = 2, - .status_base = TPS65910_INT_STS, - .mask_base = TPS65910_INT_MSK, - .ack_base = TPS65910_INT_MSK, -}; - -static struct regmap_irq_chip tps65910_irq_chip = { - .name = "tps65910", - .irqs = tps65910_irqs, - .num_irqs = ARRAY_SIZE(tps65910_irqs), - .num_regs = 2, - .irq_reg_stride = 2, - .status_base = TPS65910_INT_STS, - .mask_base = TPS65910_INT_MSK, - .ack_base = TPS65910_INT_MSK, -}; - -int tps65910_irq_init(struct tps65910 *tps65910, int irq, - struct tps65910_platform_data *pdata) -{ - int ret = 0; - static struct regmap_irq_chip *tps6591x_irqs_chip; - - if (!irq) { - dev_warn(tps65910->dev, "No interrupt support, no core IRQ\n"); - return -EINVAL; - } - - if (!pdata) { - dev_warn(tps65910->dev, "No interrupt support, no pdata\n"); - return -EINVAL; - } - - - switch (tps65910_chip_id(tps65910)) { - case TPS65910: - tps6591x_irqs_chip = &tps65910_irq_chip; - break; - case TPS65911: - tps6591x_irqs_chip = &tps65911_irq_chip; - break; - } - - tps65910->chip_irq = irq; - ret = regmap_add_irq_chip(tps65910->regmap, tps65910->chip_irq, - IRQF_ONESHOT, pdata->irq_base, - tps6591x_irqs_chip, &tps65910->irq_data); - if (ret < 0) { - dev_warn(tps65910->dev, - "Failed to add irq_chip %d\n", ret); - return ret; - } - return ret; -} - -int tps65910_irq_exit(struct tps65910 *tps65910) -{ - if (tps65910->chip_irq > 0) - regmap_del_irq_chip(tps65910->chip_irq, tps65910->irq_data); - return 0; -} diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index 27fbbe510101..d4d4eb5b5b6f 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -50,6 +53,219 @@ static struct mfd_cell tps65910s[] = { }; +static const struct regmap_irq tps65911_irqs[] = { + /* INT_STS */ + [TPS65911_IRQ_PWRHOLD_F] = { + .mask = INT_MSK_PWRHOLD_F_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_VBAT_VMHI] = { + .mask = INT_MSK_VMBHI_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_PWRON] = { + .mask = INT_MSK_PWRON_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_PWRON_LP] = { + .mask = INT_MSK_PWRON_LP_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_PWRHOLD_R] = { + .mask = INT_MSK_PWRHOLD_R_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_HOTDIE] = { + .mask = INT_MSK_HOTDIE_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_RTC_ALARM] = { + .mask = INT_MSK_RTC_ALARM_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_RTC_PERIOD] = { + .mask = INT_MSK_RTC_PERIOD_IT_MSK_MASK, + .reg_offset = 0, + }, + + /* INT_STS2 */ + [TPS65911_IRQ_GPIO0_R] = { + .mask = INT_MSK2_GPIO0_R_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO0_F] = { + .mask = INT_MSK2_GPIO0_F_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO1_R] = { + .mask = INT_MSK2_GPIO1_R_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO1_F] = { + .mask = INT_MSK2_GPIO1_F_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO2_R] = { + .mask = INT_MSK2_GPIO2_R_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO2_F] = { + .mask = INT_MSK2_GPIO2_F_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO3_R] = { + .mask = INT_MSK2_GPIO3_R_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO3_F] = { + .mask = INT_MSK2_GPIO3_F_IT_MSK_MASK, + .reg_offset = 1, + }, + + /* INT_STS2 */ + [TPS65911_IRQ_GPIO4_R] = { + .mask = INT_MSK3_GPIO4_R_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_GPIO4_F] = { + .mask = INT_MSK3_GPIO4_F_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_GPIO5_R] = { + .mask = INT_MSK3_GPIO5_R_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_GPIO5_F] = { + .mask = INT_MSK3_GPIO5_F_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_WTCHDG] = { + .mask = INT_MSK3_WTCHDG_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_VMBCH2_H] = { + .mask = INT_MSK3_VMBCH2_H_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_VMBCH2_L] = { + .mask = INT_MSK3_VMBCH2_L_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_PWRDN] = { + .mask = INT_MSK3_PWRDN_IT_MSK_MASK, + .reg_offset = 2, + }, +}; + +static const struct regmap_irq tps65910_irqs[] = { + /* INT_STS */ + [TPS65910_IRQ_VBAT_VMBDCH] = { + .mask = TPS65910_INT_MSK_VMBDCH_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_VBAT_VMHI] = { + .mask = TPS65910_INT_MSK_VMBHI_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_PWRON] = { + .mask = TPS65910_INT_MSK_PWRON_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_PWRON_LP] = { + .mask = TPS65910_INT_MSK_PWRON_LP_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_PWRHOLD] = { + .mask = TPS65910_INT_MSK_PWRHOLD_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_HOTDIE] = { + .mask = TPS65910_INT_MSK_HOTDIE_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_RTC_ALARM] = { + .mask = TPS65910_INT_MSK_RTC_ALARM_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_RTC_PERIOD] = { + .mask = TPS65910_INT_MSK_RTC_PERIOD_IT_MSK_MASK, + .reg_offset = 0, + }, + + /* INT_STS2 */ + [TPS65910_IRQ_GPIO_R] = { + .mask = TPS65910_INT_MSK2_GPIO0_F_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65910_IRQ_GPIO_F] = { + .mask = TPS65910_INT_MSK2_GPIO0_R_IT_MSK_MASK, + .reg_offset = 1, + }, +}; + +static struct regmap_irq_chip tps65911_irq_chip = { + .name = "tps65910", + .irqs = tps65911_irqs, + .num_irqs = ARRAY_SIZE(tps65911_irqs), + .num_regs = 3, + .irq_reg_stride = 2, + .status_base = TPS65910_INT_STS, + .mask_base = TPS65910_INT_MSK, + .ack_base = TPS65910_INT_MSK, +}; + +static struct regmap_irq_chip tps65910_irq_chip = { + .name = "tps65910", + .irqs = tps65910_irqs, + .num_irqs = ARRAY_SIZE(tps65910_irqs), + .num_regs = 2, + .irq_reg_stride = 2, + .status_base = TPS65910_INT_STS, + .mask_base = TPS65910_INT_MSK, + .ack_base = TPS65910_INT_MSK, +}; + +static int tps65910_irq_init(struct tps65910 *tps65910, int irq, + struct tps65910_platform_data *pdata) +{ + int ret = 0; + static struct regmap_irq_chip *tps6591x_irqs_chip; + + if (!irq) { + dev_warn(tps65910->dev, "No interrupt support, no core IRQ\n"); + return -EINVAL; + } + + if (!pdata) { + dev_warn(tps65910->dev, "No interrupt support, no pdata\n"); + return -EINVAL; + } + + switch (tps65910_chip_id(tps65910)) { + case TPS65910: + tps6591x_irqs_chip = &tps65910_irq_chip; + break; + case TPS65911: + tps6591x_irqs_chip = &tps65911_irq_chip; + break; + } + + tps65910->chip_irq = irq; + ret = regmap_add_irq_chip(tps65910->regmap, tps65910->chip_irq, + IRQF_ONESHOT, pdata->irq_base, + tps6591x_irqs_chip, &tps65910->irq_data); + if (ret < 0) + dev_warn(tps65910->dev, "Failed to add irq_chip %d\n", ret); + return ret; +} + +static int tps65910_irq_exit(struct tps65910 *tps65910) +{ + if (tps65910->chip_irq > 0) + regmap_del_irq_chip(tps65910->chip_irq, tps65910->irq_data); + return 0; +} + static bool is_volatile_reg(struct device *dev, unsigned int reg) { struct tps65910 *tps65910 = dev_get_drvdata(dev); diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h index b564ac29590a..0b16903f7e88 100644 --- a/include/linux/mfd/tps65910.h +++ b/include/linux/mfd/tps65910.h @@ -915,10 +915,6 @@ struct tps65910_platform_data { int irq_base; }; -int tps65910_irq_init(struct tps65910 *tps65910, int irq, - struct tps65910_platform_data *pdata); -int tps65910_irq_exit(struct tps65910 *tps65910); - static inline int tps65910_chip_id(struct tps65910 *tps65910) { return tps65910->id; From 17143e38afd60128332067ff91af080ddc764780 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 13 Nov 2012 19:33:59 +0530 Subject: [PATCH 037/119] mfd: tps65910: Pass irq_domain when adding mfd sub devices When adding the sub device "tps65910-rtc", is it passed the IO resource IRQ for the interrupt number. This interrupt needs to map in the device irq domain. Pass the irq domain of device in mfd_add_devices() so that proper irq mapping can be done when adding the sub device RTC. Signed-off-by: Laxman Dewangan Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65910.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index d4d4eb5b5b6f..ca3783350ccf 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -509,7 +509,8 @@ static __devinit int tps65910_i2c_probe(struct i2c_client *i2c, ret = mfd_add_devices(tps65910->dev, -1, tps65910s, ARRAY_SIZE(tps65910s), - NULL, 0, NULL); + NULL, 0, + regmap_irq_get_domain(tps65910->irq_data)); if (ret < 0) { dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret); return ret; From ffe20b6854db455a0c40e5fe1f9db2cfc9a62c6a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 4 Oct 2012 00:15:04 -0300 Subject: [PATCH 038/119] mfd: da9052-core: Use regmap_irq_get_virq() and fix the probe On a mx53qsb dt-kernel the da9052-core driver fails to probe as follows: da9052 1-0048: DA9052 ADC IRQ failed ret=-22 The reason for the error was due to passing only the offset as the interrupt number in request_threaded_irq() without da9052->irq_base. The recommended approach though is to use regmap_get_virq() to acquire the interrupt number and this allows to get rid of da9052->irq_base. Fix it and allow the driver to probe successfully. Also provide a few more error logs and change the irq string to "adc-irq", so that it appears as a single word in 'cat /proc/interrupts' Signed-off-by: Fabio Estevam Reviwed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/da9052-core.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c index a0a62b24621b..c96cdbc0daff 100644 --- a/drivers/mfd/da9052-core.c +++ b/drivers/mfd/da9052-core.c @@ -769,10 +769,15 @@ struct regmap_config da9052_regmap_config = { }; EXPORT_SYMBOL_GPL(da9052_regmap_config); +static int da9052_map_irq(struct da9052 *da9052, int irq) +{ + return regmap_irq_get_virq(da9052->irq_data, irq); +} + int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id) { struct da9052_pdata *pdata = da9052->dev->platform_data; - int ret; + int ret, i; mutex_init(&da9052->auxadc_lock); init_completion(&da9052->done); @@ -782,35 +787,34 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id) da9052->chip_id = chip_id; - if (!pdata || !pdata->irq_base) - da9052->irq_base = -1; - else - da9052->irq_base = pdata->irq_base; - ret = regmap_add_irq_chip(da9052->regmap, da9052->chip_irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT, - da9052->irq_base, &da9052_regmap_irq_chip, + -1, &da9052_regmap_irq_chip, &da9052->irq_data); - if (ret < 0) + if (ret < 0) { + dev_err(da9052->dev, "regmap_add_irq_chip failed: %d\n", ret); goto regmap_err; + } - da9052->irq_base = regmap_irq_chip_get_base(da9052->irq_data); - - ret = request_threaded_irq(DA9052_IRQ_ADC_EOM, NULL, da9052_auxadc_irq, + i = da9052_map_irq(da9052, DA9052_IRQ_ADC_EOM); + ret = request_threaded_irq(i, NULL, da9052_auxadc_irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "adc irq", da9052); + "adc-irq", da9052); if (ret != 0) dev_err(da9052->dev, "DA9052 ADC IRQ failed ret=%d\n", ret); ret = mfd_add_devices(da9052->dev, -1, da9052_subdev_info, ARRAY_SIZE(da9052_subdev_info), NULL, 0, NULL); - if (ret) + if (ret) { + dev_err(da9052->dev, "mfd_add_devices failed: %d\n", ret); goto err; + } return 0; err: - free_irq(DA9052_IRQ_ADC_EOM, da9052); + free_irq(da9052_map_irq(da9052, DA9052_IRQ_ADC_EOM), da9052); + regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data); mfd_remove_devices(da9052->dev); regmap_err: return ret; @@ -818,7 +822,7 @@ regmap_err: void da9052_device_exit(struct da9052 *da9052) { - free_irq(DA9052_IRQ_ADC_EOM, da9052); + free_irq(da9052_map_irq(da9052, DA9052_IRQ_ADC_EOM), da9052); regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data); mfd_remove_devices(da9052->dev); } From 8bad1abd6303476d6f77878aa8ea737d5d1b625c Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 4 Oct 2012 00:15:05 -0300 Subject: [PATCH 039/119] mfd: da9052: Introduce da9052-irq.c Create a da9052-irq.c file so that it can handle interrupt related functions. This is useful for allowing the da9052 drivers to use such functions when dealing with da9052 interrupts. Signed-off-by: Fabio Estevam Acked-by: Arnd Bergmann Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/Makefile | 1 + drivers/mfd/da9052-core.c | 271 +--------------------------- drivers/mfd/da9052-irq.c | 288 ++++++++++++++++++++++++++++++ include/linux/mfd/da9052/da9052.h | 10 ++ 4 files changed, 307 insertions(+), 263 deletions(-) create mode 100644 drivers/mfd/da9052-irq.c diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index a30c49ecdd95..632cce09e407 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -90,6 +90,7 @@ obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o obj-$(CONFIG_PMIC_DA903X) += da903x.o +obj-$(CONFIG_PMIC_DA9052) += da9052-irq.o obj-$(CONFIG_PMIC_DA9052) += da9052-core.o obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c index c96cdbc0daff..2153f9bba9ef 100644 --- a/drivers/mfd/da9052-core.c +++ b/drivers/mfd/da9052-core.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -24,16 +23,6 @@ #include #include -#define DA9052_NUM_IRQ_REGS 4 -#define DA9052_IRQ_MASK_POS_1 0x01 -#define DA9052_IRQ_MASK_POS_2 0x02 -#define DA9052_IRQ_MASK_POS_3 0x04 -#define DA9052_IRQ_MASK_POS_4 0x08 -#define DA9052_IRQ_MASK_POS_5 0x10 -#define DA9052_IRQ_MASK_POS_6 0x20 -#define DA9052_IRQ_MASK_POS_7 0x40 -#define DA9052_IRQ_MASK_POS_8 0x80 - static bool da9052_reg_readable(struct device *dev, unsigned int reg) { switch (reg) { @@ -425,15 +414,6 @@ err: } EXPORT_SYMBOL_GPL(da9052_adc_manual_read); -static irqreturn_t da9052_auxadc_irq(int irq, void *irq_data) -{ - struct da9052 *da9052 = irq_data; - - complete(&da9052->done); - - return IRQ_HANDLED; -} - int da9052_adc_read_temp(struct da9052 *da9052) { int tbat; @@ -447,74 +427,6 @@ int da9052_adc_read_temp(struct da9052 *da9052) } EXPORT_SYMBOL_GPL(da9052_adc_read_temp); -static struct resource da9052_rtc_resource = { - .name = "ALM", - .start = DA9052_IRQ_ALARM, - .end = DA9052_IRQ_ALARM, - .flags = IORESOURCE_IRQ, -}; - -static struct resource da9052_onkey_resource = { - .name = "ONKEY", - .start = DA9052_IRQ_NONKEY, - .end = DA9052_IRQ_NONKEY, - .flags = IORESOURCE_IRQ, -}; - -static struct resource da9052_bat_resources[] = { - { - .name = "BATT TEMP", - .start = DA9052_IRQ_TBAT, - .end = DA9052_IRQ_TBAT, - .flags = IORESOURCE_IRQ, - }, - { - .name = "DCIN DET", - .start = DA9052_IRQ_DCIN, - .end = DA9052_IRQ_DCIN, - .flags = IORESOURCE_IRQ, - }, - { - .name = "DCIN REM", - .start = DA9052_IRQ_DCINREM, - .end = DA9052_IRQ_DCINREM, - .flags = IORESOURCE_IRQ, - }, - { - .name = "VBUS DET", - .start = DA9052_IRQ_VBUS, - .end = DA9052_IRQ_VBUS, - .flags = IORESOURCE_IRQ, - }, - { - .name = "VBUS REM", - .start = DA9052_IRQ_VBUSREM, - .end = DA9052_IRQ_VBUSREM, - .flags = IORESOURCE_IRQ, - }, - { - .name = "CHG END", - .start = DA9052_IRQ_CHGEND, - .end = DA9052_IRQ_CHGEND, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct resource da9052_tsi_resources[] = { - { - .name = "PENDWN", - .start = DA9052_IRQ_PENDOWN, - .end = DA9052_IRQ_PENDOWN, - .flags = IORESOURCE_IRQ, - }, - { - .name = "TSIRDY", - .start = DA9052_IRQ_TSIREADY, - .end = DA9052_IRQ_TSIREADY, - .flags = IORESOURCE_IRQ, - }, -}; - static struct mfd_cell __devinitdata da9052_subdev_info[] = { { .name = "da9052-regulator", @@ -574,13 +486,9 @@ static struct mfd_cell __devinitdata da9052_subdev_info[] = { }, { .name = "da9052-onkey", - .resources = &da9052_onkey_resource, - .num_resources = 1, }, { .name = "da9052-rtc", - .resources = &da9052_rtc_resource, - .num_resources = 1, }, { .name = "da9052-gpio", @@ -602,160 +510,15 @@ static struct mfd_cell __devinitdata da9052_subdev_info[] = { }, { .name = "da9052-tsi", - .resources = da9052_tsi_resources, - .num_resources = ARRAY_SIZE(da9052_tsi_resources), }, { .name = "da9052-bat", - .resources = da9052_bat_resources, - .num_resources = ARRAY_SIZE(da9052_bat_resources), }, { .name = "da9052-watchdog", }, }; -static struct regmap_irq da9052_irqs[] = { - [DA9052_IRQ_DCIN] = { - .reg_offset = 0, - .mask = DA9052_IRQ_MASK_POS_1, - }, - [DA9052_IRQ_VBUS] = { - .reg_offset = 0, - .mask = DA9052_IRQ_MASK_POS_2, - }, - [DA9052_IRQ_DCINREM] = { - .reg_offset = 0, - .mask = DA9052_IRQ_MASK_POS_3, - }, - [DA9052_IRQ_VBUSREM] = { - .reg_offset = 0, - .mask = DA9052_IRQ_MASK_POS_4, - }, - [DA9052_IRQ_VDDLOW] = { - .reg_offset = 0, - .mask = DA9052_IRQ_MASK_POS_5, - }, - [DA9052_IRQ_ALARM] = { - .reg_offset = 0, - .mask = DA9052_IRQ_MASK_POS_6, - }, - [DA9052_IRQ_SEQRDY] = { - .reg_offset = 0, - .mask = DA9052_IRQ_MASK_POS_7, - }, - [DA9052_IRQ_COMP1V2] = { - .reg_offset = 0, - .mask = DA9052_IRQ_MASK_POS_8, - }, - [DA9052_IRQ_NONKEY] = { - .reg_offset = 1, - .mask = DA9052_IRQ_MASK_POS_1, - }, - [DA9052_IRQ_IDFLOAT] = { - .reg_offset = 1, - .mask = DA9052_IRQ_MASK_POS_2, - }, - [DA9052_IRQ_IDGND] = { - .reg_offset = 1, - .mask = DA9052_IRQ_MASK_POS_3, - }, - [DA9052_IRQ_CHGEND] = { - .reg_offset = 1, - .mask = DA9052_IRQ_MASK_POS_4, - }, - [DA9052_IRQ_TBAT] = { - .reg_offset = 1, - .mask = DA9052_IRQ_MASK_POS_5, - }, - [DA9052_IRQ_ADC_EOM] = { - .reg_offset = 1, - .mask = DA9052_IRQ_MASK_POS_6, - }, - [DA9052_IRQ_PENDOWN] = { - .reg_offset = 1, - .mask = DA9052_IRQ_MASK_POS_7, - }, - [DA9052_IRQ_TSIREADY] = { - .reg_offset = 1, - .mask = DA9052_IRQ_MASK_POS_8, - }, - [DA9052_IRQ_GPI0] = { - .reg_offset = 2, - .mask = DA9052_IRQ_MASK_POS_1, - }, - [DA9052_IRQ_GPI1] = { - .reg_offset = 2, - .mask = DA9052_IRQ_MASK_POS_2, - }, - [DA9052_IRQ_GPI2] = { - .reg_offset = 2, - .mask = DA9052_IRQ_MASK_POS_3, - }, - [DA9052_IRQ_GPI3] = { - .reg_offset = 2, - .mask = DA9052_IRQ_MASK_POS_4, - }, - [DA9052_IRQ_GPI4] = { - .reg_offset = 2, - .mask = DA9052_IRQ_MASK_POS_5, - }, - [DA9052_IRQ_GPI5] = { - .reg_offset = 2, - .mask = DA9052_IRQ_MASK_POS_6, - }, - [DA9052_IRQ_GPI6] = { - .reg_offset = 2, - .mask = DA9052_IRQ_MASK_POS_7, - }, - [DA9052_IRQ_GPI7] = { - .reg_offset = 2, - .mask = DA9052_IRQ_MASK_POS_8, - }, - [DA9052_IRQ_GPI8] = { - .reg_offset = 3, - .mask = DA9052_IRQ_MASK_POS_1, - }, - [DA9052_IRQ_GPI9] = { - .reg_offset = 3, - .mask = DA9052_IRQ_MASK_POS_2, - }, - [DA9052_IRQ_GPI10] = { - .reg_offset = 3, - .mask = DA9052_IRQ_MASK_POS_3, - }, - [DA9052_IRQ_GPI11] = { - .reg_offset = 3, - .mask = DA9052_IRQ_MASK_POS_4, - }, - [DA9052_IRQ_GPI12] = { - .reg_offset = 3, - .mask = DA9052_IRQ_MASK_POS_5, - }, - [DA9052_IRQ_GPI13] = { - .reg_offset = 3, - .mask = DA9052_IRQ_MASK_POS_6, - }, - [DA9052_IRQ_GPI14] = { - .reg_offset = 3, - .mask = DA9052_IRQ_MASK_POS_7, - }, - [DA9052_IRQ_GPI15] = { - .reg_offset = 3, - .mask = DA9052_IRQ_MASK_POS_8, - }, -}; - -static struct regmap_irq_chip da9052_regmap_irq_chip = { - .name = "da9052_irq", - .status_base = DA9052_EVENT_A_REG, - .mask_base = DA9052_IRQ_MASK_A_REG, - .ack_base = DA9052_EVENT_A_REG, - .num_regs = DA9052_NUM_IRQ_REGS, - .irqs = da9052_irqs, - .num_irqs = ARRAY_SIZE(da9052_irqs), -}; - struct regmap_config da9052_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -769,15 +532,10 @@ struct regmap_config da9052_regmap_config = { }; EXPORT_SYMBOL_GPL(da9052_regmap_config); -static int da9052_map_irq(struct da9052 *da9052, int irq) -{ - return regmap_irq_get_virq(da9052->irq_data, irq); -} - int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id) { struct da9052_pdata *pdata = da9052->dev->platform_data; - int ret, i; + int ret; mutex_init(&da9052->auxadc_lock); init_completion(&da9052->done); @@ -787,22 +545,12 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id) da9052->chip_id = chip_id; - ret = regmap_add_irq_chip(da9052->regmap, da9052->chip_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - -1, &da9052_regmap_irq_chip, - &da9052->irq_data); - if (ret < 0) { - dev_err(da9052->dev, "regmap_add_irq_chip failed: %d\n", ret); - goto regmap_err; + ret = da9052_irq_init(da9052); + if (ret != 0) { + dev_err(da9052->dev, "da9052_irq_init failed: %d\n", ret); + return ret; } - i = da9052_map_irq(da9052, DA9052_IRQ_ADC_EOM); - ret = request_threaded_irq(i, NULL, da9052_auxadc_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "adc-irq", da9052); - if (ret != 0) - dev_err(da9052->dev, "DA9052 ADC IRQ failed ret=%d\n", ret); - ret = mfd_add_devices(da9052->dev, -1, da9052_subdev_info, ARRAY_SIZE(da9052_subdev_info), NULL, 0, NULL); if (ret) { @@ -813,18 +561,15 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id) return 0; err: - free_irq(da9052_map_irq(da9052, DA9052_IRQ_ADC_EOM), da9052); - regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data); - mfd_remove_devices(da9052->dev); -regmap_err: + da9052_irq_exit(da9052); + return ret; } void da9052_device_exit(struct da9052 *da9052) { - free_irq(da9052_map_irq(da9052, DA9052_IRQ_ADC_EOM), da9052); - regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data); mfd_remove_devices(da9052->dev); + da9052_irq_exit(da9052); } MODULE_AUTHOR("David Dajun Chen "); diff --git a/drivers/mfd/da9052-irq.c b/drivers/mfd/da9052-irq.c new file mode 100644 index 000000000000..57ae7841f536 --- /dev/null +++ b/drivers/mfd/da9052-irq.c @@ -0,0 +1,288 @@ +/* + * DA9052 interrupt support + * + * Author: Fabio Estevam + * Based on arizona-irq.c, which is: + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DA9052_NUM_IRQ_REGS 4 +#define DA9052_IRQ_MASK_POS_1 0x01 +#define DA9052_IRQ_MASK_POS_2 0x02 +#define DA9052_IRQ_MASK_POS_3 0x04 +#define DA9052_IRQ_MASK_POS_4 0x08 +#define DA9052_IRQ_MASK_POS_5 0x10 +#define DA9052_IRQ_MASK_POS_6 0x20 +#define DA9052_IRQ_MASK_POS_7 0x40 +#define DA9052_IRQ_MASK_POS_8 0x80 + +static struct regmap_irq da9052_irqs[] = { + [DA9052_IRQ_DCIN] = { + .reg_offset = 0, + .mask = DA9052_IRQ_MASK_POS_1, + }, + [DA9052_IRQ_VBUS] = { + .reg_offset = 0, + .mask = DA9052_IRQ_MASK_POS_2, + }, + [DA9052_IRQ_DCINREM] = { + .reg_offset = 0, + .mask = DA9052_IRQ_MASK_POS_3, + }, + [DA9052_IRQ_VBUSREM] = { + .reg_offset = 0, + .mask = DA9052_IRQ_MASK_POS_4, + }, + [DA9052_IRQ_VDDLOW] = { + .reg_offset = 0, + .mask = DA9052_IRQ_MASK_POS_5, + }, + [DA9052_IRQ_ALARM] = { + .reg_offset = 0, + .mask = DA9052_IRQ_MASK_POS_6, + }, + [DA9052_IRQ_SEQRDY] = { + .reg_offset = 0, + .mask = DA9052_IRQ_MASK_POS_7, + }, + [DA9052_IRQ_COMP1V2] = { + .reg_offset = 0, + .mask = DA9052_IRQ_MASK_POS_8, + }, + [DA9052_IRQ_NONKEY] = { + .reg_offset = 1, + .mask = DA9052_IRQ_MASK_POS_1, + }, + [DA9052_IRQ_IDFLOAT] = { + .reg_offset = 1, + .mask = DA9052_IRQ_MASK_POS_2, + }, + [DA9052_IRQ_IDGND] = { + .reg_offset = 1, + .mask = DA9052_IRQ_MASK_POS_3, + }, + [DA9052_IRQ_CHGEND] = { + .reg_offset = 1, + .mask = DA9052_IRQ_MASK_POS_4, + }, + [DA9052_IRQ_TBAT] = { + .reg_offset = 1, + .mask = DA9052_IRQ_MASK_POS_5, + }, + [DA9052_IRQ_ADC_EOM] = { + .reg_offset = 1, + .mask = DA9052_IRQ_MASK_POS_6, + }, + [DA9052_IRQ_PENDOWN] = { + .reg_offset = 1, + .mask = DA9052_IRQ_MASK_POS_7, + }, + [DA9052_IRQ_TSIREADY] = { + .reg_offset = 1, + .mask = DA9052_IRQ_MASK_POS_8, + }, + [DA9052_IRQ_GPI0] = { + .reg_offset = 2, + .mask = DA9052_IRQ_MASK_POS_1, + }, + [DA9052_IRQ_GPI1] = { + .reg_offset = 2, + .mask = DA9052_IRQ_MASK_POS_2, + }, + [DA9052_IRQ_GPI2] = { + .reg_offset = 2, + .mask = DA9052_IRQ_MASK_POS_3, + }, + [DA9052_IRQ_GPI3] = { + .reg_offset = 2, + .mask = DA9052_IRQ_MASK_POS_4, + }, + [DA9052_IRQ_GPI4] = { + .reg_offset = 2, + .mask = DA9052_IRQ_MASK_POS_5, + }, + [DA9052_IRQ_GPI5] = { + .reg_offset = 2, + .mask = DA9052_IRQ_MASK_POS_6, + }, + [DA9052_IRQ_GPI6] = { + .reg_offset = 2, + .mask = DA9052_IRQ_MASK_POS_7, + }, + [DA9052_IRQ_GPI7] = { + .reg_offset = 2, + .mask = DA9052_IRQ_MASK_POS_8, + }, + [DA9052_IRQ_GPI8] = { + .reg_offset = 3, + .mask = DA9052_IRQ_MASK_POS_1, + }, + [DA9052_IRQ_GPI9] = { + .reg_offset = 3, + .mask = DA9052_IRQ_MASK_POS_2, + }, + [DA9052_IRQ_GPI10] = { + .reg_offset = 3, + .mask = DA9052_IRQ_MASK_POS_3, + }, + [DA9052_IRQ_GPI11] = { + .reg_offset = 3, + .mask = DA9052_IRQ_MASK_POS_4, + }, + [DA9052_IRQ_GPI12] = { + .reg_offset = 3, + .mask = DA9052_IRQ_MASK_POS_5, + }, + [DA9052_IRQ_GPI13] = { + .reg_offset = 3, + .mask = DA9052_IRQ_MASK_POS_6, + }, + [DA9052_IRQ_GPI14] = { + .reg_offset = 3, + .mask = DA9052_IRQ_MASK_POS_7, + }, + [DA9052_IRQ_GPI15] = { + .reg_offset = 3, + .mask = DA9052_IRQ_MASK_POS_8, + }, +}; + +static struct regmap_irq_chip da9052_regmap_irq_chip = { + .name = "da9052_irq", + .status_base = DA9052_EVENT_A_REG, + .mask_base = DA9052_IRQ_MASK_A_REG, + .ack_base = DA9052_EVENT_A_REG, + .num_regs = DA9052_NUM_IRQ_REGS, + .irqs = da9052_irqs, + .num_irqs = ARRAY_SIZE(da9052_irqs), +}; + +static int da9052_map_irq(struct da9052 *da9052, int irq) +{ + return regmap_irq_get_virq(da9052->irq_data, irq); +} + +int da9052_enable_irq(struct da9052 *da9052, int irq) +{ + irq = da9052_map_irq(da9052, irq); + if (irq < 0) + return irq; + + enable_irq(irq); + + return 0; +} +EXPORT_SYMBOL_GPL(da9052_enable_irq); + +int da9052_disable_irq(struct da9052 *da9052, int irq) +{ + irq = da9052_map_irq(da9052, irq); + if (irq < 0) + return irq; + + disable_irq(irq); + + return 0; +} +EXPORT_SYMBOL_GPL(da9052_disable_irq); + +int da9052_disable_irq_nosync(struct da9052 *da9052, int irq) +{ + irq = da9052_map_irq(da9052, irq); + if (irq < 0) + return irq; + + disable_irq_nosync(irq); + + return 0; +} +EXPORT_SYMBOL_GPL(da9052_disable_irq_nosync); + +int da9052_request_irq(struct da9052 *da9052, int irq, char *name, + irq_handler_t handler, void *data) +{ + irq = da9052_map_irq(da9052, irq); + if (irq < 0) + return irq; + + return request_threaded_irq(irq, NULL, handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + name, data); +} +EXPORT_SYMBOL_GPL(da9052_request_irq); + +void da9052_free_irq(struct da9052 *da9052, int irq, void *data) +{ + irq = da9052_map_irq(da9052, irq); + if (irq < 0) + return; + + free_irq(irq, data); +} +EXPORT_SYMBOL_GPL(da9052_free_irq); + +static irqreturn_t da9052_auxadc_irq(int irq, void *irq_data) +{ + struct da9052 *da9052 = irq_data; + + complete(&da9052->done); + + return IRQ_HANDLED; +} + +int da9052_irq_init(struct da9052 *da9052) +{ + int ret; + + ret = regmap_add_irq_chip(da9052->regmap, da9052->chip_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + -1, &da9052_regmap_irq_chip, + &da9052->irq_data); + if (ret < 0) { + dev_err(da9052->dev, "regmap_add_irq_chip failed: %d\n", ret); + goto regmap_err; + } + + ret = da9052_request_irq(da9052, DA9052_IRQ_ADC_EOM, "adc-irq", + da9052_auxadc_irq, da9052); + + if (ret != 0) { + dev_err(da9052->dev, "DA9052_IRQ_ADC_EOM failed: %d\n", ret); + goto request_irq_err; + } + + return 0; + +request_irq_err: + regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data); +regmap_err: + return ret; + +} + +int da9052_irq_exit(struct da9052 *da9052) +{ + da9052_free_irq(da9052, DA9052_IRQ_ADC_EOM , da9052); + regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data); + + return 0; +} diff --git a/include/linux/mfd/da9052/da9052.h b/include/linux/mfd/da9052/da9052.h index 0507c4c21a7d..86dd93de6ff2 100644 --- a/include/linux/mfd/da9052/da9052.h +++ b/include/linux/mfd/da9052/da9052.h @@ -146,4 +146,14 @@ void da9052_device_exit(struct da9052 *da9052); extern struct regmap_config da9052_regmap_config; +int da9052_irq_init(struct da9052 *da9052); +int da9052_irq_exit(struct da9052 *da9052); +int da9052_request_irq(struct da9052 *da9052, int irq, char *name, + irq_handler_t handler, void *data); +void da9052_free_irq(struct da9052 *da9052, int irq, void *data); + +int da9052_enable_irq(struct da9052 *da9052, int irq); +int da9052_disable_irq(struct da9052 *da9052, int irq); +int da9052_disable_irq_nosync(struct da9052 *da9052, int irq); + #endif /* __MFD_DA9052_DA9052_H */ From 8f520803d2a5dda0cf099c13cbf50ecf575d0341 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 4 Oct 2012 01:21:30 -0300 Subject: [PATCH 040/119] Input: da9052_tsi.c: Fix interrupt handling Currently da9062_tsi does not probe and it fails as follows: da9052 1-0048: Unable to determine device interrupts Use the new da9052 irq functions and allow the driver to probe and operate correctly. Tested on mx53qsb board using 'evtest' tool. Cc: Dmitry Torokhov Cc: Signed-off-by: Fabio Estevam Signed-off-by: Samuel Ortiz --- drivers/input/touchscreen/da9052_tsi.c | 59 +++++++++----------------- 1 file changed, 20 insertions(+), 39 deletions(-) diff --git a/drivers/input/touchscreen/da9052_tsi.c b/drivers/input/touchscreen/da9052_tsi.c index e8df341090c0..53133efe0418 100644 --- a/drivers/input/touchscreen/da9052_tsi.c +++ b/drivers/input/touchscreen/da9052_tsi.c @@ -27,8 +27,6 @@ struct da9052_tsi { struct input_dev *dev; struct delayed_work ts_pen_work; struct mutex mutex; - unsigned int irq_pendwn; - unsigned int irq_datardy; bool stopped; bool adc_on; }; @@ -45,8 +43,8 @@ static irqreturn_t da9052_ts_pendwn_irq(int irq, void *data) if (!tsi->stopped) { /* Mask PEN_DOWN event and unmask TSI_READY event */ - disable_irq_nosync(tsi->irq_pendwn); - enable_irq(tsi->irq_datardy); + da9052_disable_irq_nosync(tsi->da9052, DA9052_IRQ_PENDOWN); + da9052_enable_irq(tsi->da9052, DA9052_IRQ_TSIREADY); da9052_ts_adc_toggle(tsi, true); @@ -137,8 +135,8 @@ static void da9052_ts_pen_work(struct work_struct *work) return; /* Mask TSI_READY event and unmask PEN_DOWN event */ - disable_irq(tsi->irq_datardy); - enable_irq(tsi->irq_pendwn); + da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY); + da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN); } } } @@ -197,7 +195,7 @@ static int da9052_ts_input_open(struct input_dev *input_dev) mb(); /* Unmask PEN_DOWN event */ - enable_irq(tsi->irq_pendwn); + da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN); /* Enable Pen Detect Circuit */ return da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, @@ -210,11 +208,11 @@ static void da9052_ts_input_close(struct input_dev *input_dev) tsi->stopped = true; mb(); - disable_irq(tsi->irq_pendwn); + da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN); cancel_delayed_work_sync(&tsi->ts_pen_work); if (tsi->adc_on) { - disable_irq(tsi->irq_datardy); + da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY); da9052_ts_adc_toggle(tsi, false); /* @@ -222,7 +220,7 @@ static void da9052_ts_input_close(struct input_dev *input_dev) * twice and we need to enable it to keep enable/disable * counter balanced. IRQ is still off though. */ - enable_irq(tsi->irq_pendwn); + da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN); } /* Disable Pen Detect Circuit */ @@ -234,21 +232,12 @@ static int __devinit da9052_ts_probe(struct platform_device *pdev) struct da9052 *da9052; struct da9052_tsi *tsi; struct input_dev *input_dev; - int irq_pendwn; - int irq_datardy; int error; da9052 = dev_get_drvdata(pdev->dev.parent); if (!da9052) return -EINVAL; - irq_pendwn = platform_get_irq_byname(pdev, "PENDWN"); - irq_datardy = platform_get_irq_byname(pdev, "TSIRDY"); - if (irq_pendwn < 0 || irq_datardy < 0) { - dev_err(da9052->dev, "Unable to determine device interrupts\n"); - return -ENXIO; - } - tsi = kzalloc(sizeof(struct da9052_tsi), GFP_KERNEL); input_dev = input_allocate_device(); if (!tsi || !input_dev) { @@ -258,8 +247,6 @@ static int __devinit da9052_ts_probe(struct platform_device *pdev) tsi->da9052 = da9052; tsi->dev = input_dev; - tsi->irq_pendwn = da9052->irq_base + irq_pendwn; - tsi->irq_datardy = da9052->irq_base + irq_datardy; tsi->stopped = true; INIT_DELAYED_WORK(&tsi->ts_pen_work, da9052_ts_pen_work); @@ -287,31 +274,25 @@ static int __devinit da9052_ts_probe(struct platform_device *pdev) /* Disable ADC */ da9052_ts_adc_toggle(tsi, false); - error = request_threaded_irq(tsi->irq_pendwn, - NULL, da9052_ts_pendwn_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "PENDWN", tsi); + error = da9052_request_irq(tsi->da9052, DA9052_IRQ_PENDOWN, + "pendown-irq", da9052_ts_pendwn_irq, tsi); if (error) { dev_err(tsi->da9052->dev, - "Failed to register PENDWN IRQ %d, error = %d\n", - tsi->irq_pendwn, error); + "Failed to register PENDWN IRQ: %d\n", error); goto err_free_mem; } - error = request_threaded_irq(tsi->irq_datardy, - NULL, da9052_ts_datardy_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "TSIRDY", tsi); + error = da9052_request_irq(tsi->da9052, DA9052_IRQ_TSIREADY, + "tsiready-irq", da9052_ts_datardy_irq, tsi); if (error) { dev_err(tsi->da9052->dev, - "Failed to register TSIRDY IRQ %d, error = %d\n", - tsi->irq_datardy, error); + "Failed to register TSIRDY IRQ :%d\n", error); goto err_free_pendwn_irq; } /* Mask PEN_DOWN and TSI_READY events */ - disable_irq(tsi->irq_pendwn); - disable_irq(tsi->irq_datardy); + da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN); + da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY); error = da9052_configure_tsi(tsi); if (error) @@ -326,9 +307,9 @@ static int __devinit da9052_ts_probe(struct platform_device *pdev) return 0; err_free_datardy_irq: - free_irq(tsi->irq_datardy, tsi); + da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi); err_free_pendwn_irq: - free_irq(tsi->irq_pendwn, tsi); + da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi); err_free_mem: kfree(tsi); input_free_device(input_dev); @@ -342,8 +323,8 @@ static int __devexit da9052_ts_remove(struct platform_device *pdev) da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x19); - free_irq(tsi->irq_pendwn, tsi); - free_irq(tsi->irq_datardy, tsi); + da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi); + da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi); input_unregister_device(tsi->dev); kfree(tsi); From 21eed07d122a69d32220256c9a461a9d047e92b9 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 4 Oct 2012 00:15:07 -0300 Subject: [PATCH 041/119] Input: da9052_onkey.c: Convert to the new da9052 interrupt functions. Use the new da9052 irq functions and allow the driver to probe successfully. Cc: Dmitry Torokhov Cc: Signed-off-by: Fabio Estevam Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/input/misc/da9052_onkey.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c index 3c843cd725fa..3be3acc3a6eb 100644 --- a/drivers/input/misc/da9052_onkey.c +++ b/drivers/input/misc/da9052_onkey.c @@ -24,7 +24,6 @@ struct da9052_onkey { struct da9052 *da9052; struct input_dev *input; struct delayed_work work; - unsigned int irq; }; static void da9052_onkey_query(struct da9052_onkey *onkey) @@ -76,7 +75,6 @@ static int __devinit da9052_onkey_probe(struct platform_device *pdev) struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent); struct da9052_onkey *onkey; struct input_dev *input_dev; - int irq; int error; if (!da9052) { @@ -84,13 +82,6 @@ static int __devinit da9052_onkey_probe(struct platform_device *pdev) return -EINVAL; } - irq = platform_get_irq_byname(pdev, "ONKEY"); - if (irq < 0) { - dev_err(&pdev->dev, - "Failed to get an IRQ for input device, %d\n", irq); - return -EINVAL; - } - onkey = kzalloc(sizeof(*onkey), GFP_KERNEL); input_dev = input_allocate_device(); if (!onkey || !input_dev) { @@ -101,7 +92,6 @@ static int __devinit da9052_onkey_probe(struct platform_device *pdev) onkey->input = input_dev; onkey->da9052 = da9052; - onkey->irq = irq; INIT_DELAYED_WORK(&onkey->work, da9052_onkey_work); input_dev->name = "da9052-onkey"; @@ -111,13 +101,11 @@ static int __devinit da9052_onkey_probe(struct platform_device *pdev) input_dev->evbit[0] = BIT_MASK(EV_KEY); __set_bit(KEY_POWER, input_dev->keybit); - error = request_threaded_irq(onkey->irq, NULL, da9052_onkey_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "ONKEY", onkey); + error = da9052_request_irq(onkey->da9052, DA9052_IRQ_NONKEY, "ONKEY", + da9052_onkey_irq, onkey); if (error < 0) { dev_err(onkey->da9052->dev, - "Failed to register ONKEY IRQ %d, error = %d\n", - onkey->irq, error); + "Failed to register ONKEY IRQ: %d\n", error); goto err_free_mem; } @@ -132,7 +120,7 @@ static int __devinit da9052_onkey_probe(struct platform_device *pdev) return 0; err_free_irq: - free_irq(onkey->irq, onkey); + da9052_free_irq(onkey->da9052, DA9052_IRQ_NONKEY, onkey); cancel_delayed_work_sync(&onkey->work); err_free_mem: input_free_device(input_dev); @@ -145,7 +133,7 @@ static int __devexit da9052_onkey_remove(struct platform_device *pdev) { struct da9052_onkey *onkey = platform_get_drvdata(pdev); - free_irq(onkey->irq, onkey); + da9052_free_irq(onkey->da9052, DA9052_IRQ_NONKEY, onkey); cancel_delayed_work_sync(&onkey->work); input_unregister_device(onkey->input); From 4b5edf7887c6ec8c09c9064a2d14b578ef1904fe Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 4 Oct 2012 00:15:08 -0300 Subject: [PATCH 042/119] power: da9052-battery: Convert to the new da9052 interrupt functions Convert to the new da9052 interrupt functions, so that we can get rid of irq_base references. Acked-by: Anton Vorontsov Signed-off-by: Fabio Estevam Signed-off-by: Samuel Ortiz --- drivers/power/da9052-battery.c | 44 +++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/drivers/power/da9052-battery.c b/drivers/power/da9052-battery.c index d9d034d7496f..3972dc0fc879 100644 --- a/drivers/power/da9052-battery.c +++ b/drivers/power/da9052-battery.c @@ -440,8 +440,10 @@ static int da9052_bat_check_health(struct da9052_battery *bat, int *health) static irqreturn_t da9052_bat_irq(int irq, void *data) { struct da9052_battery *bat = data; + int virq; - irq -= bat->da9052->irq_base; + virq = regmap_irq_get_virq(bat->da9052->irq_data, irq); + irq -= virq; if (irq == DA9052_IRQ_CHGEND) bat->status = POWER_SUPPLY_STATUS_FULL; @@ -567,7 +569,7 @@ static struct power_supply template_battery = { .get_property = da9052_bat_get_property, }; -static const char *const da9052_bat_irqs[] = { +static char *da9052_bat_irqs[] = { "BATT TEMP", "DCIN DET", "DCIN REM", @@ -576,12 +578,20 @@ static const char *const da9052_bat_irqs[] = { "CHG END", }; +static int da9052_bat_irq_bits[] = { + DA9052_IRQ_TBAT, + DA9052_IRQ_DCIN, + DA9052_IRQ_DCINREM, + DA9052_IRQ_VBUS, + DA9052_IRQ_VBUSREM, + DA9052_IRQ_CHGEND, +}; + static s32 __devinit da9052_bat_probe(struct platform_device *pdev) { struct da9052_pdata *pdata; struct da9052_battery *bat; int ret; - int irq; int i; bat = kzalloc(sizeof(struct da9052_battery), GFP_KERNEL); @@ -602,15 +612,14 @@ static s32 __devinit da9052_bat_probe(struct platform_device *pdev) bat->psy.use_for_apm = 1; for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++) { - irq = platform_get_irq_byname(pdev, da9052_bat_irqs[i]); - ret = request_threaded_irq(bat->da9052->irq_base + irq, - NULL, da9052_bat_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - da9052_bat_irqs[i], bat); + ret = da9052_request_irq(bat->da9052, + da9052_bat_irq_bits[i], da9052_bat_irqs[i], + da9052_bat_irq, bat); + if (ret != 0) { dev_err(bat->da9052->dev, - "DA9052 failed to request %s IRQ %d: %d\n", - da9052_bat_irqs[i], irq, ret); + "DA9052 failed to request %s IRQ: %d\n", + da9052_bat_irqs[i], ret); goto err; } } @@ -623,23 +632,20 @@ static s32 __devinit da9052_bat_probe(struct platform_device *pdev) return 0; err: - while (--i >= 0) { - irq = platform_get_irq_byname(pdev, da9052_bat_irqs[i]); - free_irq(bat->da9052->irq_base + irq, bat); - } + while (--i >= 0) + da9052_free_irq(bat->da9052, da9052_bat_irq_bits[i], bat); + kfree(bat); return ret; } static int __devexit da9052_bat_remove(struct platform_device *pdev) { int i; - int irq; struct da9052_battery *bat = platform_get_drvdata(pdev); - for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++) { - irq = platform_get_irq_byname(pdev, da9052_bat_irqs[i]); - free_irq(bat->da9052->irq_base + irq, bat); - } + for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++) + da9052_free_irq(bat->da9052, da9052_bat_irq_bits[i], bat); + power_supply_unregister(&bat->psy); kfree(bat); From d511b9c3626c83874824b1118f8a2e84ef63a7d3 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 4 Oct 2012 00:15:09 -0300 Subject: [PATCH 043/119] gpio: gpio-da9052: Convert to the new da9052 interrupt functions Convert to the new da9052 interrupt functions, so that we can get rid of irq_base references. Cc: Grant Likely Signed-off-by: Fabio Estevam Reviewed-by: Mark Brown Reviewed-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/gpio/gpio-da9052.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-da9052.c b/drivers/gpio/gpio-da9052.c index 24b8c2974047..63f9f5bbc75c 100644 --- a/drivers/gpio/gpio-da9052.c +++ b/drivers/gpio/gpio-da9052.c @@ -185,7 +185,11 @@ static int da9052_gpio_to_irq(struct gpio_chip *gc, u32 offset) struct da9052_gpio *gpio = to_da9052_gpio(gc); struct da9052 *da9052 = gpio->da9052; - return da9052->irq_base + DA9052_IRQ_GPI0 + offset; + int irq; + + irq = regmap_irq_get_virq(da9052->irq_data, DA9052_IRQ_GPI0 + offset); + + return irq; } static struct gpio_chip reference_gp __devinitdata = { From b9fbb62eb61452d728c39b2e5020739c575aac53 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 9 Nov 2012 16:15:28 +0000 Subject: [PATCH 044/119] mfd: Only unregister platform devices allocated by the mfd core mfd_remove_devices would iterate over all devices sharing a parent with an mfd device regardless of whether they were allocated by the mfd core or not. This especially caused problems when the device structure was not contained within a platform_device, because to_platform_device is used on each device pointer. This patch defines a device_type for mfd devices and checks this is present from mfd_remove_devices_fn before processing the device. Cc: stable@vger.kernel.org Signed-off-by: Charles Keepax Tested-by: Peter Tyser Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/mfd-core.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index f8b77711ad2d..7604f4e5df40 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -21,6 +21,10 @@ #include #include +static struct device_type mfd_dev_type = { + .name = "mfd_device", +}; + int mfd_cell_enable(struct platform_device *pdev) { const struct mfd_cell *cell = mfd_get_cell(pdev); @@ -91,6 +95,7 @@ static int mfd_add_device(struct device *parent, int id, goto fail_device; pdev->dev.parent = parent; + pdev->dev.type = &mfd_dev_type; if (parent->of_node && cell->of_compatible) { for_each_child_of_node(parent->of_node, np) { @@ -204,10 +209,16 @@ EXPORT_SYMBOL(mfd_add_devices); static int mfd_remove_devices_fn(struct device *dev, void *c) { - struct platform_device *pdev = to_platform_device(dev); - const struct mfd_cell *cell = mfd_get_cell(pdev); + struct platform_device *pdev; + const struct mfd_cell *cell; atomic_t **usage_count = c; + if (dev->type != &mfd_dev_type) + return 0; + + pdev = to_platform_device(dev); + cell = mfd_get_cell(pdev); + /* find the base address of usage_count pointers (for freeing) */ if (!*usage_count || (cell->usage_count < *usage_count)) *usage_count = cell->usage_count; From f01312d846016dbd38cc9865e580298fb61f2aa7 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Mon, 5 Nov 2012 15:48:23 +0100 Subject: [PATCH 045/119] mfd: Add viperboard driver Add mfd driver for Nano River Technologies viperboard. Signed-off-by: Lars Poeschel Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 14 ++++ drivers/mfd/Makefile | 1 + drivers/mfd/viperboard.c | 129 +++++++++++++++++++++++++++++++++ include/linux/mfd/viperboard.h | 105 +++++++++++++++++++++++++++ 4 files changed, 249 insertions(+) create mode 100644 drivers/mfd/viperboard.c create mode 100644 include/linux/mfd/viperboard.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 34242cada125..b280639a7634 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1065,6 +1065,20 @@ config MFD_PALMAS If you say yes here you get support for the Palmas series of PMIC chips from Texas Instruments. +config MFD_VIPERBOARD + tristate "Support for Nano River Technologies Viperboard" + select MFD_CORE + depends on USB + default n + help + Say yes here if you want support for Nano River Technologies + Viperboard. + There are mfd cell drivers available for i2c master, adc and + both gpios found on the board. The spi part does not yet + have a driver. + You need to select the mfd cell drivers separately. + The drivers do not support all features the board exposes. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 632cce09e407..1c3ee7c76906 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -139,6 +139,7 @@ obj-$(CONFIG_MFD_TPS65090) += tps65090.o obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o obj-$(CONFIG_MFD_PALMAS) += palmas.o +obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_SYSCON) += syscon.o diff --git a/drivers/mfd/viperboard.c b/drivers/mfd/viperboard.c new file mode 100644 index 000000000000..cd1e2fe95647 --- /dev/null +++ b/drivers/mfd/viperboard.c @@ -0,0 +1,129 @@ +/* + * Nano River Technologies viperboard driver + * + * This is the core driver for the viperboard. There are cell drivers + * available for I2C, ADC and both GPIOs. SPI is not yet supported. + * The drivers do not support all features the board exposes. See user + * manual of the viperboard. + * + * (C) 2012 by Lemonage GmbH + * Author: Lars Poeschel + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + + +static const struct usb_device_id vprbrd_table[] = { + { USB_DEVICE(0x2058, 0x1005) }, /* Nano River Technologies */ + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, vprbrd_table); + +static struct mfd_cell vprbrd_devs[] = { +}; + +static int vprbrd_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct vprbrd *vb; + + u16 version = 0; + int pipe, ret; + unsigned char buf[1]; + + /* allocate memory for our device state and initialize it */ + vb = kzalloc(sizeof(*vb), GFP_KERNEL); + if (vb == NULL) { + dev_err(&interface->dev, "Out of memory\n"); + return -ENOMEM; + } + + mutex_init(&vb->lock); + + vb->usb_dev = usb_get_dev(interface_to_usbdev(interface)); + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, vb); + dev_set_drvdata(&vb->pdev.dev, vb); + + /* get version information, major first, minor then */ + pipe = usb_rcvctrlpipe(vb->usb_dev, 0); + ret = usb_control_msg(vb->usb_dev, pipe, VPRBRD_USB_REQUEST_MAJOR, + VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, buf, 1, + VPRBRD_USB_TIMEOUT_MS); + if (ret == 1) + version = buf[0]; + + ret = usb_control_msg(vb->usb_dev, pipe, VPRBRD_USB_REQUEST_MINOR, + VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, buf, 1, + VPRBRD_USB_TIMEOUT_MS); + if (ret == 1) { + version <<= 8; + version = version | buf[0]; + } + + dev_info(&interface->dev, + "version %x.%02x found at bus %03d address %03d\n", + version >> 8, version & 0xff, + vb->usb_dev->bus->busnum, vb->usb_dev->devnum); + + ret = mfd_add_devices(&interface->dev, -1, vprbrd_devs, + ARRAY_SIZE(vprbrd_devs), NULL, 0, NULL); + if (ret != 0) { + dev_err(&interface->dev, "Failed to add mfd devices to core."); + goto error; + } + + return 0; + +error: + if (vb) { + usb_put_dev(vb->usb_dev); + kfree(vb); + } + + return ret; +} + +static void vprbrd_disconnect(struct usb_interface *interface) +{ + struct vprbrd *vb = usb_get_intfdata(interface); + + mfd_remove_devices(&interface->dev); + usb_set_intfdata(interface, NULL); + usb_put_dev(vb->usb_dev); + kfree(vb); + + dev_dbg(&interface->dev, "disconnected\n"); +} + +static struct usb_driver vprbrd_driver = { + .name = "viperboard", + .probe = vprbrd_probe, + .disconnect = vprbrd_disconnect, + .id_table = vprbrd_table, +}; + +module_usb_driver(vprbrd_driver); + +MODULE_DESCRIPTION("Nano River Technologies viperboard mfd core driver"); +MODULE_AUTHOR("Lars Poeschel "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/viperboard.h b/include/linux/mfd/viperboard.h new file mode 100644 index 000000000000..0d1342466db5 --- /dev/null +++ b/include/linux/mfd/viperboard.h @@ -0,0 +1,105 @@ +/* + * include/linux/mfd/viperboard.h + * + * Nano River Technologies viperboard definitions + * + * (C) 2012 by Lemonage GmbH + * Author: Lars Poeschel + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __MFD_VIPERBOARD_H__ +#define __MFD_VIPERBOARD_H__ + +#include +#include + +#define VPRBRD_EP_OUT 0x02 +#define VPRBRD_EP_IN 0x86 + +#define VPRBRD_I2C_MSG_LEN 512 /* max length of a msg on USB level */ + +#define VPRBRD_I2C_FREQ_6MHZ 1 /* 6 MBit/s */ +#define VPRBRD_I2C_FREQ_3MHZ 2 /* 3 MBit/s */ +#define VPRBRD_I2C_FREQ_1MHZ 3 /* 1 MBit/s */ +#define VPRBRD_I2C_FREQ_FAST 4 /* 400 kbit/s */ +#define VPRBRD_I2C_FREQ_400KHZ VPRBRD_I2C_FREQ_FAST +#define VPRBRD_I2C_FREQ_200KHZ 5 /* 200 kbit/s */ +#define VPRBRD_I2C_FREQ_STD 6 /* 100 kbit/s */ +#define VPRBRD_I2C_FREQ_100KHZ VPRBRD_I2C_FREQ_STD +#define VPRBRD_I2C_FREQ_10KHZ 7 /* 10 kbit/s */ + +#define VPRBRD_I2C_CMD_WRITE 0x00 +#define VPRBRD_I2C_CMD_READ 0x01 +#define VPRBRD_I2C_CMD_ADDR 0x02 + +#define VPRBRD_USB_TYPE_OUT 0x40 +#define VPRBRD_USB_TYPE_IN 0xc0 +#define VPRBRD_USB_TIMEOUT_MS 100 +#define VPRBRD_USB_REQUEST_MAJOR 0xea +#define VPRBRD_USB_REQUEST_MINOR 0xeb + +struct vprbrd_i2c_write_hdr { + u8 cmd; + u16 addr; + u8 len1; + u8 len2; + u8 last; + u8 chan; + u16 spi; +} __packed; + +struct vprbrd_i2c_read_hdr { + u8 cmd; + u16 addr; + u8 len0; + u8 len1; + u8 len2; + u8 len3; + u8 len4; + u8 len5; + u16 tf1; /* transfer 1 length */ + u16 tf2; /* transfer 2 length */ +} __packed; + +struct vprbrd_i2c_status { + u8 unknown[11]; + u8 status; +} __packed; + +struct vprbrd_i2c_write_msg { + struct vprbrd_i2c_write_hdr header; + u8 data[VPRBRD_I2C_MSG_LEN + - sizeof(struct vprbrd_i2c_write_hdr)]; +} __packed; + +struct vprbrd_i2c_read_msg { + struct vprbrd_i2c_read_hdr header; + u8 data[VPRBRD_I2C_MSG_LEN + - sizeof(struct vprbrd_i2c_read_hdr)]; +} __packed; + +struct vprbrd_i2c_addr_msg { + u8 cmd; + u8 addr; + u8 unknown1; + u16 len; + u8 unknown2; + u8 unknown3; +} __packed; + +/* Structure to hold all device specific stuff */ +struct vprbrd { + struct usb_device *usb_dev; /* the usb device for this device */ + struct mutex lock; + u8 buf[sizeof(struct vprbrd_i2c_write_msg)]; + struct platform_device pdev; +}; + +#endif /* __MFD_VIPERBOARD_H__ */ From 9d5b72de0d1627b130fa69c5edf58b5b2df4ca50 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Mon, 5 Nov 2012 15:48:24 +0100 Subject: [PATCH 046/119] gpio: Add viperboard gpio driver This adds the mfd cell to use the gpio a and gpio b part of the Nano River Technologies viperboard. Signed-off-by: Lars Poeschel Reviewed-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/gpio/Kconfig | 13 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-viperboard.c | 517 +++++++++++++++++++++++++++++++++ drivers/mfd/viperboard.c | 3 + include/linux/mfd/viperboard.h | 2 + 5 files changed, 536 insertions(+) create mode 100644 drivers/gpio/gpio-viperboard.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d055cee36942..c3bbd08bcdc3 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -649,4 +649,17 @@ config GPIO_MSIC Enable support for GPIO on intel MSIC controllers found in intel MID devices +comment "USB GPIO expanders:" + +config GPIO_VIPERBOARD + tristate "Viperboard GPIO a & b support" + depends on MFD_VIPERBOARD && USB + help + Say yes here to access the GPIO signals of Nano River + Technologies Viperboard. There are two GPIO chips on the + board: gpioa and gpiob. + See viperboard API specification and Nano + River Tech's viperboard.h for detailed meaning + of the module parameters. + endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9aeed6707326..16a1385226e9 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o +obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o obj-$(CONFIG_GPIO_VT8500) += gpio-vt8500.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c new file mode 100644 index 000000000000..13772996cf24 --- /dev/null +++ b/drivers/gpio/gpio-viperboard.c @@ -0,0 +1,517 @@ +/* + * Nano River Technologies viperboard GPIO lib driver + * + * (C) 2012 by Lemonage GmbH + * Author: Lars Poeschel + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define VPRBRD_GPIOA_CLK_1MHZ 0 +#define VPRBRD_GPIOA_CLK_100KHZ 1 +#define VPRBRD_GPIOA_CLK_10KHZ 2 +#define VPRBRD_GPIOA_CLK_1KHZ 3 +#define VPRBRD_GPIOA_CLK_100HZ 4 +#define VPRBRD_GPIOA_CLK_10HZ 5 + +#define VPRBRD_GPIOA_FREQ_DEFAULT 1000 + +#define VPRBRD_GPIOA_CMD_CONT 0x00 +#define VPRBRD_GPIOA_CMD_PULSE 0x01 +#define VPRBRD_GPIOA_CMD_PWM 0x02 +#define VPRBRD_GPIOA_CMD_SETOUT 0x03 +#define VPRBRD_GPIOA_CMD_SETIN 0x04 +#define VPRBRD_GPIOA_CMD_SETINT 0x05 +#define VPRBRD_GPIOA_CMD_GETIN 0x06 + +#define VPRBRD_GPIOB_CMD_SETDIR 0x00 +#define VPRBRD_GPIOB_CMD_SETVAL 0x01 + +struct vprbrd_gpioa_msg { + u8 cmd; + u8 clk; + u8 offset; + u8 t1; + u8 t2; + u8 invert; + u8 pwmlevel; + u8 outval; + u8 risefall; + u8 answer; + u8 __fill; +} __packed; + +struct vprbrd_gpiob_msg { + u8 cmd; + u16 val; + u16 mask; +} __packed; + +struct vprbrd_gpio { + struct gpio_chip gpioa; /* gpio a related things */ + u32 gpioa_out; + u32 gpioa_val; + struct gpio_chip gpiob; /* gpio b related things */ + u32 gpiob_out; + u32 gpiob_val; + struct vprbrd *vb; +}; + +/* gpioa sampling clock module parameter */ +static unsigned char gpioa_clk; +static unsigned int gpioa_freq = VPRBRD_GPIOA_FREQ_DEFAULT; +module_param(gpioa_freq, uint, 0); +MODULE_PARM_DESC(gpioa_freq, + "gpio-a sampling freq in Hz (default is 1000Hz) valid values: 10, 100, 1000, 10000, 100000, 1000000"); + +/* ----- begin of gipo a chip -------------------------------------------- */ + +static int vprbrd_gpioa_get(struct gpio_chip *chip, + unsigned offset) +{ + int ret, answer, error = 0; + struct vprbrd_gpio *gpio = + container_of(chip, struct vprbrd_gpio, gpioa); + struct vprbrd *vb = gpio->vb; + struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf; + + /* if io is set to output, just return the saved value */ + if (gpio->gpioa_out & (1 << offset)) + return gpio->gpioa_val & (1 << offset); + + mutex_lock(&vb->lock); + + gamsg->cmd = VPRBRD_GPIOA_CMD_GETIN; + gamsg->clk = 0x00; + gamsg->offset = offset; + gamsg->t1 = 0x00; + gamsg->t2 = 0x00; + gamsg->invert = 0x00; + gamsg->pwmlevel = 0x00; + gamsg->outval = 0x00; + gamsg->risefall = 0x00; + gamsg->answer = 0x00; + gamsg->__fill = 0x00; + + ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), + VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000, + 0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg), + VPRBRD_USB_TIMEOUT_MS); + if (ret != sizeof(struct vprbrd_gpioa_msg)) + error = -EREMOTEIO; + + ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0), + VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_IN, 0x0000, + 0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg), + VPRBRD_USB_TIMEOUT_MS); + answer = gamsg->answer & 0x01; + + mutex_unlock(&vb->lock); + + if (ret != sizeof(struct vprbrd_gpioa_msg)) + error = -EREMOTEIO; + + if (error) + return error; + + return answer; +} + +static void vprbrd_gpioa_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + int ret; + struct vprbrd_gpio *gpio = + container_of(chip, struct vprbrd_gpio, gpioa); + struct vprbrd *vb = gpio->vb; + struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf; + + if (gpio->gpioa_out & (1 << offset)) { + if (value) + gpio->gpioa_val |= (1 << offset); + else + gpio->gpioa_val &= ~(1 << offset); + + mutex_lock(&vb->lock); + + gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT; + gamsg->clk = 0x00; + gamsg->offset = offset; + gamsg->t1 = 0x00; + gamsg->t2 = 0x00; + gamsg->invert = 0x00; + gamsg->pwmlevel = 0x00; + gamsg->outval = value; + gamsg->risefall = 0x00; + gamsg->answer = 0x00; + gamsg->__fill = 0x00; + + ret = usb_control_msg(vb->usb_dev, + usb_sndctrlpipe(vb->usb_dev, 0), + VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, + 0x0000, 0x0000, gamsg, + sizeof(struct vprbrd_gpioa_msg), VPRBRD_USB_TIMEOUT_MS); + + mutex_unlock(&vb->lock); + + if (ret != sizeof(struct vprbrd_gpioa_msg)) + dev_err(chip->dev, "usb error setting pin value\n"); + } +} + +static int vprbrd_gpioa_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + int ret; + struct vprbrd_gpio *gpio = + container_of(chip, struct vprbrd_gpio, gpioa); + struct vprbrd *vb = gpio->vb; + struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf; + + gpio->gpioa_out &= ~(1 << offset); + + mutex_lock(&vb->lock); + + gamsg->cmd = VPRBRD_GPIOA_CMD_SETIN; + gamsg->clk = gpioa_clk; + gamsg->offset = offset; + gamsg->t1 = 0x00; + gamsg->t2 = 0x00; + gamsg->invert = 0x00; + gamsg->pwmlevel = 0x00; + gamsg->outval = 0x00; + gamsg->risefall = 0x00; + gamsg->answer = 0x00; + gamsg->__fill = 0x00; + + ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), + VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000, + 0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg), + VPRBRD_USB_TIMEOUT_MS); + + mutex_unlock(&vb->lock); + + if (ret != sizeof(struct vprbrd_gpioa_msg)) + return -EREMOTEIO; + + return 0; +} + +static int vprbrd_gpioa_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + int ret; + struct vprbrd_gpio *gpio = + container_of(chip, struct vprbrd_gpio, gpioa); + struct vprbrd *vb = gpio->vb; + struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf; + + gpio->gpioa_out |= (1 << offset); + if (value) + gpio->gpioa_val |= (1 << offset); + else + gpio->gpioa_val &= ~(1 << offset); + + mutex_lock(&vb->lock); + + gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT; + gamsg->clk = 0x00; + gamsg->offset = offset; + gamsg->t1 = 0x00; + gamsg->t2 = 0x00; + gamsg->invert = 0x00; + gamsg->pwmlevel = 0x00; + gamsg->outval = value; + gamsg->risefall = 0x00; + gamsg->answer = 0x00; + gamsg->__fill = 0x00; + + ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), + VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000, + 0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg), + VPRBRD_USB_TIMEOUT_MS); + + mutex_unlock(&vb->lock); + + if (ret != sizeof(struct vprbrd_gpioa_msg)) + return -EREMOTEIO; + + return 0; +} + +/* ----- end of gpio a chip ---------------------------------------------- */ + +/* ----- begin of gipo b chip -------------------------------------------- */ + +static int vprbrd_gpiob_setdir(struct vprbrd *vb, unsigned offset, + unsigned dir) +{ + struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf; + int ret; + + gbmsg->cmd = VPRBRD_GPIOB_CMD_SETDIR; + gbmsg->val = cpu_to_be16(dir << offset); + gbmsg->mask = cpu_to_be16(0x0001 << offset); + + ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), + VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT, 0x0000, + 0x0000, gbmsg, sizeof(struct vprbrd_gpiob_msg), + VPRBRD_USB_TIMEOUT_MS); + + if (ret != sizeof(struct vprbrd_gpiob_msg)) + return -EREMOTEIO; + + return 0; +} + +static int vprbrd_gpiob_get(struct gpio_chip *chip, + unsigned offset) +{ + int ret; + u16 val; + struct vprbrd_gpio *gpio = + container_of(chip, struct vprbrd_gpio, gpiob); + struct vprbrd *vb = gpio->vb; + struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf; + + /* if io is set to output, just return the saved value */ + if (gpio->gpiob_out & (1 << offset)) + return gpio->gpiob_val & (1 << offset); + + mutex_lock(&vb->lock); + + ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0), + VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_IN, 0x0000, + 0x0000, gbmsg, sizeof(struct vprbrd_gpiob_msg), + VPRBRD_USB_TIMEOUT_MS); + val = gbmsg->val; + + mutex_unlock(&vb->lock); + + if (ret != sizeof(struct vprbrd_gpiob_msg)) + return ret; + + /* cache the read values */ + gpio->gpiob_val = be16_to_cpu(val); + + return (gpio->gpiob_val >> offset) & 0x1; +} + +static void vprbrd_gpiob_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + int ret; + struct vprbrd_gpio *gpio = + container_of(chip, struct vprbrd_gpio, gpiob); + struct vprbrd *vb = gpio->vb; + struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf; + + if (gpio->gpiob_out & (1 << offset)) { + if (value) + gpio->gpiob_val |= (1 << offset); + else + gpio->gpiob_val &= ~(1 << offset); + + mutex_lock(&vb->lock); + + gbmsg->cmd = VPRBRD_GPIOB_CMD_SETVAL; + gbmsg->val = cpu_to_be16(value << offset); + gbmsg->mask = cpu_to_be16(0x0001 << offset); + + ret = usb_control_msg(vb->usb_dev, + usb_sndctrlpipe(vb->usb_dev, 0), + VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT, + 0x0000, 0x0000, gbmsg, + sizeof(struct vprbrd_gpiob_msg), VPRBRD_USB_TIMEOUT_MS); + + mutex_unlock(&vb->lock); + + if (ret != sizeof(struct vprbrd_gpiob_msg)) + dev_err(chip->dev, "usb error setting pin value\n"); + } +} + +static int vprbrd_gpiob_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + int ret; + struct vprbrd_gpio *gpio = + container_of(chip, struct vprbrd_gpio, gpiob); + struct vprbrd *vb = gpio->vb; + + gpio->gpiob_out &= ~(1 << offset); + + mutex_lock(&vb->lock); + + ret = vprbrd_gpiob_setdir(vb, offset, 0); + + mutex_unlock(&vb->lock); + + if (ret) + dev_err(chip->dev, "usb error setting pin to input\n"); + + return ret; +} + +static int vprbrd_gpiob_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + int ret; + struct vprbrd_gpio *gpio = + container_of(chip, struct vprbrd_gpio, gpiob); + struct vprbrd *vb = gpio->vb; + + gpio->gpiob_out |= (1 << offset); + if (value) + gpio->gpiob_val |= (1 << offset); + else + gpio->gpiob_val &= ~(1 << offset); + + mutex_lock(&vb->lock); + + ret = vprbrd_gpiob_setdir(vb, offset, 1); + if (ret) + dev_err(chip->dev, "usb error setting pin to output\n"); + + mutex_unlock(&vb->lock); + + vprbrd_gpiob_set(chip, offset, value); + + return ret; +} + +/* ----- end of gpio b chip ---------------------------------------------- */ + +static int __devinit vprbrd_gpio_probe(struct platform_device *pdev) +{ + struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent); + struct vprbrd_gpio *vb_gpio; + int ret; + + vb_gpio = devm_kzalloc(&pdev->dev, sizeof(*vb_gpio), GFP_KERNEL); + if (vb_gpio == NULL) + return -ENOMEM; + + vb_gpio->vb = vb; + /* registering gpio a */ + vb_gpio->gpioa.label = "viperboard gpio a"; + vb_gpio->gpioa.dev = &pdev->dev; + vb_gpio->gpioa.owner = THIS_MODULE; + vb_gpio->gpioa.base = -1; + vb_gpio->gpioa.ngpio = 16; + vb_gpio->gpioa.can_sleep = 1; + vb_gpio->gpioa.set = vprbrd_gpioa_set; + vb_gpio->gpioa.get = vprbrd_gpioa_get; + vb_gpio->gpioa.direction_input = vprbrd_gpioa_direction_input; + vb_gpio->gpioa.direction_output = vprbrd_gpioa_direction_output; + ret = gpiochip_add(&vb_gpio->gpioa); + if (ret < 0) { + dev_err(vb_gpio->gpioa.dev, "could not add gpio a"); + goto err_gpioa; + } + + /* registering gpio b */ + vb_gpio->gpiob.label = "viperboard gpio b"; + vb_gpio->gpiob.dev = &pdev->dev; + vb_gpio->gpiob.owner = THIS_MODULE; + vb_gpio->gpiob.base = -1; + vb_gpio->gpiob.ngpio = 16; + vb_gpio->gpiob.can_sleep = 1; + vb_gpio->gpiob.set = vprbrd_gpiob_set; + vb_gpio->gpiob.get = vprbrd_gpiob_get; + vb_gpio->gpiob.direction_input = vprbrd_gpiob_direction_input; + vb_gpio->gpiob.direction_output = vprbrd_gpiob_direction_output; + ret = gpiochip_add(&vb_gpio->gpiob); + if (ret < 0) { + dev_err(vb_gpio->gpiob.dev, "could not add gpio b"); + goto err_gpiob; + } + + platform_set_drvdata(pdev, vb_gpio); + + return ret; + +err_gpiob: + ret = gpiochip_remove(&vb_gpio->gpioa); + +err_gpioa: + return ret; +} + +static int __devexit vprbrd_gpio_remove(struct platform_device *pdev) +{ + struct vprbrd_gpio *vb_gpio = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(&vb_gpio->gpiob); + if (ret == 0) + ret = gpiochip_remove(&vb_gpio->gpioa); + + return ret; +} + +static struct platform_driver vprbrd_gpio_driver = { + .driver.name = "viperboard-gpio", + .driver.owner = THIS_MODULE, + .probe = vprbrd_gpio_probe, + .remove = __devexit_p(vprbrd_gpio_remove), +}; + +static int __init vprbrd_gpio_init(void) +{ + switch (gpioa_freq) { + case 1000000: + gpioa_clk = VPRBRD_GPIOA_CLK_1MHZ; + break; + case 100000: + gpioa_clk = VPRBRD_GPIOA_CLK_100KHZ; + break; + case 10000: + gpioa_clk = VPRBRD_GPIOA_CLK_10KHZ; + break; + case 1000: + gpioa_clk = VPRBRD_GPIOA_CLK_1KHZ; + break; + case 100: + gpioa_clk = VPRBRD_GPIOA_CLK_100HZ; + break; + case 10: + gpioa_clk = VPRBRD_GPIOA_CLK_10HZ; + break; + default: + pr_warn("invalid gpioa_freq (%d)\n", gpioa_freq); + gpioa_clk = VPRBRD_GPIOA_CLK_1KHZ; + } + + return platform_driver_register(&vprbrd_gpio_driver); +} +subsys_initcall(vprbrd_gpio_init); + +static void __exit vprbrd_gpio_exit(void) +{ + platform_driver_unregister(&vprbrd_gpio_driver); +} +module_exit(vprbrd_gpio_exit); + +MODULE_AUTHOR("Lars Poeschel "); +MODULE_DESCRIPTION("GPIO driver for Nano River Techs Viperboard"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:viperboard-gpio"); diff --git a/drivers/mfd/viperboard.c b/drivers/mfd/viperboard.c index cd1e2fe95647..c75ac08f639c 100644 --- a/drivers/mfd/viperboard.c +++ b/drivers/mfd/viperboard.c @@ -38,6 +38,9 @@ static const struct usb_device_id vprbrd_table[] = { MODULE_DEVICE_TABLE(usb, vprbrd_table); static struct mfd_cell vprbrd_devs[] = { + { + .name = "viperboard-gpio", + }, }; static int vprbrd_probe(struct usb_interface *interface, diff --git a/include/linux/mfd/viperboard.h b/include/linux/mfd/viperboard.h index 0d1342466db5..42d339fac3e5 100644 --- a/include/linux/mfd/viperboard.h +++ b/include/linux/mfd/viperboard.h @@ -44,6 +44,8 @@ #define VPRBRD_USB_TIMEOUT_MS 100 #define VPRBRD_USB_REQUEST_MAJOR 0xea #define VPRBRD_USB_REQUEST_MINOR 0xeb +#define VPRBRD_USB_REQUEST_GPIOA 0xed +#define VPRBRD_USB_REQUEST_GPIOB 0xdd struct vprbrd_i2c_write_hdr { u8 cmd; From 174a13aa8669331605b138aaf61569dd7189e453 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Mon, 19 Nov 2012 16:36:04 +0100 Subject: [PATCH 047/119] i2c: Add viperboard i2c master driver This adds the mfd cell to use the i2c part of the Nano River Technologies viperboard as i2c master. Signed-off-by: Lars Poeschel Signed-off-by: Samuel Ortiz --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-viperboard.c | 480 ++++++++++++++++++++++++++++ drivers/mfd/viperboard.c | 3 + include/linux/mfd/viperboard.h | 2 + 5 files changed, 496 insertions(+) create mode 100644 drivers/i2c/busses/i2c-viperboard.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index e9df4612b7eb..c7bff51fe524 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -818,6 +818,16 @@ config I2C_TINY_USB This driver can also be built as a module. If so, the module will be called i2c-tiny-usb. +config I2C_VIPERBOARD + tristate "Viperboard I2C master support" + depends on MFD_VIPERBOARD && USB + help + Say yes here to access the I2C part of the Nano River + Technologies Viperboard as I2C master. + See viperboard API specification and Nano + River Tech's viperboard.h for detailed meaning + of the module parameters. + comment "Other I2C/SMBus bus drivers" config I2C_ACORN diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 395b516ffa08..e5cb209d276c 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -79,6 +79,7 @@ obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o +obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o # Other I2C/SMBus bus drivers obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o diff --git a/drivers/i2c/busses/i2c-viperboard.c b/drivers/i2c/busses/i2c-viperboard.c new file mode 100644 index 000000000000..f5fa20dea906 --- /dev/null +++ b/drivers/i2c/busses/i2c-viperboard.c @@ -0,0 +1,480 @@ +/* + * Nano River Technologies viperboard i2c master driver + * + * (C) 2012 by Lemonage GmbH + * Author: Lars Poeschel + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +struct vprbrd_i2c { + struct i2c_adapter i2c; + u8 bus_freq_param; +}; + +/* i2c bus frequency module parameter */ +static u8 i2c_bus_param; +static unsigned int i2c_bus_freq = 100; +module_param(i2c_bus_freq, int, 0); +MODULE_PARM_DESC(i2c_bus_freq, + "i2c bus frequency in khz (default is 100) valid values: 10, 100, 200, 400, 1000, 3000, 6000"); + +static int vprbrd_i2c_status(struct i2c_adapter *i2c, + struct vprbrd_i2c_status *status, bool prev_error) +{ + u16 bytes_xfer; + int ret; + struct vprbrd *vb = (struct vprbrd *)i2c->algo_data; + + /* check for protocol error */ + bytes_xfer = sizeof(struct vprbrd_i2c_status); + + ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0), + VPRBRD_USB_REQUEST_I2C, VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, + status, bytes_xfer, VPRBRD_USB_TIMEOUT_MS); + + if (ret != bytes_xfer) + prev_error = true; + + if (prev_error) { + dev_err(&i2c->dev, "failure in usb communication\n"); + return -EREMOTEIO; + } + + dev_dbg(&i2c->dev, " status = %d\n", status->status); + if (status->status != 0x00) { + dev_err(&i2c->dev, "failure: i2c protocol error\n"); + return -EPROTO; + } + return 0; +} + +static int vprbrd_i2c_receive(struct usb_device *usb_dev, + struct vprbrd_i2c_read_msg *rmsg, int bytes_xfer) +{ + int ret, bytes_actual; + int error = 0; + + /* send the read request */ + ret = usb_bulk_msg(usb_dev, + usb_sndbulkpipe(usb_dev, VPRBRD_EP_OUT), rmsg, + sizeof(struct vprbrd_i2c_read_hdr), &bytes_actual, + VPRBRD_USB_TIMEOUT_MS); + + if ((ret < 0) + || (bytes_actual != sizeof(struct vprbrd_i2c_read_hdr))) { + dev_err(&usb_dev->dev, "failure transmitting usb\n"); + error = -EREMOTEIO; + } + + /* read the actual data */ + ret = usb_bulk_msg(usb_dev, + usb_rcvbulkpipe(usb_dev, VPRBRD_EP_IN), rmsg, + bytes_xfer, &bytes_actual, VPRBRD_USB_TIMEOUT_MS); + + if ((ret < 0) || (bytes_xfer != bytes_actual)) { + dev_err(&usb_dev->dev, "failure receiving usb\n"); + error = -EREMOTEIO; + } + return error; +} + +static int vprbrd_i2c_addr(struct usb_device *usb_dev, + struct vprbrd_i2c_addr_msg *amsg) +{ + int ret, bytes_actual; + + ret = usb_bulk_msg(usb_dev, + usb_sndbulkpipe(usb_dev, VPRBRD_EP_OUT), amsg, + sizeof(struct vprbrd_i2c_addr_msg), &bytes_actual, + VPRBRD_USB_TIMEOUT_MS); + + if ((ret < 0) || + (sizeof(struct vprbrd_i2c_addr_msg) != bytes_actual)) { + dev_err(&usb_dev->dev, "failure transmitting usb\n"); + return -EREMOTEIO; + } + return 0; +} + +static int vprbrd_i2c_read(struct vprbrd *vb, struct i2c_msg *msg) +{ + int ret; + u16 remain_len, bytes_xfer, len1, len2, + start = 0x0000; + struct vprbrd_i2c_read_msg *rmsg = + (struct vprbrd_i2c_read_msg *)vb->buf; + + remain_len = msg->len; + rmsg->header.cmd = VPRBRD_I2C_CMD_READ; + while (remain_len > 0) { + rmsg->header.addr = cpu_to_le16(start + 0x4000); + if (remain_len <= 255) { + len1 = remain_len; + len2 = 0x00; + rmsg->header.len0 = remain_len; + rmsg->header.len1 = 0x00; + rmsg->header.len2 = 0x00; + rmsg->header.len3 = 0x00; + rmsg->header.len4 = 0x00; + rmsg->header.len5 = 0x00; + remain_len = 0; + } else if (remain_len <= 510) { + len1 = remain_len; + len2 = 0x00; + rmsg->header.len0 = remain_len - 255; + rmsg->header.len1 = 0xff; + rmsg->header.len2 = 0x00; + rmsg->header.len3 = 0x00; + rmsg->header.len4 = 0x00; + rmsg->header.len5 = 0x00; + remain_len = 0; + } else if (remain_len <= 512) { + len1 = remain_len; + len2 = 0x00; + rmsg->header.len0 = remain_len - 510; + rmsg->header.len1 = 0xff; + rmsg->header.len2 = 0xff; + rmsg->header.len3 = 0x00; + rmsg->header.len4 = 0x00; + rmsg->header.len5 = 0x00; + remain_len = 0; + } else if (remain_len <= 767) { + len1 = 512; + len2 = remain_len - 512; + rmsg->header.len0 = 0x02; + rmsg->header.len1 = 0xff; + rmsg->header.len2 = 0xff; + rmsg->header.len3 = remain_len - 512; + rmsg->header.len4 = 0x00; + rmsg->header.len5 = 0x00; + bytes_xfer = remain_len; + remain_len = 0; + } else if (remain_len <= 1022) { + len1 = 512; + len2 = remain_len - 512; + rmsg->header.len0 = 0x02; + rmsg->header.len1 = 0xff; + rmsg->header.len2 = 0xff; + rmsg->header.len3 = remain_len - 767; + rmsg->header.len4 = 0xff; + rmsg->header.len5 = 0x00; + remain_len = 0; + } else if (remain_len <= 1024) { + len1 = 512; + len2 = remain_len - 512; + rmsg->header.len0 = 0x02; + rmsg->header.len1 = 0xff; + rmsg->header.len2 = 0xff; + rmsg->header.len3 = remain_len - 1022; + rmsg->header.len4 = 0xff; + rmsg->header.len5 = 0xff; + remain_len = 0; + } else { + len1 = 512; + len2 = 512; + rmsg->header.len0 = 0x02; + rmsg->header.len1 = 0xff; + rmsg->header.len2 = 0xff; + rmsg->header.len3 = 0x02; + rmsg->header.len4 = 0xff; + rmsg->header.len5 = 0xff; + remain_len -= 1024; + start += 1024; + } + rmsg->header.tf1 = cpu_to_le16(len1); + rmsg->header.tf2 = cpu_to_le16(len2); + + /* first read transfer */ + ret = vprbrd_i2c_receive(vb->usb_dev, rmsg, len1); + if (ret < 0) + return ret; + /* copy the received data */ + memcpy(msg->buf + start, rmsg, len1); + + /* second read transfer if neccessary */ + if (len2 > 0) { + ret = vprbrd_i2c_receive(vb->usb_dev, rmsg, len2); + if (ret < 0) + return ret; + /* copy the received data */ + memcpy(msg->buf + start + 512, rmsg, len2); + } + } + return 0; +} + +static int vprbrd_i2c_write(struct vprbrd *vb, struct i2c_msg *msg) +{ + int ret, bytes_actual; + u16 remain_len, bytes_xfer, + start = 0x0000; + struct vprbrd_i2c_write_msg *wmsg = + (struct vprbrd_i2c_write_msg *)vb->buf; + + remain_len = msg->len; + wmsg->header.cmd = VPRBRD_I2C_CMD_WRITE; + wmsg->header.last = 0x00; + wmsg->header.chan = 0x00; + wmsg->header.spi = 0x0000; + while (remain_len > 0) { + wmsg->header.addr = cpu_to_le16(start + 0x4000); + if (remain_len > 503) { + wmsg->header.len1 = 0xff; + wmsg->header.len2 = 0xf8; + remain_len -= 503; + bytes_xfer = 503 + sizeof(struct vprbrd_i2c_write_hdr); + start += 503; + } else if (remain_len > 255) { + wmsg->header.len1 = 0xff; + wmsg->header.len2 = (remain_len - 255); + bytes_xfer = remain_len + + sizeof(struct vprbrd_i2c_write_hdr); + remain_len = 0; + } else { + wmsg->header.len1 = remain_len; + wmsg->header.len2 = 0x00; + bytes_xfer = remain_len + + sizeof(struct vprbrd_i2c_write_hdr); + remain_len = 0; + } + memcpy(wmsg->data, msg->buf + start, + bytes_xfer - sizeof(struct vprbrd_i2c_write_hdr)); + + ret = usb_bulk_msg(vb->usb_dev, + usb_sndbulkpipe(vb->usb_dev, + VPRBRD_EP_OUT), wmsg, + bytes_xfer, &bytes_actual, VPRBRD_USB_TIMEOUT_MS); + if ((ret < 0) || (bytes_xfer != bytes_actual)) + return -EREMOTEIO; + } + return 0; +} + +static int vprbrd_i2c_xfer(struct i2c_adapter *i2c, struct i2c_msg *msgs, + int num) +{ + struct i2c_msg *pmsg; + int i, ret, + error = 0; + struct vprbrd *vb = (struct vprbrd *)i2c->algo_data; + struct vprbrd_i2c_addr_msg *amsg = + (struct vprbrd_i2c_addr_msg *)vb->buf; + struct vprbrd_i2c_status *smsg = (struct vprbrd_i2c_status *)vb->buf; + + dev_dbg(&i2c->dev, "master xfer %d messages:\n", num); + + for (i = 0 ; i < num ; i++) { + pmsg = &msgs[i]; + + dev_dbg(&i2c->dev, + " %d: %s (flags %d) %d bytes to 0x%02x\n", + i, pmsg->flags & I2C_M_RD ? "read" : "write", + pmsg->flags, pmsg->len, pmsg->addr); + + /* msgs longer than 2048 bytes are not supported by adapter */ + if (pmsg->len > 2048) + return -EINVAL; + + mutex_lock(&vb->lock); + /* directly send the message */ + if (pmsg->flags & I2C_M_RD) { + /* read data */ + amsg->cmd = VPRBRD_I2C_CMD_ADDR; + amsg->unknown2 = 0x00; + amsg->unknown3 = 0x00; + amsg->addr = pmsg->addr; + amsg->unknown1 = 0x01; + amsg->len = cpu_to_le16(pmsg->len); + /* send the addr and len, we're interested to board */ + ret = vprbrd_i2c_addr(vb->usb_dev, amsg); + if (ret < 0) + error = ret; + + ret = vprbrd_i2c_read(vb, pmsg); + if (ret < 0) + error = ret; + + ret = vprbrd_i2c_status(i2c, smsg, error); + if (ret < 0) + error = ret; + /* in case of protocol error, return the error */ + if (error < 0) + goto error; + } else { + /* write data */ + ret = vprbrd_i2c_write(vb, pmsg); + + amsg->cmd = VPRBRD_I2C_CMD_ADDR; + amsg->unknown2 = 0x00; + amsg->unknown3 = 0x00; + amsg->addr = pmsg->addr; + amsg->unknown1 = 0x00; + amsg->len = cpu_to_le16(pmsg->len); + /* send the addr, the data goes to to board */ + ret = vprbrd_i2c_addr(vb->usb_dev, amsg); + if (ret < 0) + error = ret; + + ret = vprbrd_i2c_status(i2c, smsg, error); + if (ret < 0) + error = ret; + + if (error < 0) + goto error; + } + mutex_unlock(&vb->lock); + } + return 0; +error: + mutex_unlock(&vb->lock); + return error; +} + +static u32 vprbrd_i2c_func(struct i2c_adapter *i2c) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +/* This is the actual algorithm we define */ +static const struct i2c_algorithm vprbrd_algorithm = { + .master_xfer = vprbrd_i2c_xfer, + .functionality = vprbrd_i2c_func, +}; + +static int __devinit vprbrd_i2c_probe(struct platform_device *pdev) +{ + struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent); + struct vprbrd_i2c *vb_i2c; + int ret; + int pipe; + + vb_i2c = kzalloc(sizeof(*vb_i2c), GFP_KERNEL); + if (vb_i2c == NULL) + return -ENOMEM; + + /* setup i2c adapter description */ + vb_i2c->i2c.owner = THIS_MODULE; + vb_i2c->i2c.class = I2C_CLASS_HWMON; + vb_i2c->i2c.algo = &vprbrd_algorithm; + vb_i2c->i2c.algo_data = vb; + /* save the param in usb capabable memory */ + vb_i2c->bus_freq_param = i2c_bus_param; + + snprintf(vb_i2c->i2c.name, sizeof(vb_i2c->i2c.name), + "viperboard at bus %03d device %03d", + vb->usb_dev->bus->busnum, vb->usb_dev->devnum); + + /* setting the bus frequency */ + if ((i2c_bus_param <= VPRBRD_I2C_FREQ_10KHZ) + && (i2c_bus_param >= VPRBRD_I2C_FREQ_6MHZ)) { + pipe = usb_sndctrlpipe(vb->usb_dev, 0); + ret = usb_control_msg(vb->usb_dev, pipe, + VPRBRD_USB_REQUEST_I2C_FREQ, VPRBRD_USB_TYPE_OUT, + 0x0000, 0x0000, &vb_i2c->bus_freq_param, 1, + VPRBRD_USB_TIMEOUT_MS); + if (ret != 1) { + dev_err(&pdev->dev, + "failure setting i2c_bus_freq to %d\n", i2c_bus_freq); + ret = -EIO; + goto error; + } + } else { + dev_err(&pdev->dev, + "invalid i2c_bus_freq setting:%d\n", i2c_bus_freq); + ret = -EIO; + goto error; + } + + vb_i2c->i2c.dev.parent = &pdev->dev; + + /* attach to i2c layer */ + i2c_add_adapter(&vb_i2c->i2c); + + platform_set_drvdata(pdev, vb_i2c); + + return 0; + +error: + kfree(vb_i2c); + return ret; +} + +static int __devexit vprbrd_i2c_remove(struct platform_device *pdev) +{ + struct vprbrd_i2c *vb_i2c = platform_get_drvdata(pdev); + int ret; + + ret = i2c_del_adapter(&vb_i2c->i2c); + + return ret; +} + +static struct platform_driver vprbrd_i2c_driver = { + .driver.name = "viperboard-i2c", + .driver.owner = THIS_MODULE, + .probe = vprbrd_i2c_probe, + .remove = __devexit_p(vprbrd_i2c_remove), +}; + +static int __init vprbrd_i2c_init(void) +{ + switch (i2c_bus_freq) { + case 6000: + i2c_bus_param = VPRBRD_I2C_FREQ_6MHZ; + break; + case 3000: + i2c_bus_param = VPRBRD_I2C_FREQ_3MHZ; + break; + case 1000: + i2c_bus_param = VPRBRD_I2C_FREQ_1MHZ; + break; + case 400: + i2c_bus_param = VPRBRD_I2C_FREQ_400KHZ; + break; + case 200: + i2c_bus_param = VPRBRD_I2C_FREQ_200KHZ; + break; + case 100: + i2c_bus_param = VPRBRD_I2C_FREQ_100KHZ; + break; + case 10: + i2c_bus_param = VPRBRD_I2C_FREQ_10KHZ; + break; + default: + pr_warn("invalid i2c_bus_freq (%d)\n", i2c_bus_freq); + i2c_bus_param = VPRBRD_I2C_FREQ_100KHZ; + } + + return platform_driver_register(&vprbrd_i2c_driver); +} +subsys_initcall(vprbrd_i2c_init); + +static void __exit vprbrd_i2c_exit(void) +{ + platform_driver_unregister(&vprbrd_i2c_driver); +} +module_exit(vprbrd_i2c_exit); + +MODULE_AUTHOR("Lars Poeschel "); +MODULE_DESCRIPTION("I2C master driver for Nano River Techs Viperboard"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:viperboard-i2c"); diff --git a/drivers/mfd/viperboard.c b/drivers/mfd/viperboard.c index c75ac08f639c..4f74ec868a52 100644 --- a/drivers/mfd/viperboard.c +++ b/drivers/mfd/viperboard.c @@ -41,6 +41,9 @@ static struct mfd_cell vprbrd_devs[] = { { .name = "viperboard-gpio", }, + { + .name = "viperboard-i2c", + }, }; static int vprbrd_probe(struct usb_interface *interface, diff --git a/include/linux/mfd/viperboard.h b/include/linux/mfd/viperboard.h index 42d339fac3e5..ef7851430d86 100644 --- a/include/linux/mfd/viperboard.h +++ b/include/linux/mfd/viperboard.h @@ -42,6 +42,8 @@ #define VPRBRD_USB_TYPE_OUT 0x40 #define VPRBRD_USB_TYPE_IN 0xc0 #define VPRBRD_USB_TIMEOUT_MS 100 +#define VPRBRD_USB_REQUEST_I2C_FREQ 0xe6 +#define VPRBRD_USB_REQUEST_I2C 0xe9 #define VPRBRD_USB_REQUEST_MAJOR 0xea #define VPRBRD_USB_REQUEST_MINOR 0xeb #define VPRBRD_USB_REQUEST_GPIOA 0xed From ffd8a6e7a77802dd09559be78dea63956aa8f1d5 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Mon, 5 Nov 2012 15:48:26 +0100 Subject: [PATCH 048/119] iio: adc: Add viperboard adc driver This adds the mfd cell to use the adc part of the Nano River Technologies viperboard. Signed-off-by: Lars Poeschel Reviewed-by: Lars-Peter Clausen Acked-by: Jonathan Cameron Signed-off-by: Samuel Ortiz --- drivers/iio/adc/Kconfig | 7 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/viperboard_adc.c | 181 +++++++++++++++++++++++++++++++ drivers/mfd/viperboard.c | 3 + include/linux/mfd/viperboard.h | 1 + 5 files changed, 193 insertions(+) create mode 100644 drivers/iio/adc/viperboard_adc.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 1401ed1af39f..bb3053739c80 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -67,4 +67,11 @@ config TI_AM335X_ADC Say yes here to build support for Texas Instruments ADC driver which is also a MFD client. +config VIPERBOARD_ADC + tristate "Viperboard ADC support" + depends on MFD_VIPERBOARD && USB + help + Say yes here to access the ADC part of the Nano River + Technologies Viperboard. + endmenu diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 4410a90fc84b..4268fa987fe6 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o +obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o \ No newline at end of file diff --git a/drivers/iio/adc/viperboard_adc.c b/drivers/iio/adc/viperboard_adc.c new file mode 100644 index 000000000000..10136a8b20d4 --- /dev/null +++ b/drivers/iio/adc/viperboard_adc.c @@ -0,0 +1,181 @@ +/* + * Nano River Technologies viperboard IIO ADC driver + * + * (C) 2012 by Lemonage GmbH + * Author: Lars Poeschel + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define VPRBRD_ADC_CMD_GET 0x00 + +struct vprbrd_adc_msg { + u8 cmd; + u8 chan; + u8 val; +} __packed; + +struct vprbrd_adc { + struct vprbrd *vb; +}; + +#define VPRBRD_ADC_CHANNEL(_index) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _index, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 8, \ + .storagebits = 8, \ + }, \ +} + +static struct iio_chan_spec const vprbrd_adc_iio_channels[] = { + VPRBRD_ADC_CHANNEL(0), + VPRBRD_ADC_CHANNEL(1), + VPRBRD_ADC_CHANNEL(2), + VPRBRD_ADC_CHANNEL(3), +}; + +static int vprbrd_iio_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long info) +{ + int ret, error = 0; + struct vprbrd_adc *adc = iio_priv(iio_dev); + struct vprbrd *vb = adc->vb; + struct vprbrd_adc_msg *admsg = (struct vprbrd_adc_msg *)vb->buf; + + switch (info) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&vb->lock); + + admsg->cmd = VPRBRD_ADC_CMD_GET; + admsg->chan = chan->scan_index; + admsg->val = 0x00; + + ret = usb_control_msg(vb->usb_dev, + usb_sndctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC, + VPRBRD_USB_TYPE_OUT, 0x0000, 0x0000, admsg, + sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS); + if (ret != sizeof(struct vprbrd_adc_msg)) { + dev_err(&iio_dev->dev, "usb send error on adc read\n"); + error = -EREMOTEIO; + } + + ret = usb_control_msg(vb->usb_dev, + usb_rcvctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC, + VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, admsg, + sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS); + + *val = admsg->val; + + mutex_unlock(&vb->lock); + + if (ret != sizeof(struct vprbrd_adc_msg)) { + dev_err(&iio_dev->dev, "usb recv error on adc read\n"); + error = -EREMOTEIO; + } + + if (error) + goto error; + + return IIO_VAL_INT; + default: + error = -EINVAL; + break; + } +error: + return error; +} + +static const struct iio_info vprbrd_adc_iio_info = { + .read_raw = &vprbrd_iio_read_raw, + .driver_module = THIS_MODULE, +}; + +static int __devinit vprbrd_adc_probe(struct platform_device *pdev) +{ + struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent); + struct vprbrd_adc *adc; + struct iio_dev *indio_dev; + int ret; + + /* registering iio */ + indio_dev = iio_device_alloc(sizeof(*adc)); + if (!indio_dev) { + dev_err(&pdev->dev, "failed allocating iio device\n"); + return -ENOMEM; + } + + adc = iio_priv(indio_dev); + adc->vb = vb; + indio_dev->name = "viperboard adc"; + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &vprbrd_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = vprbrd_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(vprbrd_adc_iio_channels); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "could not register iio (adc)"); + goto error; + } + + platform_set_drvdata(pdev, indio_dev); + + return 0; + +error: + iio_device_free(indio_dev); + return ret; +} + +static int __devexit vprbrd_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + iio_device_free(indio_dev); + + return 0; +} + +static struct platform_driver vprbrd_adc_driver = { + .driver = { + .name = "viperboard-adc", + .owner = THIS_MODULE, + }, + .probe = vprbrd_adc_probe, + .remove = __devexit_p(vprbrd_adc_remove), +}; + +module_platform_driver(vprbrd_adc_driver); + +MODULE_AUTHOR("Lars Poeschel "); +MODULE_DESCRIPTION("IIO ADC driver for Nano River Techs Viperboard"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:viperboard-adc"); diff --git a/drivers/mfd/viperboard.c b/drivers/mfd/viperboard.c index 4f74ec868a52..276122b37199 100644 --- a/drivers/mfd/viperboard.c +++ b/drivers/mfd/viperboard.c @@ -44,6 +44,9 @@ static struct mfd_cell vprbrd_devs[] = { { .name = "viperboard-i2c", }, + { + .name = "viperboard-adc", + }, }; static int vprbrd_probe(struct usb_interface *interface, diff --git a/include/linux/mfd/viperboard.h b/include/linux/mfd/viperboard.h index ef7851430d86..193452848c04 100644 --- a/include/linux/mfd/viperboard.h +++ b/include/linux/mfd/viperboard.h @@ -46,6 +46,7 @@ #define VPRBRD_USB_REQUEST_I2C 0xe9 #define VPRBRD_USB_REQUEST_MAJOR 0xea #define VPRBRD_USB_REQUEST_MINOR 0xeb +#define VPRBRD_USB_REQUEST_ADC 0xec #define VPRBRD_USB_REQUEST_GPIOA 0xed #define VPRBRD_USB_REQUEST_GPIOB 0xdd From 014483932b3f0dbecbb01da38de45b2c7a53cdfd Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Mon, 19 Nov 2012 11:48:17 +0800 Subject: [PATCH 049/119] mmc: rtsx: Remove a duplicate command in sd_rw_multi Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mmc/host/rtsx_pci_sdmmc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 12eff6f8cab7..0e934bf51309 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -382,8 +382,6 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) 0xFF, (u8)data->blocks); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, (u8)(data->blocks >> 8)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - CARD_DATA_SOURCE, 0x01, RING_BUFFER); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0, DMA_DONE_INT, DMA_DONE_INT); From 1950c7164646bfeeb82c34bc299d82119706afb5 Mon Sep 17 00:00:00 2001 From: Davide Ciminaghi Date: Fri, 9 Nov 2012 15:19:52 +0100 Subject: [PATCH 050/119] mfd: sta2x11-mfd: Add apb-soc regs driver and factor out common code A driver for the apb-soc registers is needed by the clock infrastructure code to configure and control clocks on the sta2x11 chip. Since some of the functions in sta2x11-mfd.c were almost identical for the two existing platform devices, the following changes have been performed to avoid further code duplication while adding the apb-soc-regs driver: * The sctl_regs and apbreg_regs fields in struct sta2x11_mfd have been turned into just one array of pointers accessed by device index. * Platform probe methods have become one-liners invoking a common probe with the device's index as second parameter. * For loops have been inserted where the same operations were performed for each of the two bars of a pci device. * The apbreg_mask and sctl_mask functions were almost identical, so they were turned into inline functions invoking a common __sta2x11_mfd_mask() with the platform device's index as last parameter. To do this, enum sta2x11_mfd_plat_dev has been declared in sta2x11-mfd.h and more device types have been added to it. Reviewed-by: Mark Brown Signed-off-by: Davide Ciminaghi Acked-by: Alessandro Rubini Signed-off-by: Samuel Ortiz --- drivers/mfd/sta2x11-mfd.c | 352 ++++++++++++++++++++++---------- include/linux/mfd/sta2x11-mfd.h | 156 +++++++++++++- 2 files changed, 396 insertions(+), 112 deletions(-) diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index d35da6820bea..9e01b84aa8bc 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -40,8 +40,7 @@ struct sta2x11_mfd { struct sta2x11_instance *instance; spinlock_t lock; struct list_head list; - void __iomem *sctl_regs; - void __iomem *apbreg_regs; + void __iomem *regs[sta2x11_n_mfd_plat_devs]; }; static LIST_HEAD(sta2x11_mfd_list); @@ -100,56 +99,33 @@ static int __devexit mfd_remove(struct pci_dev *pdev) return 0; } -/* These two functions are exported and are not expected to fail */ -u32 sta2x11_sctl_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val) +/* This function is exported and is not expected to fail */ +u32 __sta2x11_mfd_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val, + enum sta2x11_mfd_plat_dev index) { struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); u32 r; unsigned long flags; + void __iomem *regs = mfd->regs[index]; if (!mfd) { dev_warn(&pdev->dev, ": can't access sctl regs\n"); return 0; } - if (!mfd->sctl_regs) { + if (!regs) { dev_warn(&pdev->dev, ": system ctl not initialized\n"); return 0; } spin_lock_irqsave(&mfd->lock, flags); - r = readl(mfd->sctl_regs + reg); + r = readl(regs + reg); r &= ~mask; r |= val; if (mask) - writel(r, mfd->sctl_regs + reg); + writel(r, regs + reg); spin_unlock_irqrestore(&mfd->lock, flags); return r; } -EXPORT_SYMBOL(sta2x11_sctl_mask); - -u32 sta2x11_apbreg_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val) -{ - struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); - u32 r; - unsigned long flags; - - if (!mfd) { - dev_warn(&pdev->dev, ": can't access apb regs\n"); - return 0; - } - if (!mfd->apbreg_regs) { - dev_warn(&pdev->dev, ": apb bridge not initialized\n"); - return 0; - } - spin_lock_irqsave(&mfd->lock, flags); - r = readl(mfd->apbreg_regs + reg); - r &= ~mask; - r |= val; - if (mask) - writel(r, mfd->apbreg_regs + reg); - spin_unlock_irqrestore(&mfd->lock, flags); - return r; -} -EXPORT_SYMBOL(sta2x11_apbreg_mask); +EXPORT_SYMBOL(__sta2x11_mfd_mask); /* Two debugfs files, for our registers (FIXME: one instance only) */ #define REG(regname) {.name = #regname, .offset = SCTL_ ## regname} @@ -180,15 +156,101 @@ static struct debugfs_regset32 apbreg_regset = { .nregs = ARRAY_SIZE(sta2x11_apbreg_regs), }; -static struct dentry *sta2x11_sctl_debugfs; -static struct dentry *sta2x11_apbreg_debugfs; +#define REG(regname) {.name = #regname, .offset = regname} +static struct debugfs_reg32 sta2x11_apb_soc_regs_regs[] = { + REG(PCIE_EP1_FUNC3_0_INTR_REG), REG(PCIE_EP1_FUNC7_4_INTR_REG), + REG(PCIE_EP2_FUNC3_0_INTR_REG), REG(PCIE_EP2_FUNC7_4_INTR_REG), + REG(PCIE_EP3_FUNC3_0_INTR_REG), REG(PCIE_EP3_FUNC7_4_INTR_REG), + REG(PCIE_EP4_FUNC3_0_INTR_REG), REG(PCIE_EP4_FUNC7_4_INTR_REG), + REG(PCIE_INTR_ENABLE0_REG), REG(PCIE_INTR_ENABLE1_REG), + REG(PCIE_EP1_FUNC_TC_REG), REG(PCIE_EP2_FUNC_TC_REG), + REG(PCIE_EP3_FUNC_TC_REG), REG(PCIE_EP4_FUNC_TC_REG), + REG(PCIE_EP1_FUNC_F_REG), REG(PCIE_EP2_FUNC_F_REG), + REG(PCIE_EP3_FUNC_F_REG), REG(PCIE_EP4_FUNC_F_REG), + REG(PCIE_PAB_AMBA_SW_RST_REG), REG(PCIE_PM_STATUS_0_PORT_0_4), + REG(PCIE_PM_STATUS_7_0_EP1), REG(PCIE_PM_STATUS_7_0_EP2), + REG(PCIE_PM_STATUS_7_0_EP3), REG(PCIE_PM_STATUS_7_0_EP4), + REG(PCIE_DEV_ID_0_EP1_REG), REG(PCIE_CC_REV_ID_0_EP1_REG), + REG(PCIE_DEV_ID_1_EP1_REG), REG(PCIE_CC_REV_ID_1_EP1_REG), + REG(PCIE_DEV_ID_2_EP1_REG), REG(PCIE_CC_REV_ID_2_EP1_REG), + REG(PCIE_DEV_ID_3_EP1_REG), REG(PCIE_CC_REV_ID_3_EP1_REG), + REG(PCIE_DEV_ID_4_EP1_REG), REG(PCIE_CC_REV_ID_4_EP1_REG), + REG(PCIE_DEV_ID_5_EP1_REG), REG(PCIE_CC_REV_ID_5_EP1_REG), + REG(PCIE_DEV_ID_6_EP1_REG), REG(PCIE_CC_REV_ID_6_EP1_REG), + REG(PCIE_DEV_ID_7_EP1_REG), REG(PCIE_CC_REV_ID_7_EP1_REG), + REG(PCIE_DEV_ID_0_EP2_REG), REG(PCIE_CC_REV_ID_0_EP2_REG), + REG(PCIE_DEV_ID_1_EP2_REG), REG(PCIE_CC_REV_ID_1_EP2_REG), + REG(PCIE_DEV_ID_2_EP2_REG), REG(PCIE_CC_REV_ID_2_EP2_REG), + REG(PCIE_DEV_ID_3_EP2_REG), REG(PCIE_CC_REV_ID_3_EP2_REG), + REG(PCIE_DEV_ID_4_EP2_REG), REG(PCIE_CC_REV_ID_4_EP2_REG), + REG(PCIE_DEV_ID_5_EP2_REG), REG(PCIE_CC_REV_ID_5_EP2_REG), + REG(PCIE_DEV_ID_6_EP2_REG), REG(PCIE_CC_REV_ID_6_EP2_REG), + REG(PCIE_DEV_ID_7_EP2_REG), REG(PCIE_CC_REV_ID_7_EP2_REG), + REG(PCIE_DEV_ID_0_EP3_REG), REG(PCIE_CC_REV_ID_0_EP3_REG), + REG(PCIE_DEV_ID_1_EP3_REG), REG(PCIE_CC_REV_ID_1_EP3_REG), + REG(PCIE_DEV_ID_2_EP3_REG), REG(PCIE_CC_REV_ID_2_EP3_REG), + REG(PCIE_DEV_ID_3_EP3_REG), REG(PCIE_CC_REV_ID_3_EP3_REG), + REG(PCIE_DEV_ID_4_EP3_REG), REG(PCIE_CC_REV_ID_4_EP3_REG), + REG(PCIE_DEV_ID_5_EP3_REG), REG(PCIE_CC_REV_ID_5_EP3_REG), + REG(PCIE_DEV_ID_6_EP3_REG), REG(PCIE_CC_REV_ID_6_EP3_REG), + REG(PCIE_DEV_ID_7_EP3_REG), REG(PCIE_CC_REV_ID_7_EP3_REG), + REG(PCIE_DEV_ID_0_EP4_REG), REG(PCIE_CC_REV_ID_0_EP4_REG), + REG(PCIE_DEV_ID_1_EP4_REG), REG(PCIE_CC_REV_ID_1_EP4_REG), + REG(PCIE_DEV_ID_2_EP4_REG), REG(PCIE_CC_REV_ID_2_EP4_REG), + REG(PCIE_DEV_ID_3_EP4_REG), REG(PCIE_CC_REV_ID_3_EP4_REG), + REG(PCIE_DEV_ID_4_EP4_REG), REG(PCIE_CC_REV_ID_4_EP4_REG), + REG(PCIE_DEV_ID_5_EP4_REG), REG(PCIE_CC_REV_ID_5_EP4_REG), + REG(PCIE_DEV_ID_6_EP4_REG), REG(PCIE_CC_REV_ID_6_EP4_REG), + REG(PCIE_DEV_ID_7_EP4_REG), REG(PCIE_CC_REV_ID_7_EP4_REG), + REG(PCIE_SUBSYS_VEN_ID_REG), REG(PCIE_COMMON_CLOCK_CONFIG_0_4_0), + REG(PCIE_MIPHYP_SSC_EN_REG), REG(PCIE_MIPHYP_ADDR_REG), + REG(PCIE_L1_ASPM_READY_REG), REG(PCIE_EXT_CFG_RDY_REG), + REG(PCIE_SoC_INT_ROUTER_STATUS0_REG), + REG(PCIE_SoC_INT_ROUTER_STATUS1_REG), + REG(PCIE_SoC_INT_ROUTER_STATUS2_REG), + REG(PCIE_SoC_INT_ROUTER_STATUS3_REG), + REG(DMA_IP_CTRL_REG), REG(DISP_BRIDGE_PU_PD_CTRL_REG), + REG(VIP_PU_PD_CTRL_REG), REG(USB_MLB_PU_PD_CTRL_REG), + REG(SDIO_PU_PD_MISCFUNC_CTRL_REG1), REG(SDIO_PU_PD_MISCFUNC_CTRL_REG2), + REG(UART_PU_PD_CTRL_REG), REG(ARM_Lock), REG(SYS_IO_CHAR_REG1), + REG(SYS_IO_CHAR_REG2), REG(SATA_CORE_ID_REG), REG(SATA_CTRL_REG), + REG(I2C_HSFIX_MISC_REG), REG(SPARE2_RESERVED), REG(SPARE3_RESERVED), + REG(MASTER_LOCK_REG), REG(SYSTEM_CONFIG_STATUS_REG), + REG(MSP_CLK_CTRL_REG), REG(COMPENSATION_REG1), REG(COMPENSATION_REG2), + REG(COMPENSATION_REG3), REG(TEST_CTL_REG), +}; +#undef REG -/* Probe for the two platform devices */ -static int sta2x11_sctl_probe(struct platform_device *dev) +static struct debugfs_regset32 apb_soc_regs_regset = { + .regs = sta2x11_apb_soc_regs_regs, + .nregs = ARRAY_SIZE(sta2x11_apb_soc_regs_regs), +}; + + +static struct dentry *sta2x11_mfd_debugfs[sta2x11_n_mfd_plat_devs]; + +static struct debugfs_regset32 *sta2x11_mfd_regset[sta2x11_n_mfd_plat_devs] = { + [sta2x11_sctl] = &sctl_regset, + [sta2x11_apbreg] = &apbreg_regset, + [sta2x11_apb_soc_regs] = &apb_soc_regs_regset, +}; + +static const char *sta2x11_mfd_names[sta2x11_n_mfd_plat_devs] = { + [sta2x11_sctl] = "sta2x11-sctl", + [sta2x11_apbreg] = "sta2x11-apbreg", + [sta2x11_apb_soc_regs] = "sta2x11-apb-soc-regs", +}; + +/* Probe for the three platform devices */ + +static int sta2x11_mfd_platform_probe(struct platform_device *dev, + enum sta2x11_mfd_plat_dev index) { struct pci_dev **pdev; struct sta2x11_mfd *mfd; struct resource *res; + const char *name = sta2x11_mfd_names[index]; + struct debugfs_regset32 *regset = sta2x11_mfd_regset[index]; pdev = dev->dev.platform_data; mfd = sta2x11_mfd_find(*pdev); @@ -199,59 +261,37 @@ static int sta2x11_sctl_probe(struct platform_device *dev) if (!res) return -ENOMEM; - if (!request_mem_region(res->start, resource_size(res), - "sta2x11-sctl")) + if (!request_mem_region(res->start, resource_size(res), name)) return -EBUSY; - mfd->sctl_regs = ioremap(res->start, resource_size(res)); - if (!mfd->sctl_regs) { + mfd->regs[index] = ioremap(res->start, resource_size(res)); + if (!mfd->regs[index]) { release_mem_region(res->start, resource_size(res)); return -ENOMEM; } - sctl_regset.base = mfd->sctl_regs; - sta2x11_sctl_debugfs = debugfs_create_regset32("sta2x11-sctl", - S_IFREG | S_IRUGO, - NULL, &sctl_regset); + regset->base = mfd->regs[index]; + sta2x11_mfd_debugfs[index] = debugfs_create_regset32(name, + S_IFREG | S_IRUGO, + NULL, regset); return 0; } +static int sta2x11_sctl_probe(struct platform_device *dev) +{ + return sta2x11_mfd_platform_probe(dev, sta2x11_sctl); +} + static int sta2x11_apbreg_probe(struct platform_device *dev) { - struct pci_dev **pdev; - struct sta2x11_mfd *mfd; - struct resource *res; - - pdev = dev->dev.platform_data; - dev_dbg(&dev->dev, "%s: pdata is %p\n", __func__, pdev); - dev_dbg(&dev->dev, "%s: *pdata is %p\n", __func__, *pdev); - - mfd = sta2x11_mfd_find(*pdev); - if (!mfd) - return -ENODEV; - - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res) - return -ENOMEM; - - if (!request_mem_region(res->start, resource_size(res), - "sta2x11-apbreg")) - return -EBUSY; - - mfd->apbreg_regs = ioremap(res->start, resource_size(res)); - if (!mfd->apbreg_regs) { - release_mem_region(res->start, resource_size(res)); - return -ENOMEM; - } - dev_dbg(&dev->dev, "%s: regbase %p\n", __func__, mfd->apbreg_regs); - - apbreg_regset.base = mfd->apbreg_regs; - sta2x11_apbreg_debugfs = debugfs_create_regset32("sta2x11-apbreg", - S_IFREG | S_IRUGO, - NULL, &apbreg_regset); - return 0; + return sta2x11_mfd_platform_probe(dev, sta2x11_apbreg); } -/* The two platform drivers */ +static int sta2x11_apb_soc_regs_probe(struct platform_device *dev) +{ + return sta2x11_mfd_platform_probe(dev, sta2x11_apb_soc_regs); +} + +/* The three platform drivers */ static struct platform_driver sta2x11_sctl_platform_driver = { .driver = { .name = "sta2x11-sctl", @@ -280,13 +320,29 @@ static int __init sta2x11_apbreg_init(void) return platform_driver_register(&sta2x11_platform_driver); } +static struct platform_driver sta2x11_apb_soc_regs_platform_driver = { + .driver = { + .name = "sta2x11-apb-soc-regs", + .owner = THIS_MODULE, + }, + .probe = sta2x11_apb_soc_regs_probe, +}; + +static int __init sta2x11_apb_soc_regs_init(void) +{ + pr_info("%s\n", __func__); + return platform_driver_register(&sta2x11_apb_soc_regs_platform_driver); +} + /* - * What follows is the PCI device that hosts the above two pdevs. + * What follows are the PCI devices that host the above pdevs. * Each logic block is 4kB and they are all consecutive: we use this info. */ -/* Bar 0 */ -enum bar0_cells { +/* Mfd 0 device */ + +/* Mfd 0, Bar 0 */ +enum mfd0_bar0_cells { STA2X11_GPIO_0 = 0, STA2X11_GPIO_1, STA2X11_GPIO_2, @@ -295,8 +351,8 @@ enum bar0_cells { STA2X11_SCR, STA2X11_TIME, }; -/* Bar 1 */ -enum bar1_cells { +/* Mfd 0 , Bar 1 */ +enum mfd0_bar1_cells { STA2X11_APBREG = 0, }; #define CELL_4K(_name, _cell) { \ @@ -330,17 +386,46 @@ static const __devinitconst struct resource apbreg_resources[] = { #define DEV(_name, _r) \ { .name = _name, .num_resources = ARRAY_SIZE(_r), .resources = _r, } -static __devinitdata struct mfd_cell sta2x11_mfd_bar0[] = { +static __devinitdata struct mfd_cell sta2x11_mfd0_bar0[] = { DEV("sta2x11-gpio", gpio_resources), /* offset 0: we add pdata later */ DEV("sta2x11-sctl", sctl_resources), DEV("sta2x11-scr", scr_resources), DEV("sta2x11-time", time_resources), }; -static __devinitdata struct mfd_cell sta2x11_mfd_bar1[] = { +static __devinitdata struct mfd_cell sta2x11_mfd0_bar1[] = { DEV("sta2x11-apbreg", apbreg_resources), }; +/* Mfd 1 devices */ + +/* Mfd 1, Bar 0 */ +enum mfd1_bar0_cells { + STA2X11_VIC = 0, +}; + +/* Mfd 1, Bar 1 */ +enum mfd1_bar1_cells { + STA2X11_APB_SOC_REGS = 0, +}; + +static const __devinitconst struct resource vic_resources[] = { + CELL_4K("sta2x11-vic", STA2X11_VIC), +}; + +static const __devinitconst struct resource apb_soc_regs_resources[] = { + CELL_4K("sta2x11-apb-soc-regs", STA2X11_APB_SOC_REGS), +}; + +static __devinitdata struct mfd_cell sta2x11_mfd1_bar0[] = { + DEV("sta2x11-vic", vic_resources), +}; + +static __devinitdata struct mfd_cell sta2x11_mfd1_bar1[] = { + DEV("sta2x11-apb-soc-regs", apb_soc_regs_resources), +}; + + static int sta2x11_mfd_suspend(struct pci_dev *pdev, pm_message_t state) { pci_save_state(pdev); @@ -363,10 +448,63 @@ static int sta2x11_mfd_resume(struct pci_dev *pdev) return 0; } +struct sta2x11_mfd_bar_setup_data { + struct mfd_cell *cells; + int ncells; +}; + +struct sta2x11_mfd_setup_data { + struct sta2x11_mfd_bar_setup_data bars[2]; +}; + +#define STA2X11_MFD0 0 +#define STA2X11_MFD1 1 + +static struct sta2x11_mfd_setup_data mfd_setup_data[] = { + /* Mfd 0: gpio, sctl, scr, timers / apbregs */ + [STA2X11_MFD0] = { + .bars = { + [0] = { + .cells = sta2x11_mfd0_bar0, + .ncells = ARRAY_SIZE(sta2x11_mfd0_bar0), + }, + [1] = { + .cells = sta2x11_mfd0_bar1, + .ncells = ARRAY_SIZE(sta2x11_mfd0_bar1), + }, + }, + }, + /* Mfd 1: vic / apb-soc-regs */ + [STA2X11_MFD1] = { + .bars = { + [0] = { + .cells = sta2x11_mfd1_bar0, + .ncells = ARRAY_SIZE(sta2x11_mfd1_bar0), + }, + [1] = { + .cells = sta2x11_mfd1_bar1, + .ncells = ARRAY_SIZE(sta2x11_mfd1_bar1), + }, + }, + }, +}; + +static void __devinit sta2x11_mfd_setup(struct pci_dev *pdev, + struct sta2x11_mfd_setup_data *sd) +{ + int i, j; + for (i = 0; i < ARRAY_SIZE(sd->bars); i++) + for (j = 0; j < sd->bars[i].ncells; j++) { + sd->bars[i].cells[j].pdata_size = sizeof(pdev); + sd->bars[i].cells[j].platform_data = &pdev; + } +} + static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) { int err, i; + struct sta2x11_mfd_setup_data *setup_data; struct sta2x11_gpio_pdata *gpio_data; dev_info(&pdev->dev, "%s\n", __func__); @@ -381,6 +519,10 @@ static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev, if (err) dev_info(&pdev->dev, "Enable msi failed\n"); + setup_data = pci_id->device == PCI_DEVICE_ID_STMICRO_GPIO ? + &mfd_setup_data[STA2X11_MFD0] : + &mfd_setup_data[STA2X11_MFD1]; + /* Read gpio config data as pci device's platform data */ gpio_data = dev_get_platdata(&pdev->dev); if (!gpio_data) @@ -392,35 +534,23 @@ static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev, pdev, &pdev); /* platform data is the pci device for all of them */ - for (i = 0; i < ARRAY_SIZE(sta2x11_mfd_bar0); i++) { - sta2x11_mfd_bar0[i].pdata_size = sizeof(pdev); - sta2x11_mfd_bar0[i].platform_data = &pdev; - } - sta2x11_mfd_bar1[0].pdata_size = sizeof(pdev); - sta2x11_mfd_bar1[0].platform_data = &pdev; + sta2x11_mfd_setup(pdev, setup_data); /* Record this pdev before mfd_add_devices: their probe looks for it */ sta2x11_mfd_add(pdev, GFP_ATOMIC); - - err = mfd_add_devices(&pdev->dev, -1, - sta2x11_mfd_bar0, - ARRAY_SIZE(sta2x11_mfd_bar0), - &pdev->resource[0], - 0, NULL); - if (err) { - dev_err(&pdev->dev, "mfd_add_devices[0] failed: %d\n", err); - goto err_disable; - } - - err = mfd_add_devices(&pdev->dev, -1, - sta2x11_mfd_bar1, - ARRAY_SIZE(sta2x11_mfd_bar1), - &pdev->resource[1], - 0, NULL); - if (err) { - dev_err(&pdev->dev, "mfd_add_devices[1] failed: %d\n", err); - goto err_disable; + /* Just 2 bars for all mfd's at present */ + for (i = 0; i < 2; i++) { + err = mfd_add_devices(&pdev->dev, -1, + setup_data->bars[i].cells, + setup_data->bars[i].ncells, + &pdev->resource[i], + 0, NULL); + if (err) { + dev_err(&pdev->dev, + "mfd_add_devices[%d] failed: %d\n", i, err); + goto err_disable; + } } return 0; @@ -434,6 +564,7 @@ err_disable: static DEFINE_PCI_DEVICE_TABLE(sta2x11_mfd_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_GPIO)}, + {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIC)}, {0,}, }; @@ -459,6 +590,7 @@ static int __init sta2x11_mfd_init(void) */ subsys_initcall(sta2x11_apbreg_init); subsys_initcall(sta2x11_sctl_init); +subsys_initcall(sta2x11_apb_soc_regs_init); rootfs_initcall(sta2x11_mfd_init); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/sta2x11-mfd.h b/include/linux/mfd/sta2x11-mfd.h index d179227e866f..4d858797d7e2 100644 --- a/include/linux/mfd/sta2x11-mfd.h +++ b/include/linux/mfd/sta2x11-mfd.h @@ -26,6 +26,20 @@ #include #include +enum sta2x11_mfd_plat_dev { + sta2x11_sctl = 0, + sta2x11_gpio, + sta2x11_scr, + sta2x11_time, + sta2x11_apbreg, + sta2x11_apb_soc_regs, + sta2x11_vic, + sta2x11_n_mfd_plat_devs, +}; + +extern u32 +__sta2x11_mfd_mask(struct pci_dev *, u32, u32, u32, enum sta2x11_mfd_plat_dev); + /* * The MFD PCI block includes the GPIO peripherals and other register blocks. * For GPIO, we have 32*4 bits (I use "gsta" for "gpio sta2x11".) @@ -182,7 +196,11 @@ struct sta2x11_gpio_pdata { * The APB bridge has its own registers, needed by our users as well. * They are accessed with the following read/mask/write function. */ -u32 sta2x11_apbreg_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val); +static inline u32 +sta2x11_apbreg_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val) +{ + return __sta2x11_mfd_mask(pdev, reg, mask, val, sta2x11_apbreg); +} /* CAN and MLB */ #define APBREG_BSR 0x00 /* Bridge Status Reg */ @@ -211,7 +229,11 @@ u32 sta2x11_apbreg_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val); * The system controller has its own registers. Some of these are accessed * by out users as well, using the following read/mask/write/function */ -u32 sta2x11_sctl_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val); +static inline +u32 sta2x11_sctl_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val) +{ + return __sta2x11_mfd_mask(pdev, reg, mask, val, sta2x11_sctl); +} #define SCTL_SCCTL 0x00 /* System controller control register */ #define SCTL_ARMCFG 0x04 /* ARM configuration register */ @@ -321,4 +343,134 @@ u32 sta2x11_sctl_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val); #define SCTL_SCPEREN1_I2C3 (1 << 16) #define SCTL_SCPEREN1_USB_PHY (1 << 17) +/* + * APB-SOC registers + */ +static inline +u32 sta2x11_apb_soc_regs_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val) +{ + return __sta2x11_mfd_mask(pdev, reg, mask, val, sta2x11_apb_soc_regs); +} + +#define PCIE_EP1_FUNC3_0_INTR_REG 0x000 +#define PCIE_EP1_FUNC7_4_INTR_REG 0x004 +#define PCIE_EP2_FUNC3_0_INTR_REG 0x008 +#define PCIE_EP2_FUNC7_4_INTR_REG 0x00c +#define PCIE_EP3_FUNC3_0_INTR_REG 0x010 +#define PCIE_EP3_FUNC7_4_INTR_REG 0x014 +#define PCIE_EP4_FUNC3_0_INTR_REG 0x018 +#define PCIE_EP4_FUNC7_4_INTR_REG 0x01c +#define PCIE_INTR_ENABLE0_REG 0x020 +#define PCIE_INTR_ENABLE1_REG 0x024 +#define PCIE_EP1_FUNC_TC_REG 0x028 +#define PCIE_EP2_FUNC_TC_REG 0x02c +#define PCIE_EP3_FUNC_TC_REG 0x030 +#define PCIE_EP4_FUNC_TC_REG 0x034 +#define PCIE_EP1_FUNC_F_REG 0x038 +#define PCIE_EP2_FUNC_F_REG 0x03c +#define PCIE_EP3_FUNC_F_REG 0x040 +#define PCIE_EP4_FUNC_F_REG 0x044 +#define PCIE_PAB_AMBA_SW_RST_REG 0x048 +#define PCIE_PM_STATUS_0_PORT_0_4 0x04c +#define PCIE_PM_STATUS_7_0_EP1 0x050 +#define PCIE_PM_STATUS_7_0_EP2 0x054 +#define PCIE_PM_STATUS_7_0_EP3 0x058 +#define PCIE_PM_STATUS_7_0_EP4 0x05c +#define PCIE_DEV_ID_0_EP1_REG 0x060 +#define PCIE_CC_REV_ID_0_EP1_REG 0x064 +#define PCIE_DEV_ID_1_EP1_REG 0x068 +#define PCIE_CC_REV_ID_1_EP1_REG 0x06c +#define PCIE_DEV_ID_2_EP1_REG 0x070 +#define PCIE_CC_REV_ID_2_EP1_REG 0x074 +#define PCIE_DEV_ID_3_EP1_REG 0x078 +#define PCIE_CC_REV_ID_3_EP1_REG 0x07c +#define PCIE_DEV_ID_4_EP1_REG 0x080 +#define PCIE_CC_REV_ID_4_EP1_REG 0x084 +#define PCIE_DEV_ID_5_EP1_REG 0x088 +#define PCIE_CC_REV_ID_5_EP1_REG 0x08c +#define PCIE_DEV_ID_6_EP1_REG 0x090 +#define PCIE_CC_REV_ID_6_EP1_REG 0x094 +#define PCIE_DEV_ID_7_EP1_REG 0x098 +#define PCIE_CC_REV_ID_7_EP1_REG 0x09c +#define PCIE_DEV_ID_0_EP2_REG 0x0a0 +#define PCIE_CC_REV_ID_0_EP2_REG 0x0a4 +#define PCIE_DEV_ID_1_EP2_REG 0x0a8 +#define PCIE_CC_REV_ID_1_EP2_REG 0x0ac +#define PCIE_DEV_ID_2_EP2_REG 0x0b0 +#define PCIE_CC_REV_ID_2_EP2_REG 0x0b4 +#define PCIE_DEV_ID_3_EP2_REG 0x0b8 +#define PCIE_CC_REV_ID_3_EP2_REG 0x0bc +#define PCIE_DEV_ID_4_EP2_REG 0x0c0 +#define PCIE_CC_REV_ID_4_EP2_REG 0x0c4 +#define PCIE_DEV_ID_5_EP2_REG 0x0c8 +#define PCIE_CC_REV_ID_5_EP2_REG 0x0cc +#define PCIE_DEV_ID_6_EP2_REG 0x0d0 +#define PCIE_CC_REV_ID_6_EP2_REG 0x0d4 +#define PCIE_DEV_ID_7_EP2_REG 0x0d8 +#define PCIE_CC_REV_ID_7_EP2_REG 0x0dC +#define PCIE_DEV_ID_0_EP3_REG 0x0e0 +#define PCIE_CC_REV_ID_0_EP3_REG 0x0e4 +#define PCIE_DEV_ID_1_EP3_REG 0x0e8 +#define PCIE_CC_REV_ID_1_EP3_REG 0x0ec +#define PCIE_DEV_ID_2_EP3_REG 0x0f0 +#define PCIE_CC_REV_ID_2_EP3_REG 0x0f4 +#define PCIE_DEV_ID_3_EP3_REG 0x0f8 +#define PCIE_CC_REV_ID_3_EP3_REG 0x0fc +#define PCIE_DEV_ID_4_EP3_REG 0x100 +#define PCIE_CC_REV_ID_4_EP3_REG 0x104 +#define PCIE_DEV_ID_5_EP3_REG 0x108 +#define PCIE_CC_REV_ID_5_EP3_REG 0x10c +#define PCIE_DEV_ID_6_EP3_REG 0x110 +#define PCIE_CC_REV_ID_6_EP3_REG 0x114 +#define PCIE_DEV_ID_7_EP3_REG 0x118 +#define PCIE_CC_REV_ID_7_EP3_REG 0x11c +#define PCIE_DEV_ID_0_EP4_REG 0x120 +#define PCIE_CC_REV_ID_0_EP4_REG 0x124 +#define PCIE_DEV_ID_1_EP4_REG 0x128 +#define PCIE_CC_REV_ID_1_EP4_REG 0x12c +#define PCIE_DEV_ID_2_EP4_REG 0x130 +#define PCIE_CC_REV_ID_2_EP4_REG 0x134 +#define PCIE_DEV_ID_3_EP4_REG 0x138 +#define PCIE_CC_REV_ID_3_EP4_REG 0x13c +#define PCIE_DEV_ID_4_EP4_REG 0x140 +#define PCIE_CC_REV_ID_4_EP4_REG 0x144 +#define PCIE_DEV_ID_5_EP4_REG 0x148 +#define PCIE_CC_REV_ID_5_EP4_REG 0x14c +#define PCIE_DEV_ID_6_EP4_REG 0x150 +#define PCIE_CC_REV_ID_6_EP4_REG 0x154 +#define PCIE_DEV_ID_7_EP4_REG 0x158 +#define PCIE_CC_REV_ID_7_EP4_REG 0x15c +#define PCIE_SUBSYS_VEN_ID_REG 0x160 +#define PCIE_COMMON_CLOCK_CONFIG_0_4_0 0x164 +#define PCIE_MIPHYP_SSC_EN_REG 0x168 +#define PCIE_MIPHYP_ADDR_REG 0x16c +#define PCIE_L1_ASPM_READY_REG 0x170 +#define PCIE_EXT_CFG_RDY_REG 0x174 +#define PCIE_SoC_INT_ROUTER_STATUS0_REG 0x178 +#define PCIE_SoC_INT_ROUTER_STATUS1_REG 0x17c +#define PCIE_SoC_INT_ROUTER_STATUS2_REG 0x180 +#define PCIE_SoC_INT_ROUTER_STATUS3_REG 0x184 +#define DMA_IP_CTRL_REG 0x324 +#define DISP_BRIDGE_PU_PD_CTRL_REG 0x328 +#define VIP_PU_PD_CTRL_REG 0x32c +#define USB_MLB_PU_PD_CTRL_REG 0x330 +#define SDIO_PU_PD_MISCFUNC_CTRL_REG1 0x334 +#define SDIO_PU_PD_MISCFUNC_CTRL_REG2 0x338 +#define UART_PU_PD_CTRL_REG 0x33c +#define ARM_Lock 0x340 +#define SYS_IO_CHAR_REG1 0x344 +#define SYS_IO_CHAR_REG2 0x348 +#define SATA_CORE_ID_REG 0x34c +#define SATA_CTRL_REG 0x350 +#define I2C_HSFIX_MISC_REG 0x354 +#define SPARE2_RESERVED 0x358 +#define SPARE3_RESERVED 0x35c +#define MASTER_LOCK_REG 0x368 +#define SYSTEM_CONFIG_STATUS_REG 0x36c +#define MSP_CLK_CTRL_REG 0x39c +#define COMPENSATION_REG1 0x3c4 +#define COMPENSATION_REG2 0x3c8 +#define COMPENSATION_REG3 0x3cc +#define TEST_CTL_REG 0x3d0 + #endif /* __STA2X11_MFD_H */ From d94e25535a7979a6c81922496f475a5dd0e006b4 Mon Sep 17 00:00:00 2001 From: Davide Ciminaghi Date: Fri, 9 Nov 2012 15:19:53 +0100 Subject: [PATCH 051/119] mfd: sta2x11-mfd: Add regmap support Signed-off-by: Davide Ciminaghi Acked-by: Alessandro Rubini Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 1 + drivers/mfd/sta2x11-mfd.c | 246 ++++++++++++++++++-------------- include/linux/mfd/sta2x11-mfd.h | 1 + 3 files changed, 137 insertions(+), 111 deletions(-) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b280639a7634..59359a7a2493 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1046,6 +1046,7 @@ config MFD_STA2X11 bool "STA2X11 multi function device support" depends on STA2X11 select MFD_CORE + select REGMAP_MMIO config MFD_SYSCON bool "System Controller Register R/W Based on Regmap" diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index 9e01b84aa8bc..0cac2013bbf6 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -27,17 +27,25 @@ #include #include #include -#include #include #include #include #include +#include #include +static inline int __reg_within_range(unsigned int r, + unsigned int start, + unsigned int end) +{ + return ((r >= start) && (r <= end)); +} + /* This describes STA2X11 MFD chip for us, we may have several */ struct sta2x11_mfd { struct sta2x11_instance *instance; + struct regmap *regmap[sta2x11_n_mfd_plat_devs]; spinlock_t lock; struct list_head list; void __iomem *regs[sta2x11_n_mfd_plat_devs]; @@ -127,113 +135,21 @@ u32 __sta2x11_mfd_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val, } EXPORT_SYMBOL(__sta2x11_mfd_mask); -/* Two debugfs files, for our registers (FIXME: one instance only) */ -#define REG(regname) {.name = #regname, .offset = SCTL_ ## regname} -static struct debugfs_reg32 sta2x11_sctl_regs[] = { - REG(SCCTL), REG(ARMCFG), REG(SCPLLCTL), REG(SCPLLFCTRL), - REG(SCRESFRACT), REG(SCRESCTRL1), REG(SCRESXTRL2), REG(SCPEREN0), - REG(SCPEREN1), REG(SCPEREN2), REG(SCGRST), REG(SCPCIPMCR1), - REG(SCPCIPMCR2), REG(SCPCIPMSR1), REG(SCPCIPMSR2), REG(SCPCIPMSR3), - REG(SCINTREN), REG(SCRISR), REG(SCCLKSTAT0), REG(SCCLKSTAT1), - REG(SCCLKSTAT2), REG(SCRSTSTA), -}; -#undef REG +/* + * Special sta2x11-mfd regmap lock/unlock functions + */ -static struct debugfs_regset32 sctl_regset = { - .regs = sta2x11_sctl_regs, - .nregs = ARRAY_SIZE(sta2x11_sctl_regs), -}; +static void sta2x11_regmap_lock(void *__lock) +{ + spinlock_t *lock = __lock; + spin_lock(lock); +} -#define REG(regname) {.name = #regname, .offset = regname} -static struct debugfs_reg32 sta2x11_apbreg_regs[] = { - REG(APBREG_BSR), REG(APBREG_PAER), REG(APBREG_PWAC), REG(APBREG_PRAC), - REG(APBREG_PCG), REG(APBREG_PUR), REG(APBREG_EMU_PCG), -}; -#undef REG - -static struct debugfs_regset32 apbreg_regset = { - .regs = sta2x11_apbreg_regs, - .nregs = ARRAY_SIZE(sta2x11_apbreg_regs), -}; - -#define REG(regname) {.name = #regname, .offset = regname} -static struct debugfs_reg32 sta2x11_apb_soc_regs_regs[] = { - REG(PCIE_EP1_FUNC3_0_INTR_REG), REG(PCIE_EP1_FUNC7_4_INTR_REG), - REG(PCIE_EP2_FUNC3_0_INTR_REG), REG(PCIE_EP2_FUNC7_4_INTR_REG), - REG(PCIE_EP3_FUNC3_0_INTR_REG), REG(PCIE_EP3_FUNC7_4_INTR_REG), - REG(PCIE_EP4_FUNC3_0_INTR_REG), REG(PCIE_EP4_FUNC7_4_INTR_REG), - REG(PCIE_INTR_ENABLE0_REG), REG(PCIE_INTR_ENABLE1_REG), - REG(PCIE_EP1_FUNC_TC_REG), REG(PCIE_EP2_FUNC_TC_REG), - REG(PCIE_EP3_FUNC_TC_REG), REG(PCIE_EP4_FUNC_TC_REG), - REG(PCIE_EP1_FUNC_F_REG), REG(PCIE_EP2_FUNC_F_REG), - REG(PCIE_EP3_FUNC_F_REG), REG(PCIE_EP4_FUNC_F_REG), - REG(PCIE_PAB_AMBA_SW_RST_REG), REG(PCIE_PM_STATUS_0_PORT_0_4), - REG(PCIE_PM_STATUS_7_0_EP1), REG(PCIE_PM_STATUS_7_0_EP2), - REG(PCIE_PM_STATUS_7_0_EP3), REG(PCIE_PM_STATUS_7_0_EP4), - REG(PCIE_DEV_ID_0_EP1_REG), REG(PCIE_CC_REV_ID_0_EP1_REG), - REG(PCIE_DEV_ID_1_EP1_REG), REG(PCIE_CC_REV_ID_1_EP1_REG), - REG(PCIE_DEV_ID_2_EP1_REG), REG(PCIE_CC_REV_ID_2_EP1_REG), - REG(PCIE_DEV_ID_3_EP1_REG), REG(PCIE_CC_REV_ID_3_EP1_REG), - REG(PCIE_DEV_ID_4_EP1_REG), REG(PCIE_CC_REV_ID_4_EP1_REG), - REG(PCIE_DEV_ID_5_EP1_REG), REG(PCIE_CC_REV_ID_5_EP1_REG), - REG(PCIE_DEV_ID_6_EP1_REG), REG(PCIE_CC_REV_ID_6_EP1_REG), - REG(PCIE_DEV_ID_7_EP1_REG), REG(PCIE_CC_REV_ID_7_EP1_REG), - REG(PCIE_DEV_ID_0_EP2_REG), REG(PCIE_CC_REV_ID_0_EP2_REG), - REG(PCIE_DEV_ID_1_EP2_REG), REG(PCIE_CC_REV_ID_1_EP2_REG), - REG(PCIE_DEV_ID_2_EP2_REG), REG(PCIE_CC_REV_ID_2_EP2_REG), - REG(PCIE_DEV_ID_3_EP2_REG), REG(PCIE_CC_REV_ID_3_EP2_REG), - REG(PCIE_DEV_ID_4_EP2_REG), REG(PCIE_CC_REV_ID_4_EP2_REG), - REG(PCIE_DEV_ID_5_EP2_REG), REG(PCIE_CC_REV_ID_5_EP2_REG), - REG(PCIE_DEV_ID_6_EP2_REG), REG(PCIE_CC_REV_ID_6_EP2_REG), - REG(PCIE_DEV_ID_7_EP2_REG), REG(PCIE_CC_REV_ID_7_EP2_REG), - REG(PCIE_DEV_ID_0_EP3_REG), REG(PCIE_CC_REV_ID_0_EP3_REG), - REG(PCIE_DEV_ID_1_EP3_REG), REG(PCIE_CC_REV_ID_1_EP3_REG), - REG(PCIE_DEV_ID_2_EP3_REG), REG(PCIE_CC_REV_ID_2_EP3_REG), - REG(PCIE_DEV_ID_3_EP3_REG), REG(PCIE_CC_REV_ID_3_EP3_REG), - REG(PCIE_DEV_ID_4_EP3_REG), REG(PCIE_CC_REV_ID_4_EP3_REG), - REG(PCIE_DEV_ID_5_EP3_REG), REG(PCIE_CC_REV_ID_5_EP3_REG), - REG(PCIE_DEV_ID_6_EP3_REG), REG(PCIE_CC_REV_ID_6_EP3_REG), - REG(PCIE_DEV_ID_7_EP3_REG), REG(PCIE_CC_REV_ID_7_EP3_REG), - REG(PCIE_DEV_ID_0_EP4_REG), REG(PCIE_CC_REV_ID_0_EP4_REG), - REG(PCIE_DEV_ID_1_EP4_REG), REG(PCIE_CC_REV_ID_1_EP4_REG), - REG(PCIE_DEV_ID_2_EP4_REG), REG(PCIE_CC_REV_ID_2_EP4_REG), - REG(PCIE_DEV_ID_3_EP4_REG), REG(PCIE_CC_REV_ID_3_EP4_REG), - REG(PCIE_DEV_ID_4_EP4_REG), REG(PCIE_CC_REV_ID_4_EP4_REG), - REG(PCIE_DEV_ID_5_EP4_REG), REG(PCIE_CC_REV_ID_5_EP4_REG), - REG(PCIE_DEV_ID_6_EP4_REG), REG(PCIE_CC_REV_ID_6_EP4_REG), - REG(PCIE_DEV_ID_7_EP4_REG), REG(PCIE_CC_REV_ID_7_EP4_REG), - REG(PCIE_SUBSYS_VEN_ID_REG), REG(PCIE_COMMON_CLOCK_CONFIG_0_4_0), - REG(PCIE_MIPHYP_SSC_EN_REG), REG(PCIE_MIPHYP_ADDR_REG), - REG(PCIE_L1_ASPM_READY_REG), REG(PCIE_EXT_CFG_RDY_REG), - REG(PCIE_SoC_INT_ROUTER_STATUS0_REG), - REG(PCIE_SoC_INT_ROUTER_STATUS1_REG), - REG(PCIE_SoC_INT_ROUTER_STATUS2_REG), - REG(PCIE_SoC_INT_ROUTER_STATUS3_REG), - REG(DMA_IP_CTRL_REG), REG(DISP_BRIDGE_PU_PD_CTRL_REG), - REG(VIP_PU_PD_CTRL_REG), REG(USB_MLB_PU_PD_CTRL_REG), - REG(SDIO_PU_PD_MISCFUNC_CTRL_REG1), REG(SDIO_PU_PD_MISCFUNC_CTRL_REG2), - REG(UART_PU_PD_CTRL_REG), REG(ARM_Lock), REG(SYS_IO_CHAR_REG1), - REG(SYS_IO_CHAR_REG2), REG(SATA_CORE_ID_REG), REG(SATA_CTRL_REG), - REG(I2C_HSFIX_MISC_REG), REG(SPARE2_RESERVED), REG(SPARE3_RESERVED), - REG(MASTER_LOCK_REG), REG(SYSTEM_CONFIG_STATUS_REG), - REG(MSP_CLK_CTRL_REG), REG(COMPENSATION_REG1), REG(COMPENSATION_REG2), - REG(COMPENSATION_REG3), REG(TEST_CTL_REG), -}; -#undef REG - -static struct debugfs_regset32 apb_soc_regs_regset = { - .regs = sta2x11_apb_soc_regs_regs, - .nregs = ARRAY_SIZE(sta2x11_apb_soc_regs_regs), -}; - - -static struct dentry *sta2x11_mfd_debugfs[sta2x11_n_mfd_plat_devs]; - -static struct debugfs_regset32 *sta2x11_mfd_regset[sta2x11_n_mfd_plat_devs] = { - [sta2x11_sctl] = &sctl_regset, - [sta2x11_apbreg] = &apbreg_regset, - [sta2x11_apb_soc_regs] = &apb_soc_regs_regset, -}; +static void sta2x11_regmap_unlock(void *__lock) +{ + spinlock_t *lock = __lock; + spin_unlock(lock); +} static const char *sta2x11_mfd_names[sta2x11_n_mfd_plat_devs] = { [sta2x11_sctl] = "sta2x11-sctl", @@ -241,6 +157,106 @@ static const char *sta2x11_mfd_names[sta2x11_n_mfd_plat_devs] = { [sta2x11_apb_soc_regs] = "sta2x11-apb-soc-regs", }; +static bool sta2x11_sctl_writeable_reg(struct device *dev, unsigned int reg) +{ + return !__reg_within_range(reg, SCTL_SCPCIECSBRST, SCTL_SCRSTSTA); +} + +static struct regmap_config sta2x11_sctl_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .lock = sta2x11_regmap_lock, + .unlock = sta2x11_regmap_unlock, + .max_register = SCTL_SCRSTSTA, + .writeable_reg = sta2x11_sctl_writeable_reg, +}; + +static bool sta2x11_apbreg_readable_reg(struct device *dev, unsigned int reg) +{ + /* Two blocks (CAN and MLB, SARAC) 0x100 bytes apart */ + if (reg >= APBREG_BSR_SARAC) + reg -= APBREG_BSR_SARAC; + switch (reg) { + case APBREG_BSR: + case APBREG_PAER: + case APBREG_PWAC: + case APBREG_PRAC: + case APBREG_PCG: + case APBREG_PUR: + case APBREG_EMU_PCG: + return true; + default: + return false; + } +} + +static bool sta2x11_apbreg_writeable_reg(struct device *dev, unsigned int reg) +{ + if (reg >= APBREG_BSR_SARAC) + reg -= APBREG_BSR_SARAC; + if (!sta2x11_apbreg_readable_reg(dev, reg)) + return false; + return reg != APBREG_PAER; +} + +static struct regmap_config sta2x11_apbreg_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .lock = sta2x11_regmap_lock, + .unlock = sta2x11_regmap_unlock, + .max_register = APBREG_EMU_PCG_SARAC, + .readable_reg = sta2x11_apbreg_readable_reg, + .writeable_reg = sta2x11_apbreg_writeable_reg, +}; + +static bool sta2x11_apb_soc_regs_readable_reg(struct device *dev, + unsigned int reg) +{ + return reg <= PCIE_SoC_INT_ROUTER_STATUS3_REG || + __reg_within_range(reg, DMA_IP_CTRL_REG, SPARE3_RESERVED) || + __reg_within_range(reg, MASTER_LOCK_REG, + SYSTEM_CONFIG_STATUS_REG) || + reg == MSP_CLK_CTRL_REG || + __reg_within_range(reg, COMPENSATION_REG1, TEST_CTL_REG); +} + +static bool sta2x11_apb_soc_regs_writeable_reg(struct device *dev, + unsigned int reg) +{ + if (!sta2x11_apb_soc_regs_readable_reg(dev, reg)) + return false; + switch (reg) { + case PCIE_COMMON_CLOCK_CONFIG_0_4_0: + case SYSTEM_CONFIG_STATUS_REG: + case COMPENSATION_REG1: + case PCIE_SoC_INT_ROUTER_STATUS0_REG...PCIE_SoC_INT_ROUTER_STATUS3_REG: + case PCIE_PM_STATUS_0_PORT_0_4...PCIE_PM_STATUS_7_0_EP4: + return false; + default: + return true; + } +} + +static struct regmap_config sta2x11_apb_soc_regs_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .lock = sta2x11_regmap_lock, + .unlock = sta2x11_regmap_unlock, + .max_register = TEST_CTL_REG, + .readable_reg = sta2x11_apb_soc_regs_readable_reg, + .writeable_reg = sta2x11_apb_soc_regs_writeable_reg, +}; + +static struct regmap_config * +sta2x11_mfd_regmap_configs[sta2x11_n_mfd_plat_devs] = { + [sta2x11_sctl] = &sta2x11_sctl_regmap_config, + [sta2x11_apbreg] = &sta2x11_apbreg_regmap_config, + [sta2x11_apb_soc_regs] = &sta2x11_apb_soc_regs_regmap_config, +}; + /* Probe for the three platform devices */ static int sta2x11_mfd_platform_probe(struct platform_device *dev, @@ -250,12 +266,14 @@ static int sta2x11_mfd_platform_probe(struct platform_device *dev, struct sta2x11_mfd *mfd; struct resource *res; const char *name = sta2x11_mfd_names[index]; - struct debugfs_regset32 *regset = sta2x11_mfd_regset[index]; + struct regmap_config *regmap_config = sta2x11_mfd_regmap_configs[index]; pdev = dev->dev.platform_data; mfd = sta2x11_mfd_find(*pdev); if (!mfd) return -ENODEV; + if (!regmap_config) + return -ENODEV; res = platform_get_resource(dev, IORESOURCE_MEM, 0); if (!res) @@ -269,10 +287,16 @@ static int sta2x11_mfd_platform_probe(struct platform_device *dev, release_mem_region(res->start, resource_size(res)); return -ENOMEM; } - regset->base = mfd->regs[index]; - sta2x11_mfd_debugfs[index] = debugfs_create_regset32(name, - S_IFREG | S_IRUGO, - NULL, regset); + regmap_config->lock_arg = &mfd->lock; + /* + No caching, registers could be reached both via regmap and via + void __iomem * + */ + regmap_config->cache_type = REGCACHE_NONE; + mfd->regmap[index] = devm_regmap_init_mmio(&dev->dev, mfd->regs[index], + regmap_config); + WARN_ON(!mfd->regmap[index]); + return 0; } diff --git a/include/linux/mfd/sta2x11-mfd.h b/include/linux/mfd/sta2x11-mfd.h index 4d858797d7e2..83344304f2cf 100644 --- a/include/linux/mfd/sta2x11-mfd.h +++ b/include/linux/mfd/sta2x11-mfd.h @@ -246,6 +246,7 @@ u32 sta2x11_sctl_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val) #define SCTL_SCPEREN1 0x20 /* Peripheral clock enable register 1 */ #define SCTL_SCPEREN2 0x24 /* Peripheral clock enable register 2 */ #define SCTL_SCGRST 0x28 /* Peripheral global reset */ +#define SCTL_SCPCIECSBRST 0x2c /* PCIe PAB CSB reset status register */ #define SCTL_SCPCIPMCR1 0x30 /* PCI power management control 1 */ #define SCTL_SCPCIPMCR2 0x34 /* PCI power management control 2 */ #define SCTL_SCPCIPMSR1 0x38 /* PCI power management status 1 */ From 29f5b5a326b44c55e81b15308255ba695fecb323 Mon Sep 17 00:00:00 2001 From: Davide Ciminaghi Date: Fri, 9 Nov 2012 15:19:54 +0100 Subject: [PATCH 052/119] mfd: sta2x11-mfd: Add sta2x11_mfd_get_regs_data() function A couple of predefined clocks (mux and gated) need to be initialized with the virtual address of the clock's controlling register and the address of a spinlock used to protect against races. This function exports such data for all the mfd cells. Signed-off-by: Davide Ciminaghi Acked-by: Alessandro Rubini Signed-off-by: Samuel Ortiz --- drivers/mfd/sta2x11-mfd.c | 22 ++++++++++++++++++++++ include/linux/mfd/sta2x11-mfd.h | 5 +++++ 2 files changed, 27 insertions(+) diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index 0cac2013bbf6..6d12ab42aba3 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -135,6 +135,28 @@ u32 __sta2x11_mfd_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val, } EXPORT_SYMBOL(__sta2x11_mfd_mask); +int sta2x11_mfd_get_regs_data(struct platform_device *dev, + enum sta2x11_mfd_plat_dev index, + void __iomem **regs, + spinlock_t **lock) +{ + struct pci_dev *pdev = *(struct pci_dev **)(dev->dev.platform_data); + struct sta2x11_mfd *mfd; + + if (!pdev) + return -ENODEV; + mfd = sta2x11_mfd_find(pdev); + if (!mfd) + return -ENODEV; + if (index >= sta2x11_n_mfd_plat_devs) + return -ENODEV; + *regs = mfd->regs[index]; + *lock = &mfd->lock[index]; + pr_debug("%s %d *regs = %p\n", __func__, __LINE__, *regs); + return *regs ? 0 : -ENODEV; +} +EXPORT_SYMBOL(sta2x11_mfd_get_regs_data); + /* * Special sta2x11-mfd regmap lock/unlock functions */ diff --git a/include/linux/mfd/sta2x11-mfd.h b/include/linux/mfd/sta2x11-mfd.h index 83344304f2cf..e813e5efbe4e 100644 --- a/include/linux/mfd/sta2x11-mfd.h +++ b/include/linux/mfd/sta2x11-mfd.h @@ -474,4 +474,9 @@ u32 sta2x11_apb_soc_regs_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val) #define COMPENSATION_REG3 0x3cc #define TEST_CTL_REG 0x3d0 +extern int sta2x11_mfd_get_regs_data(struct platform_device *pdev, + enum sta2x11_mfd_plat_dev index, + void __iomem **regs, + spinlock_t **lock); + #endif /* __STA2X11_MFD_H */ From b18adafccd497245a6bc5b867bf9cba7e01f8729 Mon Sep 17 00:00:00 2001 From: Davide Ciminaghi Date: Fri, 9 Nov 2012 15:19:55 +0100 Subject: [PATCH 053/119] mfd: sta2x11-mfd: Use defines for platform devices' names Since there are now many sta2x11-mfd platform devices, using defines for their names looks like a better solution. Signed-off-by: Davide Ciminaghi Acked-by: Alessandro Rubini Signed-off-by: Samuel Ortiz --- drivers/mfd/sta2x11-mfd.c | 42 +++++++++++++++++---------------- include/linux/mfd/sta2x11-mfd.h | 8 +++++++ 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index 6d12ab42aba3..8d38ef264722 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -174,9 +174,9 @@ static void sta2x11_regmap_unlock(void *__lock) } static const char *sta2x11_mfd_names[sta2x11_n_mfd_plat_devs] = { - [sta2x11_sctl] = "sta2x11-sctl", - [sta2x11_apbreg] = "sta2x11-apbreg", - [sta2x11_apb_soc_regs] = "sta2x11-apb-soc-regs", + [sta2x11_sctl] = STA2X11_MFD_SCTL_NAME, + [sta2x11_apbreg] = STA2X11_MFD_APBREG_NAME, + [sta2x11_apb_soc_regs] = STA2X11_MFD_APB_SOC_REGS_NAME, }; static bool sta2x11_sctl_writeable_reg(struct device *dev, unsigned int reg) @@ -340,7 +340,7 @@ static int sta2x11_apb_soc_regs_probe(struct platform_device *dev) /* The three platform drivers */ static struct platform_driver sta2x11_sctl_platform_driver = { .driver = { - .name = "sta2x11-sctl", + .name = STA2X11_MFD_SCTL_NAME, .owner = THIS_MODULE, }, .probe = sta2x11_sctl_probe, @@ -354,7 +354,7 @@ static int __init sta2x11_sctl_init(void) static struct platform_driver sta2x11_platform_driver = { .driver = { - .name = "sta2x11-apbreg", + .name = STA2X11_MFD_APBREG_NAME, .owner = THIS_MODULE, }, .probe = sta2x11_apbreg_probe, @@ -368,7 +368,7 @@ static int __init sta2x11_apbreg_init(void) static struct platform_driver sta2x11_apb_soc_regs_platform_driver = { .driver = { - .name = "sta2x11-apb-soc-regs", + .name = STA2X11_MFD_APB_SOC_REGS_NAME, .owner = THIS_MODULE, }, .probe = sta2x11_apb_soc_regs_probe, @@ -409,38 +409,40 @@ enum mfd0_bar1_cells { static const __devinitconst struct resource gpio_resources[] = { { - .name = "sta2x11_gpio", /* 4 consecutive cells, 1 driver */ + /* 4 consecutive cells, 1 driver */ + .name = STA2X11_MFD_GPIO_NAME, .start = 0, .end = (4 * 4096) - 1, .flags = IORESOURCE_MEM, } }; static const __devinitconst struct resource sctl_resources[] = { - CELL_4K("sta2x11-sctl", STA2X11_SCTL), + CELL_4K(STA2X11_MFD_SCTL_NAME, STA2X11_SCTL), }; static const __devinitconst struct resource scr_resources[] = { - CELL_4K("sta2x11-scr", STA2X11_SCR), + CELL_4K(STA2X11_MFD_SCR_NAME, STA2X11_SCR), }; static const __devinitconst struct resource time_resources[] = { - CELL_4K("sta2x11-time", STA2X11_TIME), + CELL_4K(STA2X11_MFD_TIME_NAME, STA2X11_TIME), }; static const __devinitconst struct resource apbreg_resources[] = { - CELL_4K("sta2x11-apbreg", STA2X11_APBREG), + CELL_4K(STA2X11_MFD_APBREG_NAME, STA2X11_APBREG), }; #define DEV(_name, _r) \ { .name = _name, .num_resources = ARRAY_SIZE(_r), .resources = _r, } static __devinitdata struct mfd_cell sta2x11_mfd0_bar0[] = { - DEV("sta2x11-gpio", gpio_resources), /* offset 0: we add pdata later */ - DEV("sta2x11-sctl", sctl_resources), - DEV("sta2x11-scr", scr_resources), - DEV("sta2x11-time", time_resources), + /* offset 0: we add pdata later */ + DEV(STA2X11_MFD_GPIO_NAME, gpio_resources), + DEV(STA2X11_MFD_SCTL_NAME, sctl_resources), + DEV(STA2X11_MFD_SCR_NAME, scr_resources), + DEV(STA2X11_MFD_TIME_NAME, time_resources), }; static __devinitdata struct mfd_cell sta2x11_mfd0_bar1[] = { - DEV("sta2x11-apbreg", apbreg_resources), + DEV(STA2X11_MFD_APBREG_NAME, apbreg_resources), }; /* Mfd 1 devices */ @@ -456,19 +458,19 @@ enum mfd1_bar1_cells { }; static const __devinitconst struct resource vic_resources[] = { - CELL_4K("sta2x11-vic", STA2X11_VIC), + CELL_4K(STA2X11_MFD_VIC_NAME, STA2X11_VIC), }; static const __devinitconst struct resource apb_soc_regs_resources[] = { - CELL_4K("sta2x11-apb-soc-regs", STA2X11_APB_SOC_REGS), + CELL_4K(STA2X11_MFD_APB_SOC_REGS_NAME, STA2X11_APB_SOC_REGS), }; static __devinitdata struct mfd_cell sta2x11_mfd1_bar0[] = { - DEV("sta2x11-vic", vic_resources), + DEV(STA2X11_MFD_VIC_NAME, vic_resources), }; static __devinitdata struct mfd_cell sta2x11_mfd1_bar1[] = { - DEV("sta2x11-apb-soc-regs", apb_soc_regs_resources), + DEV(STA2X11_MFD_APB_SOC_REGS_NAME, apb_soc_regs_resources), }; diff --git a/include/linux/mfd/sta2x11-mfd.h b/include/linux/mfd/sta2x11-mfd.h index e813e5efbe4e..058de2bacf76 100644 --- a/include/linux/mfd/sta2x11-mfd.h +++ b/include/linux/mfd/sta2x11-mfd.h @@ -37,6 +37,14 @@ enum sta2x11_mfd_plat_dev { sta2x11_n_mfd_plat_devs, }; +#define STA2X11_MFD_SCTL_NAME "sta2x11-sctl" +#define STA2X11_MFD_GPIO_NAME "sta2x11-gpio" +#define STA2X11_MFD_SCR_NAME "sta2x11-scr" +#define STA2X11_MFD_TIME_NAME "sta2x11-time" +#define STA2X11_MFD_APBREG_NAME "sta2x11-apbreg" +#define STA2X11_MFD_APB_SOC_REGS_NAME "sta2x11-apb-soc-regs" +#define STA2X11_MFD_VIC_NAME "sta2x11-vic" + extern u32 __sta2x11_mfd_mask(struct pci_dev *, u32, u32, u32, enum sta2x11_mfd_plat_dev); From 8ec86a302a190bc4864928dd69f3f22066137b68 Mon Sep 17 00:00:00 2001 From: Davide Ciminaghi Date: Fri, 9 Nov 2012 15:19:56 +0100 Subject: [PATCH 054/119] mfd: sta2x11-mfd: Only add sta2x11_mfd if it hasn't already been added The pci probe method is called twice now, so we have to call sta2x11_mfd_add() only once to avoid a -EBUSY error. Signed-off-by: Davide Ciminaghi Acked-by: Alessandro Rubini Signed-off-by: Samuel Ortiz --- drivers/mfd/sta2x11-mfd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index 8d38ef264722..b981dc4a7b3c 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -585,7 +585,8 @@ static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev, sta2x11_mfd_setup(pdev, setup_data); /* Record this pdev before mfd_add_devices: their probe looks for it */ - sta2x11_mfd_add(pdev, GFP_ATOMIC); + if (!sta2x11_mfd_find(pdev)) + sta2x11_mfd_add(pdev, GFP_ATOMIC); /* Just 2 bars for all mfd's at present */ for (i = 0; i < 2; i++) { From 3ce26d2f4b08252edd3ee38a0a6e30a76da9a9fa Mon Sep 17 00:00:00 2001 From: Davide Ciminaghi Date: Fri, 9 Nov 2012 15:19:57 +0100 Subject: [PATCH 055/119] mfd: sta2x11-mfd: Do not mind about gpio platform data The gpio platform driver will take care of its platform data, let's not do any checks here. Signed-off-by: Davide Ciminaghi Acked-by: Alessandro Rubini Signed-off-by: Samuel Ortiz --- drivers/mfd/sta2x11-mfd.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index b981dc4a7b3c..2c8d65e13a67 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -553,7 +553,6 @@ static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev, { int err, i; struct sta2x11_mfd_setup_data *setup_data; - struct sta2x11_gpio_pdata *gpio_data; dev_info(&pdev->dev, "%s\n", __func__); @@ -571,16 +570,6 @@ static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev, &mfd_setup_data[STA2X11_MFD0] : &mfd_setup_data[STA2X11_MFD1]; - /* Read gpio config data as pci device's platform data */ - gpio_data = dev_get_platdata(&pdev->dev); - if (!gpio_data) - dev_warn(&pdev->dev, "no gpio configuration\n"); - - dev_dbg(&pdev->dev, "%s, gpio_data = %p (%p)\n", __func__, - gpio_data, &gpio_data); - dev_dbg(&pdev->dev, "%s, pdev = %p (%p)\n", __func__, - pdev, &pdev); - /* platform data is the pci device for all of them */ sta2x11_mfd_setup(pdev, setup_data); From e885ba298098959d03c58d946c6fad8f8ed4a1c7 Mon Sep 17 00:00:00 2001 From: Davide Ciminaghi Date: Fri, 9 Nov 2012 15:19:58 +0100 Subject: [PATCH 056/119] mfd: sta2x11-mfd: Use one lock per device instead of one lock per mfd The lock is used to implement atomic operations on each platform device's registers, so it looks reasonable having one lock per device instead of one common lock for all the devices belonging to the same sta2x11 instance. Signed-off-by: Davide Ciminaghi Acked-by: Alessandro Rubini Signed-off-by: Samuel Ortiz --- drivers/mfd/sta2x11-mfd.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index 2c8d65e13a67..da65839f1d86 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -46,7 +46,7 @@ static inline int __reg_within_range(unsigned int r, struct sta2x11_mfd { struct sta2x11_instance *instance; struct regmap *regmap[sta2x11_n_mfd_plat_devs]; - spinlock_t lock; + spinlock_t lock[sta2x11_n_mfd_plat_devs]; struct list_head list; void __iomem *regs[sta2x11_n_mfd_plat_devs]; }; @@ -78,6 +78,7 @@ static struct sta2x11_mfd *sta2x11_mfd_find(struct pci_dev *pdev) static int __devinit sta2x11_mfd_add(struct pci_dev *pdev, gfp_t flags) { + int i; struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); struct sta2x11_instance *instance; @@ -90,7 +91,8 @@ static int __devinit sta2x11_mfd_add(struct pci_dev *pdev, gfp_t flags) if (!mfd) return -ENOMEM; INIT_LIST_HEAD(&mfd->list); - spin_lock_init(&mfd->lock); + for (i = 0; i < ARRAY_SIZE(mfd->lock); i++) + spin_lock_init(&mfd->lock[i]); mfd->instance = instance; list_add(&mfd->list, &sta2x11_mfd_list); return 0; @@ -124,13 +126,13 @@ u32 __sta2x11_mfd_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val, dev_warn(&pdev->dev, ": system ctl not initialized\n"); return 0; } - spin_lock_irqsave(&mfd->lock, flags); + spin_lock_irqsave(&mfd->lock[index], flags); r = readl(regs + reg); r &= ~mask; r |= val; if (mask) writel(r, regs + reg); - spin_unlock_irqrestore(&mfd->lock, flags); + spin_unlock_irqrestore(&mfd->lock[index], flags); return r; } EXPORT_SYMBOL(__sta2x11_mfd_mask); From dba6c1aeea4dd0e251e41c3f585abf4a06a4f057 Mon Sep 17 00:00:00 2001 From: Davide Ciminaghi Date: Fri, 9 Nov 2012 15:19:59 +0100 Subject: [PATCH 057/119] mfd: sta2x11-mfd: Add scr (otp registers) platform driver Signed-off-by: Davide Ciminaghi Acked-by: Alessandro Rubini Signed-off-by: Samuel Ortiz --- drivers/mfd/sta2x11-mfd.c | 52 ++++++++++++++++++++++++++++++++- include/linux/mfd/sta2x11-mfd.h | 7 +++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index da65839f1d86..7365f0f89df2 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -175,10 +175,16 @@ static void sta2x11_regmap_unlock(void *__lock) spin_unlock(lock); } +/* OTP (one time programmable registers do not require locking */ +static void sta2x11_regmap_nolock(void *__lock) +{ +} + static const char *sta2x11_mfd_names[sta2x11_n_mfd_plat_devs] = { [sta2x11_sctl] = STA2X11_MFD_SCTL_NAME, [sta2x11_apbreg] = STA2X11_MFD_APBREG_NAME, [sta2x11_apb_soc_regs] = STA2X11_MFD_APB_SOC_REGS_NAME, + [sta2x11_scr] = STA2X11_MFD_SCR_NAME, }; static bool sta2x11_sctl_writeable_reg(struct device *dev, unsigned int reg) @@ -196,6 +202,28 @@ static struct regmap_config sta2x11_sctl_regmap_config = { .writeable_reg = sta2x11_sctl_writeable_reg, }; +static bool sta2x11_scr_readable_reg(struct device *dev, unsigned int reg) +{ + return (reg == STA2X11_SECR_CR) || + __reg_within_range(reg, STA2X11_SECR_FVR0, STA2X11_SECR_FVR1); +} + +static bool sta2x11_scr_writeable_reg(struct device *dev, unsigned int reg) +{ + return false; +} + +static struct regmap_config sta2x11_scr_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .lock = sta2x11_regmap_nolock, + .unlock = sta2x11_regmap_nolock, + .max_register = STA2X11_SECR_FVR1, + .readable_reg = sta2x11_scr_readable_reg, + .writeable_reg = sta2x11_scr_writeable_reg, +}; + static bool sta2x11_apbreg_readable_reg(struct device *dev, unsigned int reg) { /* Two blocks (CAN and MLB, SARAC) 0x100 bytes apart */ @@ -279,9 +307,10 @@ sta2x11_mfd_regmap_configs[sta2x11_n_mfd_plat_devs] = { [sta2x11_sctl] = &sta2x11_sctl_regmap_config, [sta2x11_apbreg] = &sta2x11_apbreg_regmap_config, [sta2x11_apb_soc_regs] = &sta2x11_apb_soc_regs_regmap_config, + [sta2x11_scr] = &sta2x11_scr_regmap_config, }; -/* Probe for the three platform devices */ +/* Probe for the four platform devices */ static int sta2x11_mfd_platform_probe(struct platform_device *dev, enum sta2x11_mfd_plat_dev index) @@ -339,6 +368,11 @@ static int sta2x11_apb_soc_regs_probe(struct platform_device *dev) return sta2x11_mfd_platform_probe(dev, sta2x11_apb_soc_regs); } +static int sta2x11_scr_probe(struct platform_device *dev) +{ + return sta2x11_mfd_platform_probe(dev, sta2x11_scr); +} + /* The three platform drivers */ static struct platform_driver sta2x11_sctl_platform_driver = { .driver = { @@ -382,6 +416,21 @@ static int __init sta2x11_apb_soc_regs_init(void) return platform_driver_register(&sta2x11_apb_soc_regs_platform_driver); } +static struct platform_driver sta2x11_scr_platform_driver = { + .driver = { + .name = STA2X11_MFD_SCR_NAME, + .owner = THIS_MODULE, + }, + .probe = sta2x11_scr_probe, +}; + +static int __init sta2x11_scr_init(void) +{ + pr_info("%s\n", __func__); + return platform_driver_register(&sta2x11_scr_platform_driver); +} + + /* * What follows are the PCI devices that host the above pdevs. * Each logic block is 4kB and they are all consecutive: we use this info. @@ -631,6 +680,7 @@ static int __init sta2x11_mfd_init(void) subsys_initcall(sta2x11_apbreg_init); subsys_initcall(sta2x11_sctl_init); subsys_initcall(sta2x11_apb_soc_regs_init); +subsys_initcall(sta2x11_scr_init); rootfs_initcall(sta2x11_mfd_init); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/sta2x11-mfd.h b/include/linux/mfd/sta2x11-mfd.h index 058de2bacf76..08cad95758c6 100644 --- a/include/linux/mfd/sta2x11-mfd.h +++ b/include/linux/mfd/sta2x11-mfd.h @@ -482,6 +482,13 @@ u32 sta2x11_apb_soc_regs_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val) #define COMPENSATION_REG3 0x3cc #define TEST_CTL_REG 0x3d0 +/* + * SECR (OTP) registers + */ +#define STA2X11_SECR_CR 0x00 +#define STA2X11_SECR_FVR0 0x10 +#define STA2X11_SECR_FVR1 0x14 + extern int sta2x11_mfd_get_regs_data(struct platform_device *pdev, enum sta2x11_mfd_plat_dev index, void __iomem **regs, From 818b5c2528b9e31101bb39018fd211dcf159a696 Mon Sep 17 00:00:00 2001 From: Davide Ciminaghi Date: Fri, 9 Nov 2012 15:20:00 +0100 Subject: [PATCH 058/119] mfd: sta2x11-mfd: Add defines for some sta2x11 sctl registers These are required for the clock infrastructure code to properly configure and control the sta2x11 PLLs. Signed-off-by: Davide Ciminaghi Acked-by: Alessandro Rubini Signed-off-by: Samuel Ortiz --- include/linux/mfd/sta2x11-mfd.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/include/linux/mfd/sta2x11-mfd.h b/include/linux/mfd/sta2x11-mfd.h index 08cad95758c6..9a855ac11cbf 100644 --- a/include/linux/mfd/sta2x11-mfd.h +++ b/include/linux/mfd/sta2x11-mfd.h @@ -246,8 +246,29 @@ u32 sta2x11_sctl_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val) #define SCTL_SCCTL 0x00 /* System controller control register */ #define SCTL_ARMCFG 0x04 /* ARM configuration register */ #define SCTL_SCPLLCTL 0x08 /* PLL control status register */ + +#define SCTL_SCPLLCTL_AUDIO_PLL_PD BIT(1) +#define SCTL_SCPLLCTL_FRAC_CONTROL BIT(3) +#define SCTL_SCPLLCTL_STRB_BYPASS BIT(6) +#define SCTL_SCPLLCTL_STRB_INPUT BIT(8) + #define SCTL_SCPLLFCTRL 0x0c /* PLL frequency control register */ + +#define SCTL_SCPLLFCTRL_AUDIO_PLL_NDIV_MASK 0xff +#define SCTL_SCPLLFCTRL_AUDIO_PLL_NDIV_SHIFT 10 +#define SCTL_SCPLLFCTRL_AUDIO_PLL_IDF_MASK 7 +#define SCTL_SCPLLFCTRL_AUDIO_PLL_IDF_SHIFT 21 +#define SCTL_SCPLLFCTRL_AUDIO_PLL_ODF_MASK 7 +#define SCTL_SCPLLFCTRL_AUDIO_PLL_ODF_SHIFT 18 +#define SCTL_SCPLLFCTRL_DITHER_DISABLE_MASK 0x03 +#define SCTL_SCPLLFCTRL_DITHER_DISABLE_SHIFT 4 + + #define SCTL_SCRESFRACT 0x10 /* PLL fractional input register */ + +#define SCTL_SCRESFRACT_MASK 0x0000ffff + + #define SCTL_SCRESCTRL1 0x14 /* Peripheral reset control 1 */ #define SCTL_SCRESXTRL2 0x18 /* Peripheral reset control 2 */ #define SCTL_SCPEREN0 0x1c /* Peripheral clock enable register 0 */ From b73df6986bdf0186deeb30b272a9b890065ca223 Mon Sep 17 00:00:00 2001 From: Davide Ciminaghi Date: Fri, 9 Nov 2012 15:20:01 +0100 Subject: [PATCH 059/119] mfd: sta2x11-mfd: Add myself to copyright Signed-off-by: Davide Ciminaghi Acked-by: Alessandro Rubini Signed-off-by: Samuel Ortiz --- drivers/mfd/sta2x11-mfd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index 7365f0f89df2..6fb0938a9665 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2009-2011 Wind River Systems, Inc. - * Copyright (c) 2011 ST Microelectronics (Alessandro Rubini) + * Copyright (c) 2011 ST Microelectronics (Alessandro Rubini, Davide Ciminaghi) * * 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 From a3e2a76e9efac6bf5963800d2a3d88aac614ccf5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 20 Nov 2012 13:33:18 +0900 Subject: [PATCH 060/119] mfd: wm5102: Update maximum register The DSP memories are mapped into the register map, make them readable and writable by updating max_register appropriately. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 01b9255ed631..ffeba98c4500 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -2369,12 +2369,14 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) } } +#define WM5102_MAX_REGISTER 0x1a8fff + const struct regmap_config wm5102_spi_regmap = { .reg_bits = 32, .pad_bits = 16, .val_bits = 16, - .max_register = ARIZONA_DSP1_STATUS_2, + .max_register = WM5102_MAX_REGISTER, .readable_reg = wm5102_readable_register, .volatile_reg = wm5102_volatile_register, @@ -2388,7 +2390,7 @@ const struct regmap_config wm5102_i2c_regmap = { .reg_bits = 32, .val_bits = 16, - .max_register = ARIZONA_DSP1_STATUS_2, + .max_register = WM5102_MAX_REGISTER, .readable_reg = wm5102_readable_register, .volatile_reg = wm5102_volatile_register, From dbc4849db33307c72951e4bd4935fbb8d1fc7b8d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 20 Nov 2012 13:33:19 +0900 Subject: [PATCH 061/119] mfd: wm5102: Mark some more status registers as volatile Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index ffeba98c4500..1ade455a28c2 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -2331,6 +2331,9 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_SOFTWARE_RESET: case ARIZONA_DEVICE_REVISION: case ARIZONA_OUTPUT_STATUS_1: + case ARIZONA_RAW_OUTPUT_STATUS_1: + case ARIZONA_SLIMBUS_RX_PORT_STATUS: + case ARIZONA_SLIMBUS_TX_PORT_STATUS: case ARIZONA_SAMPLE_RATE_1_STATUS: case ARIZONA_SAMPLE_RATE_2_STATUS: case ARIZONA_SAMPLE_RATE_3_STATUS: From edd7eabc85e2f8d76a933b9639bebfe7f98861e4 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 14 Nov 2012 21:09:28 +0530 Subject: [PATCH 062/119] mfd: Add TI TPS80031 mfd core driver TPS80031/ TPS80032 Fully Integrated Power Management with Power Path and Battery Charger. The device provides five configurable step-down converters, 11 general purpose LDOs, USB OTG Module, ADC, RTC, 2 PWM, System Voltage Regulator/Battery Charger with Power Path from USB, 32K clock generator. Add the mfd core driver for TPS80031/TPS80032. Signed-off-by: Laxman Dewangan Reviwed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 14 + drivers/mfd/Makefile | 1 + drivers/mfd/tps80031.c | 573 +++++++++++++++++++++++++++++++ include/linux/mfd/tps80031.h | 637 +++++++++++++++++++++++++++++++++++ 4 files changed, 1225 insertions(+) create mode 100644 drivers/mfd/tps80031.c create mode 100644 include/linux/mfd/tps80031.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 59359a7a2493..ca633df7c330 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -265,6 +265,20 @@ config MFD_TPS65912_SPI If you say yes here you get support for the TPS65912 series of PM chips with SPI interface. +config MFD_TPS80031 + bool "TI TPS80031/TPS80032 Power Management chips" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + select REGMAP_I2C + select IRQ_DOMAIN + help + If you say yes here you get support for the Texas Instruments + TPS80031/ TPS80032 Fully Integrated Power Management with Power + Path and Battery Charger. The device provides five configurable + step-down converters, 11 general purpose LDOs, USB OTG Module, + ADC, RTC, 2 PWM, System Voltage Regulator/Battery Charger with + Power Path from USB, 32K clock generator. + config MENELAUS bool "Texas Instruments TWL92330/Menelaus PM chip" depends on I2C=y && ARCH_OMAP2 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 1c3ee7c76906..8072460e99d2 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -61,6 +61,7 @@ tps65912-objs := tps65912-core.o tps65912-irq.o obj-$(CONFIG_MFD_TPS65912) += tps65912.o obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o +obj-$(CONFIG_MFD_TPS80031) += tps80031.o obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o diff --git a/drivers/mfd/tps80031.c b/drivers/mfd/tps80031.c new file mode 100644 index 000000000000..f64005efa6fa --- /dev/null +++ b/drivers/mfd/tps80031.c @@ -0,0 +1,573 @@ +/* + * tps80031.c -- TI TPS80031/TPS80032 mfd core driver. + * + * MFD core driver for TI TPS80031/TPS80032 Fully Integrated + * Power Management with Power Path and Battery Charger + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct resource tps80031_rtc_resources[] = { + { + .start = TPS80031_INT_RTC_ALARM, + .end = TPS80031_INT_RTC_ALARM, + .flags = IORESOURCE_IRQ, + }, +}; + +/* TPS80031 sub mfd devices */ +static struct mfd_cell tps80031_cell[] = { + { + .name = "tps80031-pmic", + }, + { + .name = "tps80031-clock", + }, + { + .name = "tps80031-rtc", + .num_resources = ARRAY_SIZE(tps80031_rtc_resources), + .resources = tps80031_rtc_resources, + }, + { + .name = "tps80031-gpadc", + }, + { + .name = "tps80031-fuel-gauge", + }, + { + .name = "tps80031-charger", + }, +}; + +static int tps80031_slave_address[TPS80031_NUM_SLAVES] = { + TPS80031_I2C_ID0_ADDR, + TPS80031_I2C_ID1_ADDR, + TPS80031_I2C_ID2_ADDR, + TPS80031_I2C_ID3_ADDR, +}; + +struct tps80031_pupd_data { + u8 reg; + u8 pullup_bit; + u8 pulldown_bit; +}; + +#define TPS80031_IRQ(_reg, _mask) \ + { \ + .reg_offset = (TPS80031_INT_MSK_LINE_##_reg) - \ + TPS80031_INT_MSK_LINE_A, \ + .mask = BIT(_mask), \ + } + +static const struct regmap_irq tps80031_main_irqs[] = { + [TPS80031_INT_PWRON] = TPS80031_IRQ(A, 0), + [TPS80031_INT_RPWRON] = TPS80031_IRQ(A, 1), + [TPS80031_INT_SYS_VLOW] = TPS80031_IRQ(A, 2), + [TPS80031_INT_RTC_ALARM] = TPS80031_IRQ(A, 3), + [TPS80031_INT_RTC_PERIOD] = TPS80031_IRQ(A, 4), + [TPS80031_INT_HOT_DIE] = TPS80031_IRQ(A, 5), + [TPS80031_INT_VXX_SHORT] = TPS80031_IRQ(A, 6), + [TPS80031_INT_SPDURATION] = TPS80031_IRQ(A, 7), + [TPS80031_INT_WATCHDOG] = TPS80031_IRQ(B, 0), + [TPS80031_INT_BAT] = TPS80031_IRQ(B, 1), + [TPS80031_INT_SIM] = TPS80031_IRQ(B, 2), + [TPS80031_INT_MMC] = TPS80031_IRQ(B, 3), + [TPS80031_INT_RES] = TPS80031_IRQ(B, 4), + [TPS80031_INT_GPADC_RT] = TPS80031_IRQ(B, 5), + [TPS80031_INT_GPADC_SW2_EOC] = TPS80031_IRQ(B, 6), + [TPS80031_INT_CC_AUTOCAL] = TPS80031_IRQ(B, 7), + [TPS80031_INT_ID_WKUP] = TPS80031_IRQ(C, 0), + [TPS80031_INT_VBUSS_WKUP] = TPS80031_IRQ(C, 1), + [TPS80031_INT_ID] = TPS80031_IRQ(C, 2), + [TPS80031_INT_VBUS] = TPS80031_IRQ(C, 3), + [TPS80031_INT_CHRG_CTRL] = TPS80031_IRQ(C, 4), + [TPS80031_INT_EXT_CHRG] = TPS80031_IRQ(C, 5), + [TPS80031_INT_INT_CHRG] = TPS80031_IRQ(C, 6), + [TPS80031_INT_RES2] = TPS80031_IRQ(C, 7), +}; + +static struct regmap_irq_chip tps80031_irq_chip = { + .name = "tps80031", + .irqs = tps80031_main_irqs, + .num_irqs = ARRAY_SIZE(tps80031_main_irqs), + .num_regs = 3, + .status_base = TPS80031_INT_STS_A, + .mask_base = TPS80031_INT_MSK_LINE_A, +}; + +#define PUPD_DATA(_reg, _pulldown_bit, _pullup_bit) \ + { \ + .reg = TPS80031_CFG_INPUT_PUPD##_reg, \ + .pulldown_bit = _pulldown_bit, \ + .pullup_bit = _pullup_bit, \ + } + +static const struct tps80031_pupd_data tps80031_pupds[] = { + [TPS80031_PREQ1] = PUPD_DATA(1, BIT(0), BIT(1)), + [TPS80031_PREQ2A] = PUPD_DATA(1, BIT(2), BIT(3)), + [TPS80031_PREQ2B] = PUPD_DATA(1, BIT(4), BIT(5)), + [TPS80031_PREQ2C] = PUPD_DATA(1, BIT(6), BIT(7)), + [TPS80031_PREQ3] = PUPD_DATA(2, BIT(0), BIT(1)), + [TPS80031_NRES_WARM] = PUPD_DATA(2, 0, BIT(2)), + [TPS80031_PWM_FORCE] = PUPD_DATA(2, BIT(5), 0), + [TPS80031_CHRG_EXT_CHRG_STATZ] = PUPD_DATA(2, 0, BIT(6)), + [TPS80031_SIM] = PUPD_DATA(3, BIT(0), BIT(1)), + [TPS80031_MMC] = PUPD_DATA(3, BIT(2), BIT(3)), + [TPS80031_GPADC_START] = PUPD_DATA(3, BIT(4), 0), + [TPS80031_DVSI2C_SCL] = PUPD_DATA(4, 0, BIT(0)), + [TPS80031_DVSI2C_SDA] = PUPD_DATA(4, 0, BIT(1)), + [TPS80031_CTLI2C_SCL] = PUPD_DATA(4, 0, BIT(2)), + [TPS80031_CTLI2C_SDA] = PUPD_DATA(4, 0, BIT(3)), +}; +static struct tps80031 *tps80031_power_off_dev; + +int tps80031_ext_power_req_config(struct device *dev, + unsigned long ext_ctrl_flag, int preq_bit, + int state_reg_add, int trans_reg_add) +{ + u8 res_ass_reg = 0; + int preq_mask_bit = 0; + int ret; + + if (!(ext_ctrl_flag & TPS80031_EXT_PWR_REQ)) + return 0; + + if (ext_ctrl_flag & TPS80031_PWR_REQ_INPUT_PREQ1) { + res_ass_reg = TPS80031_PREQ1_RES_ASS_A + (preq_bit >> 3); + preq_mask_bit = 5; + } else if (ext_ctrl_flag & TPS80031_PWR_REQ_INPUT_PREQ2) { + res_ass_reg = TPS80031_PREQ2_RES_ASS_A + (preq_bit >> 3); + preq_mask_bit = 6; + } else if (ext_ctrl_flag & TPS80031_PWR_REQ_INPUT_PREQ3) { + res_ass_reg = TPS80031_PREQ3_RES_ASS_A + (preq_bit >> 3); + preq_mask_bit = 7; + } + + /* Configure REQ_ASS registers */ + ret = tps80031_set_bits(dev, TPS80031_SLAVE_ID1, res_ass_reg, + BIT(preq_bit & 0x7)); + if (ret < 0) { + dev_err(dev, "reg 0x%02x setbit failed, err = %d\n", + res_ass_reg, ret); + return ret; + } + + /* Unmask the PREQ */ + ret = tps80031_clr_bits(dev, TPS80031_SLAVE_ID1, + TPS80031_PHOENIX_MSK_TRANSITION, BIT(preq_mask_bit)); + if (ret < 0) { + dev_err(dev, "reg 0x%02x clrbit failed, err = %d\n", + TPS80031_PHOENIX_MSK_TRANSITION, ret); + return ret; + } + + /* Switch regulator control to resource now */ + if (ext_ctrl_flag & (TPS80031_PWR_REQ_INPUT_PREQ2 | + TPS80031_PWR_REQ_INPUT_PREQ3)) { + ret = tps80031_update(dev, TPS80031_SLAVE_ID1, state_reg_add, + 0x0, TPS80031_STATE_MASK); + if (ret < 0) + dev_err(dev, "reg 0x%02x update failed, err = %d\n", + state_reg_add, ret); + } else { + ret = tps80031_update(dev, TPS80031_SLAVE_ID1, trans_reg_add, + TPS80031_TRANS_SLEEP_OFF, + TPS80031_TRANS_SLEEP_MASK); + if (ret < 0) + dev_err(dev, "reg 0x%02x update failed, err = %d\n", + trans_reg_add, ret); + } + return ret; +} +EXPORT_SYMBOL_GPL(tps80031_ext_power_req_config); + +static void tps80031_power_off(void) +{ + dev_info(tps80031_power_off_dev->dev, "switching off PMU\n"); + tps80031_write(tps80031_power_off_dev->dev, TPS80031_SLAVE_ID1, + TPS80031_PHOENIX_DEV_ON, TPS80031_DEVOFF); +} + +static void tps80031_pupd_init(struct tps80031 *tps80031, + struct tps80031_platform_data *pdata) +{ + struct tps80031_pupd_init_data *pupd_init_data = pdata->pupd_init_data; + int data_size = pdata->pupd_init_data_size; + int i; + + for (i = 0; i < data_size; ++i) { + struct tps80031_pupd_init_data *pupd_init = &pupd_init_data[i]; + const struct tps80031_pupd_data *pupd = + &tps80031_pupds[pupd_init->input_pin]; + u8 update_value = 0; + u8 update_mask = pupd->pulldown_bit | pupd->pullup_bit; + + if (pupd_init->setting == TPS80031_PUPD_PULLDOWN) + update_value = pupd->pulldown_bit; + else if (pupd_init->setting == TPS80031_PUPD_PULLUP) + update_value = pupd->pullup_bit; + + tps80031_update(tps80031->dev, TPS80031_SLAVE_ID1, pupd->reg, + update_value, update_mask); + } +} + +static int tps80031_init_ext_control(struct tps80031 *tps80031, + struct tps80031_platform_data *pdata) +{ + struct device *dev = tps80031->dev; + int ret; + int i; + + /* Clear all external control for this rail */ + for (i = 0; i < 9; ++i) { + ret = tps80031_write(dev, TPS80031_SLAVE_ID1, + TPS80031_PREQ1_RES_ASS_A + i, 0); + if (ret < 0) { + dev_err(dev, "reg 0x%02x write failed, err = %d\n", + TPS80031_PREQ1_RES_ASS_A + i, ret); + return ret; + } + } + + /* Mask the PREQ */ + ret = tps80031_set_bits(dev, TPS80031_SLAVE_ID1, + TPS80031_PHOENIX_MSK_TRANSITION, 0x7 << 5); + if (ret < 0) { + dev_err(dev, "reg 0x%02x set_bits failed, err = %d\n", + TPS80031_PHOENIX_MSK_TRANSITION, ret); + return ret; + } + return ret; +} + +static int __devinit tps80031_irq_init(struct tps80031 *tps80031, int irq, + int irq_base) +{ + struct device *dev = tps80031->dev; + int i, ret; + + /* + * The MASK register used for updating status register when + * interrupt occurs and LINE register used to pass the status + * to actual interrupt line. As per datasheet: + * When INT_MSK_LINE [i] is set to 1, the associated interrupt + * number i is INT line masked, which means that no interrupt is + * generated on the INT line. + * When INT_MSK_LINE [i] is set to 0, the associated interrupt + * number i is line enabled: An interrupt is generated on the + * INT line. + * In any case, the INT_STS [i] status bit may or may not be updated, + * only linked to the INT_MSK_STS [i] configuration register bit. + * + * When INT_MSK_STS [i] is set to 1, the associated interrupt number + * i is status masked, which means that no interrupt is stored in + * the INT_STS[i] status bit. Note that no interrupt number i is + * generated on the INT line, even if the INT_MSK_LINE [i] register + * bit is set to 0. + * When INT_MSK_STS [i] is set to 0, the associated interrupt number i + * is status enabled: An interrupt status is updated in the INT_STS [i] + * register. The interrupt may or may not be generated on the INT line, + * depending on the INT_MSK_LINE [i] configuration register bit. + */ + for (i = 0; i < 3; i++) + tps80031_write(dev, TPS80031_SLAVE_ID2, + TPS80031_INT_MSK_STS_A + i, 0x00); + + ret = regmap_add_irq_chip(tps80031->regmap[TPS80031_SLAVE_ID2], irq, + IRQF_ONESHOT, irq_base, + &tps80031_irq_chip, &tps80031->irq_data); + if (ret < 0) { + dev_err(dev, "add irq failed, err = %d\n", ret); + return ret; + } + return ret; +} + +static bool rd_wr_reg_id0(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TPS80031_SMPS1_CFG_FORCE ... TPS80031_SMPS2_CFG_VOLTAGE: + return true; + default: + return false; + } +} + +static bool rd_wr_reg_id1(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TPS80031_SECONDS_REG ... TPS80031_RTC_RESET_STATUS_REG: + case TPS80031_VALIDITY0 ... TPS80031_VALIDITY7: + case TPS80031_PHOENIX_START_CONDITION ... TPS80031_KEY_PRESS_DUR_CFG: + case TPS80031_SMPS4_CFG_TRANS ... TPS80031_SMPS3_CFG_VOLTAGE: + case TPS80031_BROADCAST_ADDR_ALL ... TPS80031_BROADCAST_ADDR_CLK_RST: + case TPS80031_VANA_CFG_TRANS ... TPS80031_LDO7_CFG_VOLTAGE: + case TPS80031_REGEN1_CFG_TRANS ... TPS80031_TMP_CFG_STATE: + case TPS80031_PREQ1_RES_ASS_A ... TPS80031_PREQ3_RES_ASS_C: + case TPS80031_SMPS_OFFSET ... TPS80031_BATDEBOUNCING: + case TPS80031_CFG_INPUT_PUPD1 ... TPS80031_CFG_SMPS_PD: + case TPS80031_BACKUP_REG: + return true; + default: + return false; + } +} + +static bool is_volatile_reg_id1(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TPS80031_SMPS4_CFG_TRANS ... TPS80031_SMPS3_CFG_VOLTAGE: + case TPS80031_VANA_CFG_TRANS ... TPS80031_LDO7_CFG_VOLTAGE: + case TPS80031_REGEN1_CFG_TRANS ... TPS80031_TMP_CFG_STATE: + case TPS80031_PREQ1_RES_ASS_A ... TPS80031_PREQ3_RES_ASS_C: + case TPS80031_SMPS_OFFSET ... TPS80031_BATDEBOUNCING: + case TPS80031_CFG_INPUT_PUPD1 ... TPS80031_CFG_SMPS_PD: + return true; + default: + return false; + } +} + +static bool rd_wr_reg_id2(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TPS80031_USB_VENDOR_ID_LSB ... TPS80031_USB_OTG_REVISION: + case TPS80031_GPADC_CTRL ... TPS80031_CTRL_P1: + case TPS80031_RTCH0_LSB ... TPS80031_GPCH0_MSB: + case TPS80031_TOGGLE1 ... TPS80031_VIBMODE: + case TPS80031_PWM1ON ... TPS80031_PWM2OFF: + case TPS80031_FG_REG_00 ... TPS80031_FG_REG_11: + case TPS80031_INT_STS_A ... TPS80031_INT_MSK_STS_C: + case TPS80031_CONTROLLER_CTRL2 ... TPS80031_LED_PWM_CTRL2: + return true; + default: + return false; + } +} + +static bool rd_wr_reg_id3(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TPS80031_GPADC_TRIM0 ... TPS80031_GPADC_TRIM18: + return true; + default: + return false; + } +} + +static const struct regmap_config tps80031_regmap_configs[] = { + { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = rd_wr_reg_id0, + .readable_reg = rd_wr_reg_id0, + .max_register = TPS80031_MAX_REGISTER, + }, + { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = rd_wr_reg_id1, + .readable_reg = rd_wr_reg_id1, + .volatile_reg = is_volatile_reg_id1, + .max_register = TPS80031_MAX_REGISTER, + }, + { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = rd_wr_reg_id2, + .readable_reg = rd_wr_reg_id2, + .max_register = TPS80031_MAX_REGISTER, + }, + { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = rd_wr_reg_id3, + .readable_reg = rd_wr_reg_id3, + .max_register = TPS80031_MAX_REGISTER, + }, +}; + +static int __devinit tps80031_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tps80031_platform_data *pdata = client->dev.platform_data; + struct tps80031 *tps80031; + int ret; + uint8_t es_version; + uint8_t ep_ver; + int i; + + if (!pdata) { + dev_err(&client->dev, "tps80031 requires platform data\n"); + return -EINVAL; + } + + tps80031 = devm_kzalloc(&client->dev, sizeof(*tps80031), GFP_KERNEL); + if (!tps80031) { + dev_err(&client->dev, "Malloc failed for tps80031\n"); + return -ENOMEM; + } + + for (i = 0; i < TPS80031_NUM_SLAVES; i++) { + if (tps80031_slave_address[i] == client->addr) + tps80031->clients[i] = client; + else + tps80031->clients[i] = i2c_new_dummy(client->adapter, + tps80031_slave_address[i]); + if (!tps80031->clients[i]) { + dev_err(&client->dev, "can't attach client %d\n", i); + ret = -ENOMEM; + goto fail_client_reg; + } + + i2c_set_clientdata(tps80031->clients[i], tps80031); + tps80031->regmap[i] = devm_regmap_init_i2c(tps80031->clients[i], + &tps80031_regmap_configs[i]); + if (IS_ERR(tps80031->regmap[i])) { + ret = PTR_ERR(tps80031->regmap[i]); + dev_err(&client->dev, + "regmap %d init failed, err %d\n", i, ret); + goto fail_client_reg; + } + } + + ret = tps80031_read(&client->dev, TPS80031_SLAVE_ID3, + TPS80031_JTAGVERNUM, &es_version); + if (ret < 0) { + dev_err(&client->dev, + "Silicon version number read failed: %d\n", ret); + goto fail_client_reg; + } + + ret = tps80031_read(&client->dev, TPS80031_SLAVE_ID3, + TPS80031_EPROM_REV, &ep_ver); + if (ret < 0) { + dev_err(&client->dev, + "Silicon eeprom version read failed: %d\n", ret); + goto fail_client_reg; + } + + dev_info(&client->dev, "ES version 0x%02x and EPROM version 0x%02x\n", + es_version, ep_ver); + tps80031->es_version = es_version; + tps80031->dev = &client->dev; + i2c_set_clientdata(client, tps80031); + tps80031->chip_info = id->driver_data; + + ret = tps80031_irq_init(tps80031, client->irq, pdata->irq_base); + if (ret) { + dev_err(&client->dev, "IRQ init failed: %d\n", ret); + goto fail_client_reg; + } + + tps80031_pupd_init(tps80031, pdata); + + tps80031_init_ext_control(tps80031, pdata); + + ret = mfd_add_devices(tps80031->dev, -1, + tps80031_cell, ARRAY_SIZE(tps80031_cell), + NULL, 0, + regmap_irq_get_domain(tps80031->irq_data)); + if (ret < 0) { + dev_err(&client->dev, "mfd_add_devices failed: %d\n", ret); + goto fail_mfd_add; + } + + if (pdata->use_power_off && !pm_power_off) { + tps80031_power_off_dev = tps80031; + pm_power_off = tps80031_power_off; + } + return 0; + +fail_mfd_add: + regmap_del_irq_chip(client->irq, tps80031->irq_data); + +fail_client_reg: + for (i = 0; i < TPS80031_NUM_SLAVES; i++) { + if (tps80031->clients[i] && (tps80031->clients[i] != client)) + i2c_unregister_device(tps80031->clients[i]); + } + return ret; +} + +static int __devexit tps80031_remove(struct i2c_client *client) +{ + struct tps80031 *tps80031 = i2c_get_clientdata(client); + int i; + + if (tps80031_power_off_dev == tps80031) { + tps80031_power_off_dev = NULL; + pm_power_off = NULL; + } + + mfd_remove_devices(tps80031->dev); + + regmap_del_irq_chip(client->irq, tps80031->irq_data); + + for (i = 0; i < TPS80031_NUM_SLAVES; i++) { + if (tps80031->clients[i] != client) + i2c_unregister_device(tps80031->clients[i]); + } + return 0; +} + +static const struct i2c_device_id tps80031_id_table[] = { + { "tps80031", TPS80031 }, + { "tps80032", TPS80032 }, +}; +MODULE_DEVICE_TABLE(i2c, tps80031_id_table); + +static struct i2c_driver tps80031_driver = { + .driver = { + .name = "tps80031", + .owner = THIS_MODULE, + }, + .probe = tps80031_probe, + .remove = __devexit_p(tps80031_remove), + .id_table = tps80031_id_table, +}; + +static int __init tps80031_init(void) +{ + return i2c_add_driver(&tps80031_driver); +} +subsys_initcall(tps80031_init); + +static void __exit tps80031_exit(void) +{ + i2c_del_driver(&tps80031_driver); +} +module_exit(tps80031_exit); + +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_DESCRIPTION("TPS80031 core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/tps80031.h b/include/linux/mfd/tps80031.h new file mode 100644 index 000000000000..2c75c9c9318f --- /dev/null +++ b/include/linux/mfd/tps80031.h @@ -0,0 +1,637 @@ +/* + * tps80031.h -- TI TPS80031 and TI TPS80032 PMIC driver. + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef __LINUX_MFD_TPS80031_H +#define __LINUX_MFD_TPS80031_H + +#include +#include + +/* Pull-ups/Pull-downs */ +#define TPS80031_CFG_INPUT_PUPD1 0xF0 +#define TPS80031_CFG_INPUT_PUPD2 0xF1 +#define TPS80031_CFG_INPUT_PUPD3 0xF2 +#define TPS80031_CFG_INPUT_PUPD4 0xF3 +#define TPS80031_CFG_LDO_PD1 0xF4 +#define TPS80031_CFG_LDO_PD2 0xF5 +#define TPS80031_CFG_SMPS_PD 0xF6 + +/* Real Time Clock */ +#define TPS80031_SECONDS_REG 0x00 +#define TPS80031_MINUTES_REG 0x01 +#define TPS80031_HOURS_REG 0x02 +#define TPS80031_DAYS_REG 0x03 +#define TPS80031_MONTHS_REG 0x04 +#define TPS80031_YEARS_REG 0x05 +#define TPS80031_WEEKS_REG 0x06 +#define TPS80031_ALARM_SECONDS_REG 0x08 +#define TPS80031_ALARM_MINUTES_REG 0x09 +#define TPS80031_ALARM_HOURS_REG 0x0A +#define TPS80031_ALARM_DAYS_REG 0x0B +#define TPS80031_ALARM_MONTHS_REG 0x0C +#define TPS80031_ALARM_YEARS_REG 0x0D +#define TPS80031_RTC_CTRL_REG 0x10 +#define TPS80031_RTC_STATUS_REG 0x11 +#define TPS80031_RTC_INTERRUPTS_REG 0x12 +#define TPS80031_RTC_COMP_LSB_REG 0x13 +#define TPS80031_RTC_COMP_MSB_REG 0x14 +#define TPS80031_RTC_RESET_STATUS_REG 0x16 + +/*PMC Master Module */ +#define TPS80031_PHOENIX_START_CONDITION 0x1F +#define TPS80031_PHOENIX_MSK_TRANSITION 0x20 +#define TPS80031_STS_HW_CONDITIONS 0x21 +#define TPS80031_PHOENIX_LAST_TURNOFF_STS 0x22 +#define TPS80031_VSYSMIN_LO_THRESHOLD 0x23 +#define TPS80031_VSYSMIN_HI_THRESHOLD 0x24 +#define TPS80031_PHOENIX_DEV_ON 0x25 +#define TPS80031_STS_PWR_GRP_STATE 0x27 +#define TPS80031_PH_CFG_VSYSLOW 0x28 +#define TPS80031_PH_STS_BOOT 0x29 +#define TPS80031_PHOENIX_SENS_TRANSITION 0x2A +#define TPS80031_PHOENIX_SEQ_CFG 0x2B +#define TPS80031_PRIMARY_WATCHDOG_CFG 0X2C +#define TPS80031_KEY_PRESS_DUR_CFG 0X2D +#define TPS80031_SMPS_LDO_SHORT_STS 0x2E + +/* PMC Slave Module - Broadcast */ +#define TPS80031_BROADCAST_ADDR_ALL 0x31 +#define TPS80031_BROADCAST_ADDR_REF 0x32 +#define TPS80031_BROADCAST_ADDR_PROV 0x33 +#define TPS80031_BROADCAST_ADDR_CLK_RST 0x34 + +/* PMC Slave Module SMPS Regulators */ +#define TPS80031_SMPS4_CFG_TRANS 0x41 +#define TPS80031_SMPS4_CFG_STATE 0x42 +#define TPS80031_SMPS4_CFG_VOLTAGE 0x44 +#define TPS80031_VIO_CFG_TRANS 0x47 +#define TPS80031_VIO_CFG_STATE 0x48 +#define TPS80031_VIO_CFG_FORCE 0x49 +#define TPS80031_VIO_CFG_VOLTAGE 0x4A +#define TPS80031_VIO_CFG_STEP 0x48 +#define TPS80031_SMPS1_CFG_TRANS 0x53 +#define TPS80031_SMPS1_CFG_STATE 0x54 +#define TPS80031_SMPS1_CFG_FORCE 0x55 +#define TPS80031_SMPS1_CFG_VOLTAGE 0x56 +#define TPS80031_SMPS1_CFG_STEP 0x57 +#define TPS80031_SMPS2_CFG_TRANS 0x59 +#define TPS80031_SMPS2_CFG_STATE 0x5A +#define TPS80031_SMPS2_CFG_FORCE 0x5B +#define TPS80031_SMPS2_CFG_VOLTAGE 0x5C +#define TPS80031_SMPS2_CFG_STEP 0x5D +#define TPS80031_SMPS3_CFG_TRANS 0x65 +#define TPS80031_SMPS3_CFG_STATE 0x66 +#define TPS80031_SMPS3_CFG_VOLTAGE 0x68 + +/* PMC Slave Module LDO Regulators */ +#define TPS80031_VANA_CFG_TRANS 0x81 +#define TPS80031_VANA_CFG_STATE 0x82 +#define TPS80031_VANA_CFG_VOLTAGE 0x83 +#define TPS80031_LDO2_CFG_TRANS 0x85 +#define TPS80031_LDO2_CFG_STATE 0x86 +#define TPS80031_LDO2_CFG_VOLTAGE 0x87 +#define TPS80031_LDO4_CFG_TRANS 0x89 +#define TPS80031_LDO4_CFG_STATE 0x8A +#define TPS80031_LDO4_CFG_VOLTAGE 0x8B +#define TPS80031_LDO3_CFG_TRANS 0x8D +#define TPS80031_LDO3_CFG_STATE 0x8E +#define TPS80031_LDO3_CFG_VOLTAGE 0x8F +#define TPS80031_LDO6_CFG_TRANS 0x91 +#define TPS80031_LDO6_CFG_STATE 0x92 +#define TPS80031_LDO6_CFG_VOLTAGE 0x93 +#define TPS80031_LDOLN_CFG_TRANS 0x95 +#define TPS80031_LDOLN_CFG_STATE 0x96 +#define TPS80031_LDOLN_CFG_VOLTAGE 0x97 +#define TPS80031_LDO5_CFG_TRANS 0x99 +#define TPS80031_LDO5_CFG_STATE 0x9A +#define TPS80031_LDO5_CFG_VOLTAGE 0x9B +#define TPS80031_LDO1_CFG_TRANS 0x9D +#define TPS80031_LDO1_CFG_STATE 0x9E +#define TPS80031_LDO1_CFG_VOLTAGE 0x9F +#define TPS80031_LDOUSB_CFG_TRANS 0xA1 +#define TPS80031_LDOUSB_CFG_STATE 0xA2 +#define TPS80031_LDOUSB_CFG_VOLTAGE 0xA3 +#define TPS80031_LDO7_CFG_TRANS 0xA5 +#define TPS80031_LDO7_CFG_STATE 0xA6 +#define TPS80031_LDO7_CFG_VOLTAGE 0xA7 + +/* PMC Slave Module External Control */ +#define TPS80031_REGEN1_CFG_TRANS 0xAE +#define TPS80031_REGEN1_CFG_STATE 0xAF +#define TPS80031_REGEN2_CFG_TRANS 0xB1 +#define TPS80031_REGEN2_CFG_STATE 0xB2 +#define TPS80031_SYSEN_CFG_TRANS 0xB4 +#define TPS80031_SYSEN_CFG_STATE 0xB5 + +/* PMC Slave Module Internal Control */ +#define TPS80031_NRESPWRON_CFG_TRANS 0xB7 +#define TPS80031_NRESPWRON_CFG_STATE 0xB8 +#define TPS80031_CLK32KAO_CFG_TRANS 0xBA +#define TPS80031_CLK32KAO_CFG_STATE 0xBB +#define TPS80031_CLK32KG_CFG_TRANS 0xBD +#define TPS80031_CLK32KG_CFG_STATE 0xBE +#define TPS80031_CLK32KAUDIO_CFG_TRANS 0xC0 +#define TPS80031_CLK32KAUDIO_CFG_STATE 0xC1 +#define TPS80031_VRTC_CFG_TRANS 0xC3 +#define TPS80031_VRTC_CFG_STATE 0xC4 +#define TPS80031_BIAS_CFG_TRANS 0xC6 +#define TPS80031_BIAS_CFG_STATE 0xC7 +#define TPS80031_VSYSMIN_HI_CFG_TRANS 0xC9 +#define TPS80031_VSYSMIN_HI_CFG_STATE 0xCA +#define TPS80031_RC6MHZ_CFG_TRANS 0xCC +#define TPS80031_RC6MHZ_CFG_STATE 0xCD +#define TPS80031_TMP_CFG_TRANS 0xCF +#define TPS80031_TMP_CFG_STATE 0xD0 + +/* PMC Slave Module resources assignment */ +#define TPS80031_PREQ1_RES_ASS_A 0xD7 +#define TPS80031_PREQ1_RES_ASS_B 0xD8 +#define TPS80031_PREQ1_RES_ASS_C 0xD9 +#define TPS80031_PREQ2_RES_ASS_A 0xDA +#define TPS80031_PREQ2_RES_ASS_B 0xDB +#define TPS80031_PREQ2_RES_ASS_C 0xDC +#define TPS80031_PREQ3_RES_ASS_A 0xDD +#define TPS80031_PREQ3_RES_ASS_B 0xDE +#define TPS80031_PREQ3_RES_ASS_C 0xDF + +/* PMC Slave Module Miscellaneous */ +#define TPS80031_SMPS_OFFSET 0xE0 +#define TPS80031_SMPS_MULT 0xE3 +#define TPS80031_MISC1 0xE4 +#define TPS80031_MISC2 0xE5 +#define TPS80031_BBSPOR_CFG 0xE6 +#define TPS80031_TMP_CFG 0xE7 + +/* Battery Charging Controller and Indicator LED */ +#define TPS80031_CONTROLLER_CTRL2 0xDA +#define TPS80031_CONTROLLER_VSEL_COMP 0xDB +#define TPS80031_CHARGERUSB_VSYSREG 0xDC +#define TPS80031_CHARGERUSB_VICHRG_PC 0xDD +#define TPS80031_LINEAR_CHRG_STS 0xDE +#define TPS80031_CONTROLLER_INT_MASK 0xE0 +#define TPS80031_CONTROLLER_CTRL1 0xE1 +#define TPS80031_CONTROLLER_WDG 0xE2 +#define TPS80031_CONTROLLER_STAT1 0xE3 +#define TPS80031_CHARGERUSB_INT_STATUS 0xE4 +#define TPS80031_CHARGERUSB_INT_MASK 0xE5 +#define TPS80031_CHARGERUSB_STATUS_INT1 0xE6 +#define TPS80031_CHARGERUSB_STATUS_INT2 0xE7 +#define TPS80031_CHARGERUSB_CTRL1 0xE8 +#define TPS80031_CHARGERUSB_CTRL2 0xE9 +#define TPS80031_CHARGERUSB_CTRL3 0xEA +#define TPS80031_CHARGERUSB_STAT1 0xEB +#define TPS80031_CHARGERUSB_VOREG 0xEC +#define TPS80031_CHARGERUSB_VICHRG 0xED +#define TPS80031_CHARGERUSB_CINLIMIT 0xEE +#define TPS80031_CHARGERUSB_CTRLLIMIT1 0xEF +#define TPS80031_CHARGERUSB_CTRLLIMIT2 0xF0 +#define TPS80031_LED_PWM_CTRL1 0xF4 +#define TPS80031_LED_PWM_CTRL2 0xF5 + +/* USB On-The-Go */ +#define TPS80031_BACKUP_REG 0xFA +#define TPS80031_USB_VENDOR_ID_LSB 0x00 +#define TPS80031_USB_VENDOR_ID_MSB 0x01 +#define TPS80031_USB_PRODUCT_ID_LSB 0x02 +#define TPS80031_USB_PRODUCT_ID_MSB 0x03 +#define TPS80031_USB_VBUS_CTRL_SET 0x04 +#define TPS80031_USB_VBUS_CTRL_CLR 0x05 +#define TPS80031_USB_ID_CTRL_SET 0x06 +#define TPS80031_USB_ID_CTRL_CLR 0x07 +#define TPS80031_USB_VBUS_INT_SRC 0x08 +#define TPS80031_USB_VBUS_INT_LATCH_SET 0x09 +#define TPS80031_USB_VBUS_INT_LATCH_CLR 0x0A +#define TPS80031_USB_VBUS_INT_EN_LO_SET 0x0B +#define TPS80031_USB_VBUS_INT_EN_LO_CLR 0x0C +#define TPS80031_USB_VBUS_INT_EN_HI_SET 0x0D +#define TPS80031_USB_VBUS_INT_EN_HI_CLR 0x0E +#define TPS80031_USB_ID_INT_SRC 0x0F +#define TPS80031_USB_ID_INT_LATCH_SET 0x10 +#define TPS80031_USB_ID_INT_LATCH_CLR 0x11 +#define TPS80031_USB_ID_INT_EN_LO_SET 0x12 +#define TPS80031_USB_ID_INT_EN_LO_CLR 0x13 +#define TPS80031_USB_ID_INT_EN_HI_SET 0x14 +#define TPS80031_USB_ID_INT_EN_HI_CLR 0x15 +#define TPS80031_USB_OTG_ADP_CTRL 0x16 +#define TPS80031_USB_OTG_ADP_HIGH 0x17 +#define TPS80031_USB_OTG_ADP_LOW 0x18 +#define TPS80031_USB_OTG_ADP_RISE 0x19 +#define TPS80031_USB_OTG_REVISION 0x1A + +/* Gas Gauge */ +#define TPS80031_FG_REG_00 0xC0 +#define TPS80031_FG_REG_01 0xC1 +#define TPS80031_FG_REG_02 0xC2 +#define TPS80031_FG_REG_03 0xC3 +#define TPS80031_FG_REG_04 0xC4 +#define TPS80031_FG_REG_05 0xC5 +#define TPS80031_FG_REG_06 0xC6 +#define TPS80031_FG_REG_07 0xC7 +#define TPS80031_FG_REG_08 0xC8 +#define TPS80031_FG_REG_09 0xC9 +#define TPS80031_FG_REG_10 0xCA +#define TPS80031_FG_REG_11 0xCB + +/* General Purpose ADC */ +#define TPS80031_GPADC_CTRL 0x2E +#define TPS80031_GPADC_CTRL2 0x2F +#define TPS80031_RTSELECT_LSB 0x32 +#define TPS80031_RTSELECT_ISB 0x33 +#define TPS80031_RTSELECT_MSB 0x34 +#define TPS80031_GPSELECT_ISB 0x35 +#define TPS80031_CTRL_P1 0x36 +#define TPS80031_RTCH0_LSB 0x37 +#define TPS80031_RTCH0_MSB 0x38 +#define TPS80031_RTCH1_LSB 0x39 +#define TPS80031_RTCH1_MSB 0x3A +#define TPS80031_GPCH0_LSB 0x3B +#define TPS80031_GPCH0_MSB 0x3C + +/* SIM, MMC and Battery Detection */ +#define TPS80031_SIMDEBOUNCING 0xEB +#define TPS80031_SIMCTRL 0xEC +#define TPS80031_MMCDEBOUNCING 0xED +#define TPS80031_MMCCTRL 0xEE +#define TPS80031_BATDEBOUNCING 0xEF + +/* Vibrator Driver and PWMs */ +#define TPS80031_VIBCTRL 0x9B +#define TPS80031_VIBMODE 0x9C +#define TPS80031_PWM1ON 0xBA +#define TPS80031_PWM1OFF 0xBB +#define TPS80031_PWM2ON 0xBD +#define TPS80031_PWM2OFF 0xBE + +/* Control Interface */ +#define TPS80031_INT_STS_A 0xD0 +#define TPS80031_INT_STS_B 0xD1 +#define TPS80031_INT_STS_C 0xD2 +#define TPS80031_INT_MSK_LINE_A 0xD3 +#define TPS80031_INT_MSK_LINE_B 0xD4 +#define TPS80031_INT_MSK_LINE_C 0xD5 +#define TPS80031_INT_MSK_STS_A 0xD6 +#define TPS80031_INT_MSK_STS_B 0xD7 +#define TPS80031_INT_MSK_STS_C 0xD8 +#define TPS80031_TOGGLE1 0x90 +#define TPS80031_TOGGLE2 0x91 +#define TPS80031_TOGGLE3 0x92 +#define TPS80031_PWDNSTATUS1 0x93 +#define TPS80031_PWDNSTATUS2 0x94 +#define TPS80031_VALIDITY0 0x17 +#define TPS80031_VALIDITY1 0x18 +#define TPS80031_VALIDITY2 0x19 +#define TPS80031_VALIDITY3 0x1A +#define TPS80031_VALIDITY4 0x1B +#define TPS80031_VALIDITY5 0x1C +#define TPS80031_VALIDITY6 0x1D +#define TPS80031_VALIDITY7 0x1E + +/* Version number related register */ +#define TPS80031_JTAGVERNUM 0x87 +#define TPS80031_EPROM_REV 0xDF + +/* GPADC Trimming Bits. */ +#define TPS80031_GPADC_TRIM0 0xCC +#define TPS80031_GPADC_TRIM1 0xCD +#define TPS80031_GPADC_TRIM2 0xCE +#define TPS80031_GPADC_TRIM3 0xCF +#define TPS80031_GPADC_TRIM4 0xD0 +#define TPS80031_GPADC_TRIM5 0xD1 +#define TPS80031_GPADC_TRIM6 0xD2 +#define TPS80031_GPADC_TRIM7 0xD3 +#define TPS80031_GPADC_TRIM8 0xD4 +#define TPS80031_GPADC_TRIM9 0xD5 +#define TPS80031_GPADC_TRIM10 0xD6 +#define TPS80031_GPADC_TRIM11 0xD7 +#define TPS80031_GPADC_TRIM12 0xD8 +#define TPS80031_GPADC_TRIM13 0xD9 +#define TPS80031_GPADC_TRIM14 0xDA +#define TPS80031_GPADC_TRIM15 0xDB +#define TPS80031_GPADC_TRIM16 0xDC +#define TPS80031_GPADC_TRIM17 0xDD +#define TPS80031_GPADC_TRIM18 0xDE + +/* TPS80031_CONTROLLER_STAT1 bit fields */ +#define TPS80031_CONTROLLER_STAT1_BAT_TEMP 0 +#define TPS80031_CONTROLLER_STAT1_BAT_REMOVED 1 +#define TPS80031_CONTROLLER_STAT1_VBUS_DET 2 +#define TPS80031_CONTROLLER_STAT1_VAC_DET 3 +#define TPS80031_CONTROLLER_STAT1_FAULT_WDG 4 +#define TPS80031_CONTROLLER_STAT1_LINCH_GATED 6 +/* TPS80031_CONTROLLER_INT_MASK bit filed */ +#define TPS80031_CONTROLLER_INT_MASK_MVAC_DET 0 +#define TPS80031_CONTROLLER_INT_MASK_MVBUS_DET 1 +#define TPS80031_CONTROLLER_INT_MASK_MBAT_TEMP 2 +#define TPS80031_CONTROLLER_INT_MASK_MFAULT_WDG 3 +#define TPS80031_CONTROLLER_INT_MASK_MBAT_REMOVED 4 +#define TPS80031_CONTROLLER_INT_MASK_MLINCH_GATED 5 + +#define TPS80031_CHARGE_CONTROL_SUB_INT_MASK 0x3F + +/* TPS80031_PHOENIX_DEV_ON bit field */ +#define TPS80031_DEVOFF 0x1 + +#define TPS80031_EXT_CONTROL_CFG_TRANS 0 +#define TPS80031_EXT_CONTROL_CFG_STATE 1 + +/* State register field */ +#define TPS80031_STATE_OFF 0x00 +#define TPS80031_STATE_ON 0x01 +#define TPS80031_STATE_MASK 0x03 + +/* Trans register field */ +#define TPS80031_TRANS_ACTIVE_OFF 0x00 +#define TPS80031_TRANS_ACTIVE_ON 0x01 +#define TPS80031_TRANS_ACTIVE_MASK 0x03 +#define TPS80031_TRANS_SLEEP_OFF 0x00 +#define TPS80031_TRANS_SLEEP_ON 0x04 +#define TPS80031_TRANS_SLEEP_MASK 0x0C +#define TPS80031_TRANS_OFF_OFF 0x00 +#define TPS80031_TRANS_OFF_ACTIVE 0x10 +#define TPS80031_TRANS_OFF_MASK 0x30 + +#define TPS80031_EXT_PWR_REQ (TPS80031_PWR_REQ_INPUT_PREQ1 | \ + TPS80031_PWR_REQ_INPUT_PREQ2 | \ + TPS80031_PWR_REQ_INPUT_PREQ3) + +/* TPS80031_BBSPOR_CFG bit field */ +#define TPS80031_BBSPOR_CHG_EN 0x8 +#define TPS80031_MAX_REGISTER 0xFF + +struct i2c_client; + +/* Supported chips */ +enum chips { + TPS80031 = 0x00000001, + TPS80032 = 0x00000002, +}; + +enum { + TPS80031_INT_PWRON, + TPS80031_INT_RPWRON, + TPS80031_INT_SYS_VLOW, + TPS80031_INT_RTC_ALARM, + TPS80031_INT_RTC_PERIOD, + TPS80031_INT_HOT_DIE, + TPS80031_INT_VXX_SHORT, + TPS80031_INT_SPDURATION, + TPS80031_INT_WATCHDOG, + TPS80031_INT_BAT, + TPS80031_INT_SIM, + TPS80031_INT_MMC, + TPS80031_INT_RES, + TPS80031_INT_GPADC_RT, + TPS80031_INT_GPADC_SW2_EOC, + TPS80031_INT_CC_AUTOCAL, + TPS80031_INT_ID_WKUP, + TPS80031_INT_VBUSS_WKUP, + TPS80031_INT_ID, + TPS80031_INT_VBUS, + TPS80031_INT_CHRG_CTRL, + TPS80031_INT_EXT_CHRG, + TPS80031_INT_INT_CHRG, + TPS80031_INT_RES2, + TPS80031_INT_BAT_TEMP_OVRANGE, + TPS80031_INT_BAT_REMOVED, + TPS80031_INT_VBUS_DET, + TPS80031_INT_VAC_DET, + TPS80031_INT_FAULT_WDG, + TPS80031_INT_LINCH_GATED, + + /* Last interrupt id to get the end number */ + TPS80031_INT_NR, +}; + +/* TPS80031 Slave IDs */ +#define TPS80031_NUM_SLAVES 4 +#define TPS80031_SLAVE_ID0 0 +#define TPS80031_SLAVE_ID1 1 +#define TPS80031_SLAVE_ID2 2 +#define TPS80031_SLAVE_ID3 3 + +/* TPS80031 I2C addresses */ +#define TPS80031_I2C_ID0_ADDR 0x12 +#define TPS80031_I2C_ID1_ADDR 0x48 +#define TPS80031_I2C_ID2_ADDR 0x49 +#define TPS80031_I2C_ID3_ADDR 0x4A + +enum { + TPS80031_REGULATOR_VIO, + TPS80031_REGULATOR_SMPS1, + TPS80031_REGULATOR_SMPS2, + TPS80031_REGULATOR_SMPS3, + TPS80031_REGULATOR_SMPS4, + TPS80031_REGULATOR_VANA, + TPS80031_REGULATOR_LDO1, + TPS80031_REGULATOR_LDO2, + TPS80031_REGULATOR_LDO3, + TPS80031_REGULATOR_LDO4, + TPS80031_REGULATOR_LDO5, + TPS80031_REGULATOR_LDO6, + TPS80031_REGULATOR_LDO7, + TPS80031_REGULATOR_LDOLN, + TPS80031_REGULATOR_LDOUSB, + TPS80031_REGULATOR_VBUS, + TPS80031_REGULATOR_REGEN1, + TPS80031_REGULATOR_REGEN2, + TPS80031_REGULATOR_SYSEN, + TPS80031_REGULATOR_MAX, +}; + +/* Different configurations for the rails */ +enum { + /* USBLDO input selection */ + TPS80031_USBLDO_INPUT_VSYS = 0x00000001, + TPS80031_USBLDO_INPUT_PMID = 0x00000002, + + /* LDO3 output mode */ + TPS80031_LDO3_OUTPUT_VIB = 0x00000004, + + /* VBUS configuration */ + TPS80031_VBUS_DISCHRG_EN_PDN = 0x00000004, + TPS80031_VBUS_SW_ONLY = 0x00000008, + TPS80031_VBUS_SW_N_ID = 0x00000010, +}; + +/* External controls requests */ +enum tps80031_ext_control { + TPS80031_PWR_REQ_INPUT_NONE = 0x00000000, + TPS80031_PWR_REQ_INPUT_PREQ1 = 0x00000001, + TPS80031_PWR_REQ_INPUT_PREQ2 = 0x00000002, + TPS80031_PWR_REQ_INPUT_PREQ3 = 0x00000004, + TPS80031_PWR_OFF_ON_SLEEP = 0x00000008, + TPS80031_PWR_ON_ON_SLEEP = 0x00000010, +}; + +enum tps80031_pupd_pins { + TPS80031_PREQ1 = 0, + TPS80031_PREQ2A, + TPS80031_PREQ2B, + TPS80031_PREQ2C, + TPS80031_PREQ3, + TPS80031_NRES_WARM, + TPS80031_PWM_FORCE, + TPS80031_CHRG_EXT_CHRG_STATZ, + TPS80031_SIM, + TPS80031_MMC, + TPS80031_GPADC_START, + TPS80031_DVSI2C_SCL, + TPS80031_DVSI2C_SDA, + TPS80031_CTLI2C_SCL, + TPS80031_CTLI2C_SDA, +}; + +enum tps80031_pupd_settings { + TPS80031_PUPD_NORMAL, + TPS80031_PUPD_PULLDOWN, + TPS80031_PUPD_PULLUP, +}; + +struct tps80031 { + struct device *dev; + unsigned long chip_info; + int es_version; + struct i2c_client *clients[TPS80031_NUM_SLAVES]; + struct regmap *regmap[TPS80031_NUM_SLAVES]; + struct regmap_irq_chip_data *irq_data; +}; + +struct tps80031_pupd_init_data { + int input_pin; + int setting; +}; + +/* + * struct tps80031_regulator_platform_data - tps80031 regulator platform data. + * + * @reg_init_data: The regulator init data. + * @ext_ctrl_flag: External control flag for sleep/power request control. + * @config_flags: Configuration flag to configure the rails. + * It should be ORed of config enums. + */ + +struct tps80031_regulator_platform_data { + struct regulator_init_data *reg_init_data; + unsigned int ext_ctrl_flag; + unsigned int config_flags; +}; + +struct tps80031_platform_data { + int irq_base; + bool use_power_off; + struct tps80031_pupd_init_data *pupd_init_data; + int pupd_init_data_size; + struct tps80031_regulator_platform_data + *regulator_pdata[TPS80031_REGULATOR_MAX]; +}; + +static inline int tps80031_write(struct device *dev, int sid, + int reg, uint8_t val) +{ + struct tps80031 *tps80031 = dev_get_drvdata(dev); + + return regmap_write(tps80031->regmap[sid], reg, val); +} + +static inline int tps80031_writes(struct device *dev, int sid, int reg, + int len, uint8_t *val) +{ + struct tps80031 *tps80031 = dev_get_drvdata(dev); + + return regmap_bulk_write(tps80031->regmap[sid], reg, val, len); +} + +static inline int tps80031_read(struct device *dev, int sid, + int reg, uint8_t *val) +{ + struct tps80031 *tps80031 = dev_get_drvdata(dev); + unsigned int ival; + int ret; + + ret = regmap_read(tps80031->regmap[sid], reg, &ival); + if (ret < 0) { + dev_err(dev, "failed reading from reg 0x%02x\n", reg); + return ret; + } + + *val = ival; + return ret; +} + +static inline int tps80031_reads(struct device *dev, int sid, + int reg, int len, uint8_t *val) +{ + struct tps80031 *tps80031 = dev_get_drvdata(dev); + + return regmap_bulk_read(tps80031->regmap[sid], reg, val, len); +} + +static inline int tps80031_set_bits(struct device *dev, int sid, + int reg, uint8_t bit_mask) +{ + struct tps80031 *tps80031 = dev_get_drvdata(dev); + + return regmap_update_bits(tps80031->regmap[sid], reg, + bit_mask, bit_mask); +} + +static inline int tps80031_clr_bits(struct device *dev, int sid, + int reg, uint8_t bit_mask) +{ + struct tps80031 *tps80031 = dev_get_drvdata(dev); + + return regmap_update_bits(tps80031->regmap[sid], reg, bit_mask, 0); +} + +static inline int tps80031_update(struct device *dev, int sid, + int reg, uint8_t val, uint8_t mask) +{ + struct tps80031 *tps80031 = dev_get_drvdata(dev); + + return regmap_update_bits(tps80031->regmap[sid], reg, mask, val); +} + +static inline unsigned long tps80031_get_chip_info(struct device *dev) +{ + struct tps80031 *tps80031 = dev_get_drvdata(dev); + + return tps80031->chip_info; +} + +static inline int tps80031_get_pmu_version(struct device *dev) +{ + struct tps80031 *tps80031 = dev_get_drvdata(dev); + + return tps80031->es_version; +} + +static inline int tps80031_irq_get_virq(struct device *dev, int irq) +{ + struct tps80031 *tps80031 = dev_get_drvdata(dev); + + return regmap_irq_get_virq(tps80031->irq_data, irq); +} + +extern int tps80031_ext_power_req_config(struct device *dev, + unsigned long ext_ctrl_flag, int preq_bit, + int state_reg_add, int trans_reg_add); +#endif /*__LINUX_MFD_TPS80031_H */ From e2e8ffc97029a1159ac83bce1933a7e539f16a9d Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 20 Nov 2012 08:44:45 +0530 Subject: [PATCH 063/119] mfd: Add battery charger in tps65090 sub devs TPS65090 supports the battery charging and hence adding the device name in the list of TPS65090 children. Also remove the tps65090-regulator as it duplicates with tps65090-pmic for regulator driver. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65090.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 074ae32b0d2a..9b79d6811cae 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -74,7 +74,7 @@ static struct mfd_cell tps65090s[] = { .name = "tps65090-pmic", }, { - .name = "tps65090-regulator", + .name = "tps65090-charger", }, }; From e8e6f047e666a1682b59e52637a7acaa8a0b4c89 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 20 Nov 2012 08:44:46 +0530 Subject: [PATCH 064/119] mfd: tps65090: Add error prints when mem alloc failed Add error prints when memory allocation failed for tps65090 data. Also cleanups the melloc arguments. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65090.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 9b79d6811cae..f95f7f6b846c 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -263,10 +263,11 @@ static int __devinit tps65090_i2c_probe(struct i2c_client *client, return -EINVAL; } - tps65090 = devm_kzalloc(&client->dev, sizeof(struct tps65090), - GFP_KERNEL); - if (tps65090 == NULL) + tps65090 = devm_kzalloc(&client->dev, sizeof(*tps65090), GFP_KERNEL); + if (!tps65090) { + dev_err(&client->dev, "mem alloc for tps65090 failed\n"); return -ENOMEM; + } tps65090->client = client; tps65090->dev = &client->dev; From 3863db3e800c64e21e4effcc3de0f72cdb9b0d77 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 20 Nov 2012 08:44:47 +0530 Subject: [PATCH 065/119] mfd: tps65090: Remove unused member of struct tps65090 Remove unused member from tps65090 data structure as these are not used. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65090.c | 6 +----- include/linux/mfd/tps65090.h | 11 ----------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index f95f7f6b846c..3cfc9dcbe9dc 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -269,12 +269,9 @@ static int __devinit tps65090_i2c_probe(struct i2c_client *client, return -ENOMEM; } - tps65090->client = client; tps65090->dev = &client->dev; i2c_set_clientdata(client, tps65090); - mutex_init(&tps65090->lock); - if (client->irq) { ret = tps65090_irq_init(tps65090, client->irq, pdata->irq_base); if (ret) { @@ -284,8 +281,7 @@ static int __devinit tps65090_i2c_probe(struct i2c_client *client, } } - tps65090->rmap = devm_regmap_init_i2c(tps65090->client, - &tps65090_regmap_config); + tps65090->rmap = devm_regmap_init_i2c(client, &tps65090_regmap_config); if (IS_ERR(tps65090->rmap)) { ret = PTR_ERR(tps65090->rmap); dev_err(&client->dev, "regmap_init failed with err: %d\n", ret); diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 6bc31d854626..6c576224f637 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -25,26 +25,15 @@ #include struct tps65090 { - struct mutex lock; struct device *dev; - struct i2c_client *client; struct regmap *rmap; struct irq_chip irq_chip; struct mutex irq_lock; int irq_base; - unsigned int id; -}; - -struct tps65090_subdev_info { - int id; - const char *name; - void *platform_data; }; struct tps65090_platform_data { int irq_base; - int num_subdevs; - struct tps65090_subdev_info *subdevs; }; /* From b9c79323166530a14c1fa8c10337eeaa54e3f98d Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 20 Nov 2012 08:44:48 +0530 Subject: [PATCH 066/119] mfd: tps65090: Move register access APIs to header Since tps65090 register is accessed via regmap, moving the register access APIs to header and making it as inline. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65090.c | 34 ------------------------------- include/linux/mfd/tps65090.h | 39 ++++++++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 38 deletions(-) diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 3cfc9dcbe9dc..355a07749454 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #define NUM_INT_REG 2 @@ -78,39 +77,6 @@ static struct mfd_cell tps65090s[] = { }, }; -int tps65090_write(struct device *dev, int reg, uint8_t val) -{ - struct tps65090 *tps = dev_get_drvdata(dev); - return regmap_write(tps->rmap, reg, val); -} -EXPORT_SYMBOL_GPL(tps65090_write); - -int tps65090_read(struct device *dev, int reg, uint8_t *val) -{ - struct tps65090 *tps = dev_get_drvdata(dev); - unsigned int temp_val; - int ret; - ret = regmap_read(tps->rmap, reg, &temp_val); - if (!ret) - *val = temp_val; - return ret; -} -EXPORT_SYMBOL_GPL(tps65090_read); - -int tps65090_set_bits(struct device *dev, int reg, uint8_t bit_num) -{ - struct tps65090 *tps = dev_get_drvdata(dev); - return regmap_update_bits(tps->rmap, reg, BIT(bit_num), ~0u); -} -EXPORT_SYMBOL_GPL(tps65090_set_bits); - -int tps65090_clr_bits(struct device *dev, int reg, uint8_t bit_num) -{ - struct tps65090 *tps = dev_get_drvdata(dev); - return regmap_update_bits(tps->rmap, reg, BIT(bit_num), 0u); -} -EXPORT_SYMBOL_GPL(tps65090_clr_bits); - static void tps65090_irq_lock(struct irq_data *data) { struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data); diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 6c576224f637..1a5f916b7383 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -23,6 +23,7 @@ #define __LINUX_MFD_TPS65090_H #include +#include struct tps65090 { struct device *dev; @@ -40,9 +41,39 @@ struct tps65090_platform_data { * NOTE: the functions below are not intended for use outside * of the TPS65090 sub-device drivers */ -extern int tps65090_write(struct device *dev, int reg, uint8_t val); -extern int tps65090_read(struct device *dev, int reg, uint8_t *val); -extern int tps65090_set_bits(struct device *dev, int reg, uint8_t bit_num); -extern int tps65090_clr_bits(struct device *dev, int reg, uint8_t bit_num); +static inline int tps65090_write(struct device *dev, int reg, uint8_t val) +{ + struct tps65090 *tps = dev_get_drvdata(dev); + + return regmap_write(tps->rmap, reg, val); +} + +static inline int tps65090_read(struct device *dev, int reg, uint8_t *val) +{ + struct tps65090 *tps = dev_get_drvdata(dev); + unsigned int temp_val; + int ret; + + ret = regmap_read(tps->rmap, reg, &temp_val); + if (!ret) + *val = temp_val; + return ret; +} + +static inline int tps65090_set_bits(struct device *dev, int reg, + uint8_t bit_num) +{ + struct tps65090 *tps = dev_get_drvdata(dev); + + return regmap_update_bits(tps->rmap, reg, BIT(bit_num), ~0u); +} + +static inline int tps65090_clr_bits(struct device *dev, int reg, + uint8_t bit_num) +{ + struct tps65090 *tps = dev_get_drvdata(dev); + + return regmap_update_bits(tps->rmap, reg, BIT(bit_num), 0u); +} #endif /*__LINUX_MFD_TPS65090_H */ From 759f2598ef3876637e40d99a4ceb7a3d83a4d8d3 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 20 Nov 2012 08:44:49 +0530 Subject: [PATCH 067/119] mfd: tps65090: Use regmap irq framework for interrupt support Use the regmap irq framework for implementing TPS65090 interrupt support in place of implementing it locally. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65090.c | 263 +++++++++++++---------------------- include/linux/mfd/tps65090.h | 23 ++- 2 files changed, 118 insertions(+), 168 deletions(-) diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 355a07749454..2eaae52cb5b8 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -38,35 +38,21 @@ #define TPS65090_INT_MSK 0x2 #define TPS65090_INT_MSK2 0x3 -struct tps65090_irq_data { - u8 mask_reg; - u8 mask_pos; -}; - -#define TPS65090_IRQ(_reg, _mask_pos) \ - { \ - .mask_reg = (_reg), \ - .mask_pos = (_mask_pos), \ - } - -static const struct tps65090_irq_data tps65090_irqs[] = { - [0] = TPS65090_IRQ(0, 0), - [1] = TPS65090_IRQ(0, 1), - [2] = TPS65090_IRQ(0, 2), - [3] = TPS65090_IRQ(0, 3), - [4] = TPS65090_IRQ(0, 4), - [5] = TPS65090_IRQ(0, 5), - [6] = TPS65090_IRQ(0, 6), - [7] = TPS65090_IRQ(0, 7), - [8] = TPS65090_IRQ(1, 0), - [9] = TPS65090_IRQ(1, 1), - [10] = TPS65090_IRQ(1, 2), - [11] = TPS65090_IRQ(1, 3), - [12] = TPS65090_IRQ(1, 4), - [13] = TPS65090_IRQ(1, 5), - [14] = TPS65090_IRQ(1, 6), - [15] = TPS65090_IRQ(1, 7), -}; +#define TPS65090_INT1_MASK_VAC_STATUS_CHANGE 1 +#define TPS65090_INT1_MASK_VSYS_STATUS_CHANGE 2 +#define TPS65090_INT1_MASK_BAT_STATUS_CHANGE 3 +#define TPS65090_INT1_MASK_CHARGING_STATUS_CHANGE 4 +#define TPS65090_INT1_MASK_CHARGING_COMPLETE 5 +#define TPS65090_INT1_MASK_OVERLOAD_DCDC1 6 +#define TPS65090_INT1_MASK_OVERLOAD_DCDC2 7 +#define TPS65090_INT2_MASK_OVERLOAD_DCDC3 0 +#define TPS65090_INT2_MASK_OVERLOAD_FET1 1 +#define TPS65090_INT2_MASK_OVERLOAD_FET2 2 +#define TPS65090_INT2_MASK_OVERLOAD_FET3 3 +#define TPS65090_INT2_MASK_OVERLOAD_FET4 4 +#define TPS65090_INT2_MASK_OVERLOAD_FET5 5 +#define TPS65090_INT2_MASK_OVERLOAD_FET6 6 +#define TPS65090_INT2_MASK_OVERLOAD_FET7 7 static struct mfd_cell tps65090s[] = { { @@ -77,132 +63,77 @@ static struct mfd_cell tps65090s[] = { }, }; -static void tps65090_irq_lock(struct irq_data *data) -{ - struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data); +static const struct regmap_irq tps65090_irqs[] = { + /* INT1 IRQs*/ + [TPS65090_IRQ_VAC_STATUS_CHANGE] = { + .mask = TPS65090_INT1_MASK_VAC_STATUS_CHANGE, + }, + [TPS65090_IRQ_VSYS_STATUS_CHANGE] = { + .mask = TPS65090_INT1_MASK_VSYS_STATUS_CHANGE, + }, + [TPS65090_IRQ_BAT_STATUS_CHANGE] = { + .mask = TPS65090_INT1_MASK_BAT_STATUS_CHANGE, + }, + [TPS65090_IRQ_CHARGING_STATUS_CHANGE] = { + .mask = TPS65090_INT1_MASK_CHARGING_STATUS_CHANGE, + }, + [TPS65090_IRQ_CHARGING_COMPLETE] = { + .mask = TPS65090_INT1_MASK_CHARGING_COMPLETE, + }, + [TPS65090_IRQ_OVERLOAD_DCDC1] = { + .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC1, + }, + [TPS65090_IRQ_OVERLOAD_DCDC2] = { + .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC2, + }, + /* INT2 IRQs*/ + [TPS65090_IRQ_OVERLOAD_DCDC3] = { + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_DCDC3, + }, + [TPS65090_IRQ_OVERLOAD_FET1] = { + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET1, + }, + [TPS65090_IRQ_OVERLOAD_FET2] = { + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET2, + }, + [TPS65090_IRQ_OVERLOAD_FET3] = { + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET3, + }, + [TPS65090_IRQ_OVERLOAD_FET4] = { + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET4, + }, + [TPS65090_IRQ_OVERLOAD_FET5] = { + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET5, + }, + [TPS65090_IRQ_OVERLOAD_FET6] = { + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET6, + }, + [TPS65090_IRQ_OVERLOAD_FET7] = { + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET7, + }, +}; - mutex_lock(&tps65090->irq_lock); -} - -static void tps65090_irq_mask(struct irq_data *irq_data) -{ - struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data); - unsigned int __irq = irq_data->hwirq; - const struct tps65090_irq_data *data = &tps65090_irqs[__irq]; - - tps65090_set_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg), - data->mask_pos); -} - -static void tps65090_irq_unmask(struct irq_data *irq_data) -{ - struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data); - unsigned int __irq = irq_data->irq - tps65090->irq_base; - const struct tps65090_irq_data *data = &tps65090_irqs[__irq]; - - tps65090_clr_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg), - data->mask_pos); -} - -static void tps65090_irq_sync_unlock(struct irq_data *data) -{ - struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data); - - mutex_unlock(&tps65090->irq_lock); -} - -static irqreturn_t tps65090_irq(int irq, void *data) -{ - struct tps65090 *tps65090 = data; - int ret = 0; - u8 status, mask; - unsigned long int acks = 0; - int i; - - for (i = 0; i < NUM_INT_REG; i++) { - ret = tps65090_read(tps65090->dev, TPS65090_INT_MSK + i, &mask); - if (ret < 0) { - dev_err(tps65090->dev, - "failed to read mask reg [addr:%d]\n", - TPS65090_INT_MSK + i); - return IRQ_NONE; - } - ret = tps65090_read(tps65090->dev, TPS65090_INT_STS + i, - &status); - if (ret < 0) { - dev_err(tps65090->dev, - "failed to read status reg [addr:%d]\n", - TPS65090_INT_STS + i); - return IRQ_NONE; - } - if (status) { - /* Ack only those interrupts which are not masked */ - status &= (~mask); - ret = tps65090_write(tps65090->dev, - TPS65090_INT_STS + i, status); - if (ret < 0) { - dev_err(tps65090->dev, - "failed to write interrupt status\n"); - return IRQ_NONE; - } - acks |= (status << (i * 8)); - } - } - - for_each_set_bit(i, &acks, ARRAY_SIZE(tps65090_irqs)) - handle_nested_irq(tps65090->irq_base + i); - return acks ? IRQ_HANDLED : IRQ_NONE; -} - -static int __devinit tps65090_irq_init(struct tps65090 *tps65090, int irq, - int irq_base) -{ - int i, ret; - - if (!irq_base) { - dev_err(tps65090->dev, "IRQ base not set\n"); - return -EINVAL; - } - - mutex_init(&tps65090->irq_lock); - - for (i = 0; i < NUM_INT_REG; i++) - tps65090_write(tps65090->dev, TPS65090_INT_MSK + i, 0xFF); - - for (i = 0; i < NUM_INT_REG; i++) - tps65090_write(tps65090->dev, TPS65090_INT_STS + i, 0xff); - - tps65090->irq_base = irq_base; - tps65090->irq_chip.name = "tps65090"; - tps65090->irq_chip.irq_mask = tps65090_irq_mask; - tps65090->irq_chip.irq_unmask = tps65090_irq_unmask; - tps65090->irq_chip.irq_bus_lock = tps65090_irq_lock; - tps65090->irq_chip.irq_bus_sync_unlock = tps65090_irq_sync_unlock; - - for (i = 0; i < ARRAY_SIZE(tps65090_irqs); i++) { - int __irq = i + tps65090->irq_base; - irq_set_chip_data(__irq, tps65090); - irq_set_chip_and_handler(__irq, &tps65090->irq_chip, - handle_simple_irq); - irq_set_nested_thread(__irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(__irq, IRQF_VALID); -#endif - } - - ret = request_threaded_irq(irq, NULL, tps65090_irq, IRQF_ONESHOT, - "tps65090", tps65090); - if (!ret) { - device_init_wakeup(tps65090->dev, 1); - enable_irq_wake(irq); - } - - return ret; -} +static struct regmap_irq_chip tps65090_irq_chip = { + .name = "tps65090", + .irqs = tps65090_irqs, + .num_irqs = ARRAY_SIZE(tps65090_irqs), + .num_regs = NUM_INT_REG, + .status_base = TPS65090_INT_STS, + .mask_base = TPS65090_INT_MSK, + .mask_invert = true, +}; static bool is_volatile_reg(struct device *dev, unsigned int reg) { - if (reg == TPS65090_INT_STS) + if ((reg == TPS65090_INT_STS) || (reg == TPS65090_INT_STS2)) return true; else return false; @@ -238,24 +169,27 @@ static int __devinit tps65090_i2c_probe(struct i2c_client *client, tps65090->dev = &client->dev; i2c_set_clientdata(client, tps65090); - if (client->irq) { - ret = tps65090_irq_init(tps65090, client->irq, pdata->irq_base); - if (ret) { - dev_err(&client->dev, "IRQ init failed with err: %d\n", - ret); - goto err_exit; - } - } - tps65090->rmap = devm_regmap_init_i2c(client, &tps65090_regmap_config); if (IS_ERR(tps65090->rmap)) { ret = PTR_ERR(tps65090->rmap); dev_err(&client->dev, "regmap_init failed with err: %d\n", ret); - goto err_irq_exit; + return ret; + } + + if (client->irq) { + ret = regmap_add_irq_chip(tps65090->rmap, client->irq, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, pdata->irq_base, + &tps65090_irq_chip, &tps65090->irq_data); + if (ret) { + dev_err(&client->dev, + "IRQ init failed with err: %d\n", ret); + return ret; + } } ret = mfd_add_devices(tps65090->dev, -1, tps65090s, - ARRAY_SIZE(tps65090s), NULL, 0, NULL); + ARRAY_SIZE(tps65090s), NULL, + regmap_irq_chip_get_base(tps65090->irq_data), NULL); if (ret) { dev_err(&client->dev, "add mfd devices failed with err: %d\n", ret); @@ -266,8 +200,7 @@ static int __devinit tps65090_i2c_probe(struct i2c_client *client, err_irq_exit: if (client->irq) - free_irq(client->irq, tps65090); -err_exit: + regmap_del_irq_chip(client->irq, tps65090->irq_data); return ret; } @@ -277,7 +210,7 @@ static int __devexit tps65090_i2c_remove(struct i2c_client *client) mfd_remove_devices(tps65090->dev); if (client->irq) - free_irq(client->irq, tps65090); + regmap_del_irq_chip(client->irq, tps65090->irq_data); return 0; } diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 1a5f916b7383..4bbbb1350b91 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -25,12 +25,29 @@ #include #include +/* TPS65090 IRQs */ +enum { + TPS65090_IRQ_VAC_STATUS_CHANGE, + TPS65090_IRQ_VSYS_STATUS_CHANGE, + TPS65090_IRQ_BAT_STATUS_CHANGE, + TPS65090_IRQ_CHARGING_STATUS_CHANGE, + TPS65090_IRQ_CHARGING_COMPLETE, + TPS65090_IRQ_OVERLOAD_DCDC1, + TPS65090_IRQ_OVERLOAD_DCDC2, + TPS65090_IRQ_OVERLOAD_DCDC3, + TPS65090_IRQ_OVERLOAD_FET1, + TPS65090_IRQ_OVERLOAD_FET2, + TPS65090_IRQ_OVERLOAD_FET3, + TPS65090_IRQ_OVERLOAD_FET4, + TPS65090_IRQ_OVERLOAD_FET5, + TPS65090_IRQ_OVERLOAD_FET6, + TPS65090_IRQ_OVERLOAD_FET7, +}; + struct tps65090 { struct device *dev; struct regmap *rmap; - struct irq_chip irq_chip; - struct mutex irq_lock; - int irq_base; + struct regmap_irq_chip_data *irq_data; }; struct tps65090_platform_data { From 38d324df75e6642ea6f71bc67765cc5b8c4f751b Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 20 Nov 2012 11:24:36 +0800 Subject: [PATCH 068/119] mmc: rtsx: Configure SD_CFG2 register in sd_rw_multi For Realtek card reader, internal regsiter SD_CFG2 should be configured before transferring data. The default value of SD_CFG2 is proper for writing data. But for reading sequence, the timing is not good enough. So in some extreme circumstance, card reader may sample the response data from the card as good even if the data is wrong. And this will cause the bad consequence. In the prior version, the value of this register has been calculated, but forgotten to write back to the internal register. Signed-off-by: Wei WANG Acked-by: Chris Ball Signed-off-by: Samuel Ortiz --- drivers/mmc/host/rtsx_pci_sdmmc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 0e934bf51309..067dd4627362 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -405,6 +405,7 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, cfg2); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF, trans_mode | SD_TRANSFER_START); rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, From 433e075c7f3b45c946c539ceeddc641c61adef5e Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 20 Nov 2012 11:24:44 +0800 Subject: [PATCH 069/119] mmc: rtsx: Explicitely include slab.h in rtsx_pci_sdmmc.c This fixes the following build error on some architectures (parisc at least): drivers/mmc/host/rtsx_pci_sdmmc.c: In function 'sd_normal_rw': drivers/mmc/host/rtsx_pci_sdmmc.c:448:2: error: implicit declaration of function 'kzalloc' [-Werror=implicit-function-declaration] drivers/mmc/host/rtsx_pci_sdmmc.c:448:6: warning: assignment makes pointer from integer without a cast [enabled by default] drivers/mmc/host/rtsx_pci_sdmmc.c:472:2: error: implicit declaration of function 'kfree' [-Werror=implicit-function-declaration] Signed-off-by: Wei WANG Acked-by: Chris Ball Signed-off-by: Samuel Ortiz --- drivers/mmc/host/rtsx_pci_sdmmc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 067dd4627362..571915dfb218 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -21,6 +21,7 @@ */ #include +#include #include #include #include From eb433dad48b49f757255f2860db5c2bd61dcae0d Mon Sep 17 00:00:00 2001 From: Colin Foe-Parker Date: Tue, 20 Nov 2012 15:18:44 +0530 Subject: [PATCH 070/119] mfd: tps65217: Set PMIC to shutdown on PWR_EN toggle Set tps65217 PMIC status to OFF if power enable toggle is supported. By setting this bit to 1 to enter PMIC to OFF state when PWR_EN pin is pulled low. Also adds a DT flag to specify that device pmic supports shutdown control or not. Signed-off-by: Colin Foe-Parker [anilkumar@ti.com: move the additions to tps65217 MFD driver] Signed-off-by: AnilKumar Ch Signed-off-by: Samuel Ortiz --- .../devicetree/bindings/regulator/tps65217.txt | 4 ++++ drivers/mfd/tps65217.c | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/tps65217.txt b/Documentation/devicetree/bindings/regulator/tps65217.txt index d316fb895daf..4f05d208c95c 100644 --- a/Documentation/devicetree/bindings/regulator/tps65217.txt +++ b/Documentation/devicetree/bindings/regulator/tps65217.txt @@ -11,6 +11,9 @@ Required properties: using the standard binding for regulators found at Documentation/devicetree/bindings/regulator/regulator.txt. +Optional properties: +- ti,pmic-shutdown-controller: Telling the PMIC to shutdown on PWR_EN toggle. + The valid names for regulators are: tps65217: dcdc1, dcdc2, dcdc3, ldo1, ldo2, ldo3 and ldo4 @@ -20,6 +23,7 @@ Example: tps: tps@24 { compatible = "ti,tps65217"; + ti,pmic-shutdown-controller; regulators { dcdc1_reg: dcdc1 { diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index 3fb32e655254..c7f17d86db78 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -160,6 +160,7 @@ static int __devinit tps65217_probe(struct i2c_client *client, unsigned int version; unsigned int chip_id = ids->driver_data; const struct of_device_id *match; + bool status_off = false; int ret; if (client->dev.of_node) { @@ -170,6 +171,8 @@ static int __devinit tps65217_probe(struct i2c_client *client, return -EINVAL; } chip_id = (unsigned int)match->data; + status_off = of_property_read_bool(client->dev.of_node, + "ti,pmic-shutdown-controller"); } if (!chip_id) { @@ -207,6 +210,15 @@ static int __devinit tps65217_probe(struct i2c_client *client, return ret; } + /* Set the PMIC to shutdown on PWR_EN toggle */ + if (status_off) { + ret = tps65217_set_bits(tps, TPS65217_REG_STATUS, + TPS65217_STATUS_OFF, TPS65217_STATUS_OFF, + TPS65217_PROTECT_NONE); + if (ret) + dev_warn(tps->dev, "unable to set the status OFF\n"); + } + dev_info(tps->dev, "TPS65217 ID %#x version 1.%d\n", (version & TPS65217_CHIPID_CHIP_MASK) >> 4, version & TPS65217_CHIPID_REV_MASK); From 442613f974cd6e2ff6faa4b07215240a970f61e7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 20 Nov 2012 16:53:47 +0900 Subject: [PATCH 071/119] mfd: wm5102: Make FLL NCO test registers readable They contain documented status readback fields. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 1ade455a28c2..5cb3374cde54 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -1571,6 +1571,7 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_FLL1_CONTROL_5: case ARIZONA_FLL1_CONTROL_6: case ARIZONA_FLL1_LOOP_FILTER_TEST_1: + case ARIZONA_FLL1_NCO_TEST_0: case ARIZONA_FLL1_SYNCHRONISER_1: case ARIZONA_FLL1_SYNCHRONISER_2: case ARIZONA_FLL1_SYNCHRONISER_3: @@ -1586,6 +1587,7 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_FLL2_CONTROL_5: case ARIZONA_FLL2_CONTROL_6: case ARIZONA_FLL2_LOOP_FILTER_TEST_1: + case ARIZONA_FLL2_NCO_TEST_0: case ARIZONA_FLL2_SYNCHRONISER_1: case ARIZONA_FLL2_SYNCHRONISER_2: case ARIZONA_FLL2_SYNCHRONISER_3: @@ -2339,6 +2341,8 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_SAMPLE_RATE_3_STATUS: case ARIZONA_HAPTICS_STATUS: case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS: + case ARIZONA_FLL1_NCO_TEST_0: + case ARIZONA_FLL2_NCO_TEST_0: case ARIZONA_FX_CTRL2: case ARIZONA_INTERRUPT_STATUS_1: case ARIZONA_INTERRUPT_STATUS_2: From dac98aef59eae72c74d9d2464f389f4def15a347 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 20 Nov 2012 11:19:15 +0530 Subject: [PATCH 072/119] mfd: twl6040: Remove duplicate inclusion of linux/err.h linux/err.h was included twice. Signed-off-by: Sachin Kamat Signed-off-by: Samuel Ortiz --- drivers/mfd/twl6040.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index e5f7b795afff..583be76e36a8 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include From c7b76dce8ac95fd464bfae741b830d407884c274 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Sun, 18 Nov 2012 18:36:20 +0200 Subject: [PATCH 073/119] mfd: Introduce retu-mfd driver Retu is a multi-function device found on Nokia Internet Tablets implementing at least watchdog, RTC, headset detection and power button functionality. This patch implements minimum functionality providing register access, IRQ handling and power off functions. Acked-by: Felipe Balbi Acked-by: Tony Lindgren Signed-off-by: Aaro Koskinen Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 9 ++ drivers/mfd/Makefile | 1 + drivers/mfd/retu-mfd.c | 264 +++++++++++++++++++++++++++++++++++++++ include/linux/mfd/retu.h | 22 ++++ 4 files changed, 296 insertions(+) create mode 100644 drivers/mfd/retu-mfd.c create mode 100644 include/linux/mfd/retu.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ca633df7c330..f5b839b718aa 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1094,6 +1094,15 @@ config MFD_VIPERBOARD You need to select the mfd cell drivers separately. The drivers do not support all features the board exposes. +config MFD_RETU + tristate "Support for Retu multi-function device" + select MFD_CORE + depends on I2C + select REGMAP_IRQ + help + Retu is a multi-function device found on Nokia Internet Tablets + (770, N800 and N810). + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 8072460e99d2..2689c8a0d781 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -145,3 +145,4 @@ obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o +obj-$(CONFIG_MFD_RETU) += retu-mfd.o diff --git a/drivers/mfd/retu-mfd.c b/drivers/mfd/retu-mfd.c new file mode 100644 index 000000000000..7ff4a37ab0c0 --- /dev/null +++ b/drivers/mfd/retu-mfd.c @@ -0,0 +1,264 @@ +/* + * Retu MFD driver + * + * Copyright (C) 2004, 2005 Nokia Corporation + * + * Based on code written by Juha Yrjölä, David Weinehall and Mikko Ylinen. + * Rewritten by Aaro Koskinen. + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define RETU_REG_ASICR 0x00 /* ASIC ID and revision */ +#define RETU_REG_ASICR_VILMA (1 << 7) /* Bit indicating Vilma */ +#define RETU_REG_IDR 0x01 /* Interrupt ID */ +#define RETU_REG_IMR 0x02 /* Interrupt mask */ + +/* Interrupt sources */ +#define RETU_INT_PWR 0 /* Power button */ + +struct retu_dev { + struct regmap *regmap; + struct device *dev; + struct mutex mutex; + struct regmap_irq_chip_data *irq_data; +}; + +static struct resource retu_pwrbutton_res[] = { + { + .name = "retu-pwrbutton", + .start = RETU_INT_PWR, + .end = RETU_INT_PWR, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell retu_devs[] = { + { + .name = "retu-wdt" + }, + { + .name = "retu-pwrbutton", + .resources = retu_pwrbutton_res, + .num_resources = ARRAY_SIZE(retu_pwrbutton_res), + } +}; + +static struct regmap_irq retu_irqs[] = { + [RETU_INT_PWR] = { + .mask = 1 << RETU_INT_PWR, + } +}; + +static struct regmap_irq_chip retu_irq_chip = { + .name = "RETU", + .irqs = retu_irqs, + .num_irqs = ARRAY_SIZE(retu_irqs), + .num_regs = 1, + .status_base = RETU_REG_IDR, + .mask_base = RETU_REG_IMR, + .ack_base = RETU_REG_IDR, +}; + +/* Retu device registered for the power off. */ +static struct retu_dev *retu_pm_power_off; + +int retu_read(struct retu_dev *rdev, u8 reg) +{ + int ret; + int value; + + mutex_lock(&rdev->mutex); + ret = regmap_read(rdev->regmap, reg, &value); + mutex_unlock(&rdev->mutex); + + return ret ? ret : value; +} +EXPORT_SYMBOL_GPL(retu_read); + +int retu_write(struct retu_dev *rdev, u8 reg, u16 data) +{ + int ret; + + mutex_lock(&rdev->mutex); + ret = regmap_write(rdev->regmap, reg, data); + mutex_unlock(&rdev->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(retu_write); + +static void retu_power_off(void) +{ + struct retu_dev *rdev = retu_pm_power_off; + int reg; + + mutex_lock(&retu_pm_power_off->mutex); + + /* Ignore power button state */ + regmap_read(rdev->regmap, RETU_REG_CC1, ®); + regmap_write(rdev->regmap, RETU_REG_CC1, reg | 2); + + /* Expire watchdog immediately */ + regmap_write(rdev->regmap, RETU_REG_WATCHDOG, 0); + + /* Wait for poweroff */ + for (;;) + cpu_relax(); + + mutex_unlock(&retu_pm_power_off->mutex); +} + +static int retu_regmap_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + int ret; + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + + BUG_ON(reg_size != 1 || val_size != 2); + + ret = i2c_smbus_read_word_data(i2c, *(u8 const *)reg); + if (ret < 0) + return ret; + + *(u16 *)val = ret; + return 0; +} + +static int retu_regmap_write(void *context, const void *data, size_t count) +{ + u8 reg; + u16 val; + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + + BUG_ON(count != sizeof(reg) + sizeof(val)); + memcpy(®, data, sizeof(reg)); + memcpy(&val, data + sizeof(reg), sizeof(val)); + return i2c_smbus_write_word_data(i2c, reg, val); +} + +static struct regmap_bus retu_bus = { + .read = retu_regmap_read, + .write = retu_regmap_write, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +static struct regmap_config retu_config = { + .reg_bits = 8, + .val_bits = 16, +}; + +static int __devinit retu_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct retu_dev *rdev; + int ret; + + rdev = devm_kzalloc(&i2c->dev, sizeof(*rdev), GFP_KERNEL); + if (rdev == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rdev); + rdev->dev = &i2c->dev; + mutex_init(&rdev->mutex); + rdev->regmap = devm_regmap_init(&i2c->dev, &retu_bus, &i2c->dev, + &retu_config); + if (IS_ERR(rdev->regmap)) + return PTR_ERR(rdev->regmap); + + ret = retu_read(rdev, RETU_REG_ASICR); + if (ret < 0) { + dev_err(rdev->dev, "could not read Retu revision: %d\n", ret); + return ret; + } + + dev_info(rdev->dev, "Retu%s v%d.%d found\n", + (ret & RETU_REG_ASICR_VILMA) ? " & Vilma" : "", + (ret >> 4) & 0x7, ret & 0xf); + + /* Mask all RETU interrupts. */ + ret = retu_write(rdev, RETU_REG_IMR, 0xffff); + if (ret < 0) + return ret; + + ret = regmap_add_irq_chip(rdev->regmap, i2c->irq, IRQF_ONESHOT, -1, + &retu_irq_chip, &rdev->irq_data); + if (ret < 0) + return ret; + + ret = mfd_add_devices(rdev->dev, -1, retu_devs, ARRAY_SIZE(retu_devs), + NULL, regmap_irq_chip_get_base(rdev->irq_data), + NULL); + if (ret < 0) { + regmap_del_irq_chip(i2c->irq, rdev->irq_data); + return ret; + } + + if (!pm_power_off) { + retu_pm_power_off = rdev; + pm_power_off = retu_power_off; + } + + return 0; +} + +static int __devexit retu_remove(struct i2c_client *i2c) +{ + struct retu_dev *rdev = i2c_get_clientdata(i2c); + + if (retu_pm_power_off == rdev) { + pm_power_off = NULL; + retu_pm_power_off = NULL; + } + mfd_remove_devices(rdev->dev); + regmap_del_irq_chip(i2c->irq, rdev->irq_data); + + return 0; +} + +static const struct i2c_device_id retu_id[] = { + { "retu-mfd", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, retu_id); + +static struct i2c_driver retu_driver = { + .driver = { + .name = "retu-mfd", + .owner = THIS_MODULE, + }, + .probe = retu_probe, + .remove = retu_remove, + .id_table = retu_id, +}; +module_i2c_driver(retu_driver); + +MODULE_DESCRIPTION("Retu MFD driver"); +MODULE_AUTHOR("Juha Yrjölä"); +MODULE_AUTHOR("David Weinehall"); +MODULE_AUTHOR("Mikko Ylinen"); +MODULE_AUTHOR("Aaro Koskinen "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/retu.h b/include/linux/mfd/retu.h new file mode 100644 index 000000000000..1e2715d5b836 --- /dev/null +++ b/include/linux/mfd/retu.h @@ -0,0 +1,22 @@ +/* + * Retu MFD driver interface + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + */ + +#ifndef __LINUX_MFD_RETU_H +#define __LINUX_MFD_RETU_H + +struct retu_dev; + +int retu_read(struct retu_dev *, u8); +int retu_write(struct retu_dev *, u8, u16); + +/* Registers */ +#define RETU_REG_WATCHDOG 0x17 /* Watchdog */ +#define RETU_REG_CC1 0x0d /* Common control register 1 */ +#define RETU_REG_STATUS 0x16 /* Status register */ + +#endif /* __LINUX_MFD_RETU_H */ From 24ae36f5c285d14f449dd19821aca1097a2a2313 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 Nov 2012 09:28:42 +0100 Subject: [PATCH 074/119] mfd: twl-core: Register twl4030-madc child only for twl4030 class twl4030-madc driver can only handle twl4030 class MADC. The newer revisions of twl does not have MADC, instead they have different IP called GPADC which is not backward compatible. Acked-by: Tero Kristo Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 4ae642320205..262466837689 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -646,7 +646,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, return PTR_ERR(child); } - if (IS_ENABLED(CONFIG_TWL4030_MADC) && pdata->madc) { + if (IS_ENABLED(CONFIG_TWL4030_MADC) && pdata->madc && + twl_class_is_4030()) { child = add_child(2, "twl4030_madc", pdata->madc, sizeof(*pdata->madc), true, irq_base + MADC_INTR_OFFSET, 0); From afc45898f62c7b139fca3cf0b72fb8ec476fee3f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 Nov 2012 09:28:43 +0100 Subject: [PATCH 075/119] mfd: twl-core: Support for proper PWM drivers The twl6030-pwm driver is going to be deleted since it was only able to control the Charging indicator LED on the twl6030 PMIC. The new set of drivers are going to provide support for both PWMs and PWM driven LED outputs on TWL4030 and TWL6030 PMICs. The twl-pwm driver will handle the PWMs (2 instance) while the twl-pwmled driver is to control the two LED instance on TWL4030 and to charging indicator LED (1 instance) on TWL6030. Acked-by: Tero Kristo Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 262466837689..1dfd58344425 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -671,8 +671,15 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, return PTR_ERR(child); } - if (IS_ENABLED(CONFIG_PWM_TWL6030) && twl_class_is_6030()) { - child = add_child(TWL6030_MODULE_ID1, "twl6030-pwm", NULL, 0, + if (IS_ENABLED(CONFIG_PWM_TWL)) { + child = add_child(SUB_CHIP_ID1, "twl-pwm", NULL, 0, + false, 0, 0); + if (IS_ERR(child)) + return PTR_ERR(child); + } + + if (IS_ENABLED(CONFIG_PWM_TWL_LED)) { + child = add_child(SUB_CHIP_ID1, "twl-pwmled", NULL, 0, false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); From 2473d25a2f61985f8980c7c3d41cb85da1abea0f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 Nov 2012 09:28:44 +0100 Subject: [PATCH 076/119] mfd: twl-core: Convert to use regmap for I/O Remove the custom code to do I/O and replace it with standard regmap calls. Reviewed-by: Mark Brown Acked-by: Tero Kristo Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 143 ++++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 65 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 1dfd58344425..5043f505a230 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -171,13 +172,7 @@ EXPORT_SYMBOL(twl_rev); /* Structure for each TWL4030/TWL6030 Slave */ struct twl_client { struct i2c_client *client; - u8 address; - - /* max numb of i2c_msg required is for read =2 */ - struct i2c_msg xfer_msg[2]; - - /* To lock access to xfer_msg */ - struct mutex xfer_lock; + struct regmap *regmap; }; static struct twl_client twl_modules[TWL_NUM_SLAVES]; @@ -225,6 +220,33 @@ static struct twl_mapping twl4030_map[TWL4030_MODULE_LAST + 1] = { { 3, TWL4030_BASEADD_SECURED_REG }, }; +static struct regmap_config twl4030_regmap_config[4] = { + { + /* Address 0x48 */ + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + }, + { + /* Address 0x49 */ + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + }, + { + /* Address 0x4a */ + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + }, + { + /* Address 0x4b */ + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + }, +}; + static struct twl_mapping twl6030_map[] = { /* * NOTE: don't change this table without updating the @@ -262,6 +284,27 @@ static struct twl_mapping twl6030_map[] = { { SUB_CHIP_ID1, TWL6025_BASEADD_CHARGER }, }; +static struct regmap_config twl6030_regmap_config[3] = { + { + /* Address 0x48 */ + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + }, + { + /* Address 0x49 */ + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + }, + { + /* Address 0x4a */ + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + }, +}; + /*----------------------------------------------------------------------*/ /* Exported Functions */ @@ -283,7 +326,6 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int ret; int sid; struct twl_client *twl; - struct i2c_msg *msg; if (unlikely(mod_no > TWL_MODULE_LAST)) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); @@ -301,32 +343,14 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) } twl = &twl_modules[sid]; - mutex_lock(&twl->xfer_lock); - /* - * [MSG1]: fill the register address data - * fill the data Tx buffer - */ - msg = &twl->xfer_msg[0]; - msg->addr = twl->address; - msg->len = num_bytes + 1; - msg->flags = 0; - msg->buf = value; - /* over write the first byte of buffer with the register address */ - *value = twl_map[mod_no].base + reg; - ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 1); - mutex_unlock(&twl->xfer_lock); + ret = regmap_bulk_write(twl->regmap, twl_map[mod_no].base + reg, + &value[1], num_bytes); - /* i2c_transfer returns number of messages transferred */ - if (ret != 1) { - pr_err("%s: i2c_write failed to transfer all messages\n", - DRIVER_NAME); - if (ret < 0) - return ret; - else - return -EIO; - } else { - return 0; - } + if (ret) + pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n", + DRIVER_NAME, mod_no, reg, num_bytes); + + return ret; } EXPORT_SYMBOL(twl_i2c_write); @@ -342,10 +366,8 @@ EXPORT_SYMBOL(twl_i2c_write); int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) { int ret; - u8 val; int sid; struct twl_client *twl; - struct i2c_msg *msg; if (unlikely(mod_no > TWL_MODULE_LAST)) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); @@ -363,34 +385,14 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) } twl = &twl_modules[sid]; - mutex_lock(&twl->xfer_lock); - /* [MSG1] fill the register address data */ - msg = &twl->xfer_msg[0]; - msg->addr = twl->address; - msg->len = 1; - msg->flags = 0; /* Read the register value */ - val = twl_map[mod_no].base + reg; - msg->buf = &val; - /* [MSG2] fill the data rx buffer */ - msg = &twl->xfer_msg[1]; - msg->addr = twl->address; - msg->flags = I2C_M_RD; /* Read the register value */ - msg->len = num_bytes; /* only n bytes */ - msg->buf = value; - ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 2); - mutex_unlock(&twl->xfer_lock); + ret = regmap_bulk_read(twl->regmap, twl_map[mod_no].base + reg, + value, num_bytes); - /* i2c_transfer returns number of messages transferred */ - if (ret != 2) { - pr_err("%s: i2c_read failed to transfer all messages\n", - DRIVER_NAME); - if (ret < 0) - return ret; - else - return -EIO; - } else { - return 0; - } + if (ret) + pr_err("%s: Read failed (mod %d, reg 0x%02x count %d)\n", + DRIVER_NAME, mod_no, reg, num_bytes); + + return ret; } EXPORT_SYMBOL(twl_i2c_read); @@ -1184,6 +1186,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) struct twl4030_platform_data *pdata = client->dev.platform_data; struct device_node *node = client->dev.of_node; struct platform_device *pdev; + struct regmap_config *twl_regmap_config; int irq_base = 0; int status; unsigned i, num_slaves; @@ -1237,22 +1240,23 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) if ((id->driver_data) & TWL6030_CLASS) { twl_id = TWL6030_CLASS_ID; twl_map = &twl6030_map[0]; + twl_regmap_config = twl6030_regmap_config; num_slaves = TWL_NUM_SLAVES - 1; } else { twl_id = TWL4030_CLASS_ID; twl_map = &twl4030_map[0]; + twl_regmap_config = twl4030_regmap_config; num_slaves = TWL_NUM_SLAVES; } for (i = 0; i < num_slaves; i++) { struct twl_client *twl = &twl_modules[i]; - twl->address = client->addr + i; if (i == 0) { twl->client = client; } else { twl->client = i2c_new_dummy(client->adapter, - twl->address); + client->addr + i); if (!twl->client) { dev_err(&client->dev, "can't attach client %d\n", i); @@ -1260,7 +1264,16 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) goto fail; } } - mutex_init(&twl->xfer_lock); + + twl->regmap = devm_regmap_init_i2c(twl->client, + &twl_regmap_config[i]); + if (IS_ERR(twl->regmap)) { + status = PTR_ERR(twl->regmap); + dev_err(&client->dev, + "Failed to allocate regmap %d, err: %d\n", i, + status); + goto fail; + } } inuse = true; From 14591d888e35c12b15eccf54e490c7769eb6d6d2 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 Nov 2012 09:28:45 +0100 Subject: [PATCH 077/119] mfd/rtc/gpio: twl: No need to allocate bigger buffer for write Since the twl-core has been converted to use regmap it is no longer needed to allocate bigger buffer for data when writing to twl. CC: Grant Likely CC: Alessandro Zummo Acked-by: Linus Walleij Acked-by: Tero Kristo Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/gpio/gpio-twl4030.c | 12 ++++++------ drivers/mfd/twl-core.c | 9 ++------- drivers/mfd/twl4030-irq.c | 6 +++--- drivers/mfd/twl6030-irq.c | 4 ++-- drivers/rtc/rtc-twl.c | 32 ++++++++++++++++---------------- 5 files changed, 29 insertions(+), 34 deletions(-) diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index c5f8ca233e1f..88829c3337bf 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -354,13 +354,13 @@ static struct gpio_chip twl_gpiochip = { static int __devinit gpio_twl4030_pulls(u32 ups, u32 downs) { - u8 message[6]; + u8 message[5]; unsigned i, gpio_bit; /* For most pins, a pulldown was enabled by default. * We should have data that's specific to this board. */ - for (gpio_bit = 1, i = 1; i < 6; i++) { + for (gpio_bit = 1, i = 0; i < 5; i++) { u8 bit_mask; unsigned j; @@ -379,16 +379,16 @@ static int __devinit gpio_twl4030_pulls(u32 ups, u32 downs) static int __devinit gpio_twl4030_debounce(u32 debounce, u8 mmc_cd) { - u8 message[4]; + u8 message[3]; /* 30 msec of debouncing is always used for MMC card detect, * and is optional for everything else. */ - message[1] = (debounce & 0xff) | (mmc_cd & 0x03); + message[0] = (debounce & 0xff) | (mmc_cd & 0x03); debounce >>= 8; - message[2] = (debounce & 0xff); + message[1] = (debounce & 0xff); debounce >>= 8; - message[3] = (debounce & 0x03); + message[2] = (debounce & 0x03); return twl_i2c_write(TWL4030_MODULE_GPIO, message, REG_GPIO_DEBEN1, 3); diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 5043f505a230..a55ab15ecf1a 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -344,7 +344,7 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) twl = &twl_modules[sid]; ret = regmap_bulk_write(twl->regmap, twl_map[mod_no].base + reg, - &value[1], num_bytes); + value, num_bytes); if (ret) pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n", @@ -406,12 +406,7 @@ EXPORT_SYMBOL(twl_i2c_read); */ int twl_i2c_write_u8(u8 mod_no, u8 value, u8 reg) { - - /* 2 bytes offset 1 contains the data offset 0 is used by i2c_write */ - u8 temp_buffer[2] = { 0 }; - /* offset 1 contains the data */ - temp_buffer[1] = value; - return twl_i2c_write(mod_no, temp_buffer, reg, 1); + return twl_i2c_write(mod_no, &value, reg, 1); } EXPORT_SYMBOL(twl_i2c_write_u8); diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index ad733d76207a..e900402633f5 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -501,7 +501,7 @@ static void twl4030_sih_bus_sync_unlock(struct irq_data *data) } imr; /* byte[0] gets overwritten as we write ... */ - imr.word = cpu_to_le32(agent->imr << 8); + imr.word = cpu_to_le32(agent->imr); agent->imr_change_pending = false; /* write the whole mask ... simpler than subsetting it */ @@ -526,7 +526,7 @@ static void twl4030_sih_bus_sync_unlock(struct irq_data *data) * any processor on the other IRQ line, EDR registers are * shared. */ - status = twl_i2c_read(sih->module, bytes + 1, + status = twl_i2c_read(sih->module, bytes, sih->edr_offset, sih->bytes_edr); if (status) { pr_err("twl4030: %s, %s --> %d\n", __func__, @@ -538,7 +538,7 @@ static void twl4030_sih_bus_sync_unlock(struct irq_data *data) while (edge_change) { int i = fls(edge_change) - 1; struct irq_data *idata; - int byte = 1 + (i >> 2); + int byte = i >> 2; int off = (i & 0x3) * 2; unsigned int type; diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index b76902f1e44a..277a8dba42d5 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -355,7 +355,7 @@ int twl6030_init_irq(struct device *dev, int irq_num) static struct irq_chip twl6030_irq_chip; int status = 0; int i; - u8 mask[4]; + u8 mask[3]; nr_irqs = TWL6030_NR_IRQS; @@ -370,9 +370,9 @@ int twl6030_init_irq(struct device *dev, int irq_num) irq_end = irq_base + nr_irqs; + mask[0] = 0xFF; mask[1] = 0xFF; mask[2] = 0xFF; - mask[3] = 0xFF; /* mask all int lines */ twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_LINE_A, 3); diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index 9277d945bf48..8b7464c8b5cf 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -233,7 +233,7 @@ static int twl_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) */ static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm) { - unsigned char rtc_data[ALL_TIME_REGS + 1]; + unsigned char rtc_data[ALL_TIME_REGS]; int ret; u8 save_control; u8 rtc_control; @@ -300,15 +300,15 @@ static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm) static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm) { unsigned char save_control; - unsigned char rtc_data[ALL_TIME_REGS + 1]; + unsigned char rtc_data[ALL_TIME_REGS]; int ret; - rtc_data[1] = bin2bcd(tm->tm_sec); - rtc_data[2] = bin2bcd(tm->tm_min); - rtc_data[3] = bin2bcd(tm->tm_hour); - rtc_data[4] = bin2bcd(tm->tm_mday); - rtc_data[5] = bin2bcd(tm->tm_mon + 1); - rtc_data[6] = bin2bcd(tm->tm_year - 100); + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour); + rtc_data[3] = bin2bcd(tm->tm_mday); + rtc_data[4] = bin2bcd(tm->tm_mon + 1); + rtc_data[5] = bin2bcd(tm->tm_year - 100); /* Stop RTC while updating the TC registers */ ret = twl_rtc_read_u8(&save_control, REG_RTC_CTRL_REG); @@ -341,7 +341,7 @@ out: */ static int twl_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) { - unsigned char rtc_data[ALL_TIME_REGS + 1]; + unsigned char rtc_data[ALL_TIME_REGS]; int ret; ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data, @@ -368,19 +368,19 @@ static int twl_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) static int twl_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) { - unsigned char alarm_data[ALL_TIME_REGS + 1]; + unsigned char alarm_data[ALL_TIME_REGS]; int ret; ret = twl_rtc_alarm_irq_enable(dev, 0); if (ret) goto out; - alarm_data[1] = bin2bcd(alm->time.tm_sec); - alarm_data[2] = bin2bcd(alm->time.tm_min); - alarm_data[3] = bin2bcd(alm->time.tm_hour); - alarm_data[4] = bin2bcd(alm->time.tm_mday); - alarm_data[5] = bin2bcd(alm->time.tm_mon + 1); - alarm_data[6] = bin2bcd(alm->time.tm_year - 100); + alarm_data[0] = bin2bcd(alm->time.tm_sec); + alarm_data[1] = bin2bcd(alm->time.tm_min); + alarm_data[2] = bin2bcd(alm->time.tm_hour); + alarm_data[3] = bin2bcd(alm->time.tm_mday); + alarm_data[4] = bin2bcd(alm->time.tm_mon + 1); + alarm_data[5] = bin2bcd(alm->time.tm_year - 100); /* update all the alarm registers in one shot */ ret = twl_i2c_write(TWL_MODULE_RTC, alarm_data, From 2d86ad37ece5fd064aa7c4fba5dd4378679c2cbf Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 Nov 2012 09:28:46 +0100 Subject: [PATCH 078/119] mfd: twl-core: Clean up and correct child registration Make the twl child registration calls a bit more uniform by always using the SUB_CHIP_ID* define instead of the mixed use of the define and magic number. At the same time correct the following devices so they are registered for the correct parent device (i2c slave): twl4030_wdt is accessible on 0x4b address and not 0x48 twl4030_pwrbutton is accessible on 0x4b address and not 0x49 twl4030-audio is on 0x49 all the time Acked-by: Tero Kristo Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index a55ab15ecf1a..d666c9d454cb 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -645,7 +645,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, if (IS_ENABLED(CONFIG_TWL4030_MADC) && pdata->madc && twl_class_is_4030()) { - child = add_child(2, "twl4030_madc", + child = add_child(SUB_CHIP_ID2, "twl4030_madc", pdata->madc, sizeof(*pdata->madc), true, irq_base + MADC_INTR_OFFSET, 0); if (IS_ERR(child)) @@ -661,8 +661,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, * HW security concerns, and "least privilege". */ sub_chip_id = twl_map[TWL_MODULE_RTC].sid; - child = add_child(sub_chip_id, "twl_rtc", - NULL, 0, + child = add_child(sub_chip_id, "twl_rtc", NULL, 0, true, irq_base + RTC_INTR_OFFSET, 0); if (IS_ERR(child)) return PTR_ERR(child); @@ -728,9 +727,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, } - child = add_child(0, "twl4030_usb", - pdata->usb, sizeof(*pdata->usb), - true, + child = add_child(SUB_CHIP_ID0, "twl4030_usb", + pdata->usb, sizeof(*pdata->usb), true, /* irq0 = USB_PRES, irq1 = USB */ irq_base + USB_PRES_INTR_OFFSET, irq_base + USB_INTR_OFFSET); @@ -778,9 +776,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, pdata->usb->features = features; - child = add_child(0, "twl6030_usb", - pdata->usb, sizeof(*pdata->usb), - true, + child = add_child(SUB_CHIP_ID0, "twl6030_usb", + pdata->usb, sizeof(*pdata->usb), true, /* irq1 = VBUS_PRES, irq0 = USB ID */ irq_base + USBOTG_INTR_OFFSET, irq_base + USB_PRES_INTR_OFFSET); @@ -804,22 +801,22 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, } if (IS_ENABLED(CONFIG_TWL4030_WATCHDOG) && twl_class_is_4030()) { - child = add_child(0, "twl4030_wdt", NULL, 0, false, 0, 0); + child = add_child(SUB_CHIP_ID3, "twl4030_wdt", NULL, 0, + false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } if (IS_ENABLED(CONFIG_INPUT_TWL4030_PWRBUTTON) && twl_class_is_4030()) { - child = add_child(1, "twl4030_pwrbutton", - NULL, 0, true, irq_base + 8 + 0, 0); + child = add_child(SUB_CHIP_ID3, "twl4030_pwrbutton", NULL, 0, + true, irq_base + 8 + 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } if (IS_ENABLED(CONFIG_MFD_TWL4030_AUDIO) && pdata->audio && twl_class_is_4030()) { - sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; - child = add_child(sub_chip_id, "twl4030-audio", + child = add_child(SUB_CHIP_ID1, "twl4030-audio", pdata->audio, sizeof(*pdata->audio), false, 0, 0); if (IS_ERR(child)) @@ -1059,7 +1056,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, if (IS_ENABLED(CONFIG_CHARGER_TWL4030) && pdata->bci && !(features & (TPS_SUBSET | TWL5031))) { - child = add_child(3, "twl4030_bci", + child = add_child(SUB_CHIP_ID3, "twl4030_bci", pdata->bci, sizeof(*pdata->bci), false, /* irq0 = CHG_PRES, irq1 = BCI */ irq_base + BCI_PRES_INTR_OFFSET, From 0e8f1398a388bbaa5ca965711b9ed5ac4794332d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 Nov 2012 09:28:47 +0100 Subject: [PATCH 079/119] mfd: twl: Remove unused TWL_MODULE definitions AUDIO and MADC only available on twl4030 series and the TWL_MODULE_* mapping is not needed. Acked-by: Tero Kristo Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- include/linux/i2c/twl.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 9a5e28462324..7278c72479d1 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -73,9 +73,7 @@ #define TWL4030_MODULE_SECURED_REG 0x17 #define TWL_MODULE_USB TWL4030_MODULE_USB -#define TWL_MODULE_AUDIO_VOICE TWL4030_MODULE_AUDIO_VOICE #define TWL_MODULE_PIH TWL4030_MODULE_PIH -#define TWL_MODULE_MADC TWL4030_MODULE_MADC #define TWL_MODULE_MAIN_CHARGE TWL4030_MODULE_MAIN_CHARGE #define TWL_MODULE_PM_MASTER TWL4030_MODULE_PM_MASTER #define TWL_MODULE_PM_RECEIVER TWL4030_MODULE_PM_RECEIVER From da059ecfc9f9d98556607c6d6db065aa3b7f162d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 Nov 2012 09:28:48 +0100 Subject: [PATCH 080/119] mfd: twl: Convert module id definitions to enums Use enum list for the module definitions (TWL4030_MODULE_*) which will ease up future work with the IDs. At the same time group the IDs in block of five so it is easier to find the ID we are looking for (to count the number they stand for). At the same time define TWL_MODULE_LED so client drivers can switch to use it as soon as it is possible. Acked-by: Tero Kristo Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 9 +++---- include/linux/i2c/twl.h | 57 +++++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index d666c9d454cb..bb33b52cfa57 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -66,9 +66,6 @@ /* Triton Core internal information (BEGIN) */ -/* Last - for index max*/ -#define TWL4030_MODULE_LAST TWL4030_MODULE_SECURED_REG - #define TWL_NUM_SLAVES 4 #define SUB_CHIP_ID0 0 @@ -184,7 +181,7 @@ struct twl_mapping { }; static struct twl_mapping *twl_map; -static struct twl_mapping twl4030_map[TWL4030_MODULE_LAST + 1] = { +static struct twl_mapping twl4030_map[] = { /* * NOTE: don't change this table without updating the * defines for TWL4030_MODULE_* @@ -327,7 +324,7 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int sid; struct twl_client *twl; - if (unlikely(mod_no > TWL_MODULE_LAST)) { + if (unlikely(mod_no >= TWL_MODULE_LAST)) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } @@ -369,7 +366,7 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int sid; struct twl_client *twl; - if (unlikely(mod_no > TWL_MODULE_LAST)) { + if (unlikely(mod_no >= TWL_MODULE_LAST)) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 7278c72479d1..b1c44cccef31 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -39,39 +39,39 @@ * address each module uses within a given i2c slave. */ -/* Slave 0 (i2c address 0x48) */ -#define TWL4030_MODULE_USB 0x00 +enum twl4030_module_ids { + TWL4030_MODULE_USB = 0, /* Slave 0 (i2c address 0x48) */ + TWL4030_MODULE_AUDIO_VOICE, /* Slave 1 (i2c address 0x49) */ + TWL4030_MODULE_GPIO, + TWL4030_MODULE_INTBR, + TWL4030_MODULE_PIH, -/* Slave 1 (i2c address 0x49) */ -#define TWL4030_MODULE_AUDIO_VOICE 0x01 -#define TWL4030_MODULE_GPIO 0x02 -#define TWL4030_MODULE_INTBR 0x03 -#define TWL4030_MODULE_PIH 0x04 -#define TWL4030_MODULE_TEST 0x05 + TWL4030_MODULE_TEST, + TWL4030_MODULE_KEYPAD, /* Slave 2 (i2c address 0x4a) */ + TWL4030_MODULE_MADC, + TWL4030_MODULE_INTERRUPTS, + TWL4030_MODULE_LED, -/* Slave 2 (i2c address 0x4a) */ -#define TWL4030_MODULE_KEYPAD 0x06 -#define TWL4030_MODULE_MADC 0x07 -#define TWL4030_MODULE_INTERRUPTS 0x08 -#define TWL4030_MODULE_LED 0x09 -#define TWL4030_MODULE_MAIN_CHARGE 0x0A -#define TWL4030_MODULE_PRECHARGE 0x0B -#define TWL4030_MODULE_PWM0 0x0C -#define TWL4030_MODULE_PWM1 0x0D -#define TWL4030_MODULE_PWMA 0x0E -#define TWL4030_MODULE_PWMB 0x0F + TWL4030_MODULE_MAIN_CHARGE, + TWL4030_MODULE_PRECHARGE, + TWL4030_MODULE_PWM0, + TWL4030_MODULE_PWM1, + TWL4030_MODULE_PWMA, -#define TWL5031_MODULE_ACCESSORY 0x10 -#define TWL5031_MODULE_INTERRUPTS 0x11 + TWL4030_MODULE_PWMB, + TWL5031_MODULE_ACCESSORY, + TWL5031_MODULE_INTERRUPTS, + TWL4030_MODULE_BACKUP, /* Slave 3 (i2c address 0x4b) */ + TWL4030_MODULE_INT, -/* Slave 3 (i2c address 0x4b) */ -#define TWL4030_MODULE_BACKUP 0x12 -#define TWL4030_MODULE_INT 0x13 -#define TWL4030_MODULE_PM_MASTER 0x14 -#define TWL4030_MODULE_PM_RECEIVER 0x15 -#define TWL4030_MODULE_RTC 0x16 -#define TWL4030_MODULE_SECURED_REG 0x17 + TWL4030_MODULE_PM_MASTER, + TWL4030_MODULE_PM_RECEIVER, + TWL4030_MODULE_RTC, + TWL4030_MODULE_SECURED_REG, + TWL4030_MODULE_LAST, +}; +/* Similar functionalities implemented in TWL4030/6030 */ #define TWL_MODULE_USB TWL4030_MODULE_USB #define TWL_MODULE_PIH TWL4030_MODULE_PIH #define TWL_MODULE_MAIN_CHARGE TWL4030_MODULE_MAIN_CHARGE @@ -79,6 +79,7 @@ #define TWL_MODULE_PM_RECEIVER TWL4030_MODULE_PM_RECEIVER #define TWL_MODULE_RTC TWL4030_MODULE_RTC #define TWL_MODULE_PWM TWL4030_MODULE_PWM0 +#define TWL_MODULE_LED TWL4030_MODULE_LED #define TWL6030_MODULE_ID0 0x0D #define TWL6030_MODULE_ID1 0x0E From 04575d5a3dc15d762c7428d2745d30b2d448e443 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 Nov 2012 09:28:49 +0100 Subject: [PATCH 081/119] mfd: twl: Use decimal numbers for TWL6030_MODULE_IDs It is easier fro humans to understand decimal numbers than hexadecimals when they are used as indexes. Acked-by: Tero Kristo Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- include/linux/i2c/twl.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index b1c44cccef31..1ff54b114efc 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -81,9 +81,9 @@ enum twl4030_module_ids { #define TWL_MODULE_PWM TWL4030_MODULE_PWM0 #define TWL_MODULE_LED TWL4030_MODULE_LED -#define TWL6030_MODULE_ID0 0x0D -#define TWL6030_MODULE_ID1 0x0E -#define TWL6030_MODULE_ID2 0x0F +#define TWL6030_MODULE_ID0 13 +#define TWL6030_MODULE_ID1 14 +#define TWL6030_MODULE_ID2 15 #define GPIO_INTR_OFFSET 0 #define KEYPAD_INTR_OFFSET 1 From 6691ccd0565954f2275fb10cb5e4f0cef4a9ff64 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 Nov 2012 09:28:50 +0100 Subject: [PATCH 082/119] mfd: twl-core: re-group the twl_mapping table for easier reading Group the twl_mapping table in 5 lines chunks so it is more easier to find the row we are looking for (if we need to). Acked-by: Tero Kristo Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index bb33b52cfa57..f857097aca5c 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -189,28 +189,29 @@ static struct twl_mapping twl4030_map[] = { */ { 0, TWL4030_BASEADD_USB }, - { 1, TWL4030_BASEADD_AUDIO_VOICE }, { 1, TWL4030_BASEADD_GPIO }, { 1, TWL4030_BASEADD_INTBR }, { 1, TWL4030_BASEADD_PIH }, - { 1, TWL4030_BASEADD_TEST }, + { 1, TWL4030_BASEADD_TEST }, { 2, TWL4030_BASEADD_KEYPAD }, { 2, TWL4030_BASEADD_MADC }, { 2, TWL4030_BASEADD_INTERRUPTS }, { 2, TWL4030_BASEADD_LED }, + { 2, TWL4030_BASEADD_MAIN_CHARGE }, { 2, TWL4030_BASEADD_PRECHARGE }, { 2, TWL4030_BASEADD_PWM0 }, { 2, TWL4030_BASEADD_PWM1 }, { 2, TWL4030_BASEADD_PWMA }, + { 2, TWL4030_BASEADD_PWMB }, { 2, TWL5031_BASEADD_ACCESSORY }, { 2, TWL5031_BASEADD_INTERRUPTS }, - { 3, TWL4030_BASEADD_BACKUP }, { 3, TWL4030_BASEADD_INT }, + { 3, TWL4030_BASEADD_PM_MASTER }, { 3, TWL4030_BASEADD_PM_RECEIVER }, { 3, TWL4030_BASEADD_RTC }, @@ -273,9 +274,9 @@ static struct twl_mapping twl6030_map[] = { { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, + { SUB_CHIP_ID0, TWL6030_BASEADD_PM_MASTER }, { SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_MISC }, - { SUB_CHIP_ID0, TWL6030_BASEADD_RTC }, { SUB_CHIP_ID0, TWL6030_BASEADD_MEM }, { SUB_CHIP_ID1, TWL6025_BASEADD_CHARGER }, From e45342f678d0f08d284358504f995f495fc6f4f1 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 Nov 2012 09:28:51 +0100 Subject: [PATCH 083/119] mfd: twl4030-madc: Change TWL4030_MODULE_* ids to TWL_MODULE_* To facilitate upcoming cleanup in twl stack. No functional changes. Acked-by: Tero Kristo Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-madc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c index 456ecb5ac4fe..2baabaa14244 100644 --- a/drivers/mfd/twl4030-madc.c +++ b/drivers/mfd/twl4030-madc.c @@ -173,7 +173,7 @@ static int twl4030battery_temperature(int raw_volt) volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R; /* Getting and calculating the supply current in micro ampers */ - ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, REG_BCICTL2); if (ret < 0) return ret; @@ -196,7 +196,7 @@ static int twl4030battery_current(int raw_volt) int ret; u8 val; - ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, TWL4030_BCI_BCICTL1); if (ret) return ret; @@ -635,7 +635,7 @@ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, int ret; u8 regval; - ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, ®val, TWL4030_BCI_BCICTL1); if (ret) { dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X", @@ -646,7 +646,7 @@ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, regval |= chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; else regval &= chan ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN; - ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, regval, TWL4030_BCI_BCICTL1); if (ret) { dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n", @@ -668,7 +668,7 @@ static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on) u8 regval; int ret; - ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, ®val, TWL4030_MADC_CTRL1); if (ret) { dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n", @@ -725,7 +725,7 @@ static int __devinit twl4030_madc_probe(struct platform_device *pdev) if (ret < 0) goto err_current_generator; - ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, ®val, TWL4030_BCI_BCICTL1); if (ret) { dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n", @@ -733,7 +733,7 @@ static int __devinit twl4030_madc_probe(struct platform_device *pdev) goto err_i2c; } regval |= TWL4030_BCI_MESBAT; - ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, regval, TWL4030_BCI_BCICTL1); if (ret) { dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n", From 4850f1247aa495a664133fb3f6b8139b89b2c6db Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 Nov 2012 09:28:52 +0100 Subject: [PATCH 084/119] mfd: twl4030-power: Change TWL4030_MODULE_* ids to TWL_MODULE_* To facilitate upcoming cleanup in twl stack. No functional changes. Acked-by: Tero Kristo Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-power.c | 124 +++++++++++++++--------------------- 1 file changed, 51 insertions(+), 73 deletions(-) diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c index 79ca33dfacca..4fda77c74552 100644 --- a/drivers/mfd/twl4030-power.c +++ b/drivers/mfd/twl4030-power.c @@ -128,12 +128,10 @@ static int __devinit twl4030_write_script_byte(u8 address, u8 byte) { int err; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address, - R_MEMORY_ADDRESS); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, address, R_MEMORY_ADDRESS); if (err) goto out; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, byte, - R_MEMORY_DATA); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, byte, R_MEMORY_DATA); out: return err; } @@ -189,19 +187,16 @@ static int __devinit twl4030_config_wakeup3_sequence(u8 address) u8 data; /* Set SLEEP to ACTIVE SEQ address for P3 */ - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address, - R_SEQ_ADD_S2A3); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, address, R_SEQ_ADD_S2A3); if (err) goto out; /* P3 LVL_WAKEUP should be on LEVEL */ - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data, - R_P3_SW_EVENTS); + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data, R_P3_SW_EVENTS); if (err) goto out; data |= LVL_WAKEUP; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data, - R_P3_SW_EVENTS); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, R_P3_SW_EVENTS); out: if (err) pr_err("TWL4030 wakeup sequence for P3 config error\n"); @@ -214,43 +209,38 @@ static int __devinit twl4030_config_wakeup12_sequence(u8 address) u8 data; /* Set SLEEP to ACTIVE SEQ address for P1 and P2 */ - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address, - R_SEQ_ADD_S2A12); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, address, R_SEQ_ADD_S2A12); if (err) goto out; /* P1/P2 LVL_WAKEUP should be on LEVEL */ - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data, - R_P1_SW_EVENTS); + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data, R_P1_SW_EVENTS); if (err) goto out; data |= LVL_WAKEUP; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data, - R_P1_SW_EVENTS); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, R_P1_SW_EVENTS); if (err) goto out; - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data, - R_P2_SW_EVENTS); + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data, R_P2_SW_EVENTS); if (err) goto out; data |= LVL_WAKEUP; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data, - R_P2_SW_EVENTS); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, R_P2_SW_EVENTS); if (err) goto out; if (machine_is_omap_3430sdp() || machine_is_omap_ldp()) { /* Disabling AC charger effect on sleep-active transitions */ - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data, - R_CFG_P1_TRANSITION); + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data, + R_CFG_P1_TRANSITION); if (err) goto out; data &= ~(1<<1); - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data , - R_CFG_P1_TRANSITION); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, + R_CFG_P1_TRANSITION); if (err) goto out; } @@ -267,8 +257,7 @@ static int __devinit twl4030_config_sleep_sequence(u8 address) int err; /* Set ACTIVE to SLEEP SEQ address in T2 memory*/ - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address, - R_SEQ_ADD_A2S); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, address, R_SEQ_ADD_A2S); if (err) pr_err("TWL4030 sleep sequence config error\n"); @@ -282,42 +271,35 @@ static int __devinit twl4030_config_warmreset_sequence(u8 address) u8 rd_data; /* Set WARM RESET SEQ address for P1 */ - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address, - R_SEQ_ADD_WARM); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, address, R_SEQ_ADD_WARM); if (err) goto out; /* P1/P2/P3 enable WARMRESET */ - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data, - R_P1_SW_EVENTS); + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &rd_data, R_P1_SW_EVENTS); if (err) goto out; rd_data |= ENABLE_WARMRESET; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data, - R_P1_SW_EVENTS); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, rd_data, R_P1_SW_EVENTS); if (err) goto out; - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data, - R_P2_SW_EVENTS); + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &rd_data, R_P2_SW_EVENTS); if (err) goto out; rd_data |= ENABLE_WARMRESET; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data, - R_P2_SW_EVENTS); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, rd_data, R_P2_SW_EVENTS); if (err) goto out; - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data, - R_P3_SW_EVENTS); + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &rd_data, R_P3_SW_EVENTS); if (err) goto out; rd_data |= ENABLE_WARMRESET; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data, - R_P3_SW_EVENTS); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, rd_data, R_P3_SW_EVENTS); out: if (err) pr_err("TWL4030 warmreset seq config error\n"); @@ -341,7 +323,7 @@ static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfi rconfig_addr = res_config_addrs[rconfig->resource]; /* Set resource group */ - err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &grp, + err = twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &grp, rconfig_addr + DEV_GRP_OFFSET); if (err) { pr_err("TWL4030 Resource %d group could not be read\n", @@ -352,7 +334,7 @@ static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfi if (rconfig->devgroup != TWL4030_RESCONFIG_UNDEF) { grp &= ~DEV_GRP_MASK; grp |= rconfig->devgroup << DEV_GRP_SHIFT; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + err = twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, grp, rconfig_addr + DEV_GRP_OFFSET); if (err < 0) { pr_err("TWL4030 failed to program devgroup\n"); @@ -361,7 +343,7 @@ static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfi } /* Set resource types */ - err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &type, + err = twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &type, rconfig_addr + TYPE_OFFSET); if (err < 0) { pr_err("TWL4030 Resource %d type could not be read\n", @@ -379,7 +361,7 @@ static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfi type |= rconfig->type2 << TYPE2_SHIFT; } - err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + err = twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, type, rconfig_addr + TYPE_OFFSET); if (err < 0) { pr_err("TWL4030 failed to program resource type\n"); @@ -387,7 +369,7 @@ static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfi } /* Set remap states */ - err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &remap, + err = twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &remap, rconfig_addr + REMAP_OFFSET); if (err < 0) { pr_err("TWL4030 Resource %d remap could not be read\n", @@ -405,7 +387,7 @@ static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfi remap |= rconfig->remap_sleep << SLEEP_STATE_SHIFT; } - err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + err = twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, remap, rconfig_addr + REMAP_OFFSET); if (err < 0) { @@ -463,49 +445,47 @@ int twl4030_remove_script(u8 flags) { int err = 0; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, - TWL4030_PM_MASTER_KEY_CFG1, - TWL4030_PM_MASTER_PROTECT_KEY); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1, + TWL4030_PM_MASTER_PROTECT_KEY); if (err) { pr_err("twl4030: unable to unlock PROTECT_KEY\n"); return err; } - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, - TWL4030_PM_MASTER_KEY_CFG2, - TWL4030_PM_MASTER_PROTECT_KEY); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2, + TWL4030_PM_MASTER_PROTECT_KEY); if (err) { pr_err("twl4030: unable to unlock PROTECT_KEY\n"); return err; } if (flags & TWL4030_WRST_SCRIPT) { - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT, - R_SEQ_ADD_WARM); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, END_OF_SCRIPT, + R_SEQ_ADD_WARM); if (err) return err; } if (flags & TWL4030_WAKEUP12_SCRIPT) { - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT, - R_SEQ_ADD_S2A12); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, END_OF_SCRIPT, + R_SEQ_ADD_S2A12); if (err) return err; } if (flags & TWL4030_WAKEUP3_SCRIPT) { - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT, - R_SEQ_ADD_S2A3); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, END_OF_SCRIPT, + R_SEQ_ADD_S2A3); if (err) return err; } if (flags & TWL4030_SLEEP_SCRIPT) { - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT, - R_SEQ_ADD_A2S); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, END_OF_SCRIPT, + R_SEQ_ADD_A2S); if (err) return err; } - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, - TWL4030_PM_MASTER_PROTECT_KEY); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0, + TWL4030_PM_MASTER_PROTECT_KEY); if (err) pr_err("TWL4030 Unable to relock registers\n"); @@ -521,7 +501,7 @@ void twl4030_power_off(void) { int err; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, PWR_DEVOFF, + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, PWR_DEVOFF, TWL4030_PM_MASTER_P1_SW_EVENTS); if (err) pr_err("TWL4030 Unable to power off\n"); @@ -534,15 +514,13 @@ void __devinit twl4030_power_init(struct twl4030_power_data *twl4030_scripts) struct twl4030_resconfig *resconfig; u8 val, address = twl4030_start_script_address; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, - TWL4030_PM_MASTER_KEY_CFG1, - TWL4030_PM_MASTER_PROTECT_KEY); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1, + TWL4030_PM_MASTER_PROTECT_KEY); if (err) goto unlock; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, - TWL4030_PM_MASTER_KEY_CFG2, - TWL4030_PM_MASTER_PROTECT_KEY); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2, + TWL4030_PM_MASTER_PROTECT_KEY); if (err) goto unlock; @@ -567,14 +545,14 @@ void __devinit twl4030_power_init(struct twl4030_power_data *twl4030_scripts) /* Board has to be wired properly to use this feature */ if (twl4030_scripts->use_poweroff && !pm_power_off) { /* Default for SEQ_OFFSYNC is set, lets ensure this */ - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &val, + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &val, TWL4030_PM_MASTER_CFG_P123_TRANSITION); if (err) { pr_warning("TWL4030 Unable to read registers\n"); } else if (!(val & SEQ_OFFSYNC)) { val |= SEQ_OFFSYNC; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, val, + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, val, TWL4030_PM_MASTER_CFG_P123_TRANSITION); if (err) { pr_err("TWL4030 Unable to setup SEQ_OFFSYNC\n"); @@ -586,8 +564,8 @@ void __devinit twl4030_power_init(struct twl4030_power_data *twl4030_scripts) } relock: - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, - TWL4030_PM_MASTER_PROTECT_KEY); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0, + TWL4030_PM_MASTER_PROTECT_KEY); if (err) pr_err("TWL4030 Unable to relock registers\n"); return; From 6fbc6420b730f32d0088103da0877981da67df39 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 Nov 2012 09:28:53 +0100 Subject: [PATCH 085/119] mfd: twl4030-irq: Change TWL4030_MODULE_* ids to TWL_MODULE_* To facilitate upcoming cleanup in twl stack. No functional changes. Acked-by: Tero Kristo Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-irq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index e900402633f5..518da0c73907 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -295,8 +295,8 @@ static irqreturn_t handle_twl4030_pih(int irq, void *devid) irqreturn_t ret; u8 pih_isr; - ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr, - REG_PIH_ISR_P1); + ret = twl_i2c_read_u8(TWL_MODULE_PIH, &pih_isr, + REG_PIH_ISR_P1); if (ret) { pr_warning("twl4030: I2C error %d reading PIH ISR\n", ret); return IRQ_NONE; From d640e757949e2991215838c0edbfd6afc37e5b06 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 Nov 2012 09:28:54 +0100 Subject: [PATCH 086/119] mfd: twl-core: Change TWL4030_MODULE_* ids to TWL_MODULE_* To facilitate upcoming cleanup in twl stack. No functional changes. Acked-by: Tero Kristo Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index f857097aca5c..43c7b4c92940 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -1077,8 +1077,8 @@ static inline int __init protect_pm_master(void) { int e = 0; - e = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, - TWL4030_PM_MASTER_PROTECT_KEY); + e = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0, + TWL4030_PM_MASTER_PROTECT_KEY); return e; } @@ -1086,12 +1086,10 @@ static inline int __init unprotect_pm_master(void) { int e = 0; - e |= twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, - TWL4030_PM_MASTER_KEY_CFG1, - TWL4030_PM_MASTER_PROTECT_KEY); - e |= twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, - TWL4030_PM_MASTER_KEY_CFG2, - TWL4030_PM_MASTER_PROTECT_KEY); + e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1, + TWL4030_PM_MASTER_PROTECT_KEY); + e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2, + TWL4030_PM_MASTER_PROTECT_KEY); return e; } From e294bc91760e11d2f1ebbac1d0a979069edf7adb Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 21 Nov 2012 17:30:50 +0100 Subject: [PATCH 087/119] mfd: lpc_ich: Fix resource request for [mem 0x00000000] The older southbridges supported by the lpc_ich driver do not provide memory-mapped space of the root complex. The driver correctly avoids computing the iomem address in this case, yet submits a zeroed resource request anyway (via mfd_add_devices()). Remove the iomem resource from the resource array submitted to the mfd core for the older southbridges. Acked-by: Aaron Sierra Cc: Peter Tyser Signed-off-by: Peter Hurley Signed-off-by: Samuel Ortiz --- drivers/mfd/lpc_ich.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index a22544fe5319..afb2f77a9ae8 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -830,7 +830,10 @@ static int __devinit lpc_ich_init_wdt(struct pci_dev *dev, * we have to read RCBA from PCI Config space 0xf0 and use * it as base. GCS = RCBA + ICH6_GCS(0x3410). */ - if (lpc_chipset_info[id->driver_data].iTCO_version == 2) { + if (lpc_chipset_info[id->driver_data].iTCO_version == 1) { + /* Don't register iomem for TCO ver 1 */ + lpc_ich_cells[LPC_WDT].num_resources--; + } else { pci_read_config_dword(dev, RCBABASE, &base_addr_cfg); base_addr = base_addr_cfg & 0xffffc000; if (!(base_addr_cfg & 1)) { From 0c418844dce21fa7000b51190f393c7d6a7ee12d Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Mon, 19 Nov 2012 21:04:11 +0100 Subject: [PATCH 088/119] mfd: lpc_ich: One uninitialized cell is no error At every boot of an (outdated) laptop lpc_ich prints an error: lpc_ich 0000:00:1f.0: I/O space for GPIO uninitialized But if one looks at lpc_ich's probe function one notices that the code only cares if both lpc_ich_init_wdt() and lpc_ich_init_gpio() fail to add any cells. So stop treating the failure to add a single cell as an error. Those messages can be printed at notice level. And then only warn if no cells were added. Signed-off-by: Paul Bolle Signed-off-by: Samuel Ortiz --- drivers/mfd/lpc_ich.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index afb2f77a9ae8..b6dd4993c66b 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -734,7 +734,7 @@ static int __devinit lpc_ich_init_gpio(struct pci_dev *dev, pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg); base_addr = base_addr_cfg & 0x0000ff80; if (!base_addr) { - dev_err(&dev->dev, "I/O space for ACPI uninitialized\n"); + dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n"); lpc_ich_cells[LPC_GPIO].num_resources--; goto gpe0_done; } @@ -760,7 +760,7 @@ gpe0_done: pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg); base_addr = base_addr_cfg & 0x0000ff80; if (!base_addr) { - dev_err(&dev->dev, "I/O space for GPIO uninitialized\n"); + dev_notice(&dev->dev, "I/O space for GPIO uninitialized\n"); ret = -ENODEV; goto gpio_done; } @@ -810,7 +810,7 @@ static int __devinit lpc_ich_init_wdt(struct pci_dev *dev, pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg); base_addr = base_addr_cfg & 0x0000ff80; if (!base_addr) { - dev_err(&dev->dev, "I/O space for ACPI uninitialized\n"); + dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n"); ret = -ENODEV; goto wdt_done; } @@ -837,8 +837,8 @@ static int __devinit lpc_ich_init_wdt(struct pci_dev *dev, pci_read_config_dword(dev, RCBABASE, &base_addr_cfg); base_addr = base_addr_cfg & 0xffffc000; if (!(base_addr_cfg & 1)) { - pr_err("RCBA is disabled by hardware/BIOS, " - "device disabled\n"); + dev_notice(&dev->dev, "RCBA is disabled by " + "hardware/BIOS, device disabled\n"); ret = -ENODEV; goto wdt_done; } @@ -874,6 +874,7 @@ static int __devinit lpc_ich_probe(struct pci_dev *dev, * successfully. */ if (!cell_added) { + dev_warn(&dev->dev, "No MFD cells added\n"); lpc_ich_restore_config_space(dev); return -ENODEV; } From cb5faba951142fc00c41b680eec7003f21947b92 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 22 Nov 2012 10:40:29 +0530 Subject: [PATCH 089/119] mfd: stmpe: Use devm_*() routines This patch frees stmpe driver from tension of freeing resources. devm_* derivatives of multiple routines are used while allocating resources, which would be freed automatically by kernel. Signed-off-by: Viresh Kumar Acked-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/stmpe.c | 60 ++++++++++++++++----------------------------- 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index ba157d45192d..c0df4b9d51c3 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -1052,17 +1052,17 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum) int ret; if (!pdata) { - if (np) { - pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - stmpe_of_probe(pdata, np); - } else + if (!np) return -EINVAL; + + pdata = devm_kzalloc(ci->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + stmpe_of_probe(pdata, np); } - stmpe = kzalloc(sizeof(struct stmpe), GFP_KERNEL); + stmpe = devm_kzalloc(ci->dev, sizeof(struct stmpe), GFP_KERNEL); if (!stmpe) return -ENOMEM; @@ -1084,11 +1084,12 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum) ci->init(stmpe); if (pdata->irq_over_gpio) { - ret = gpio_request_one(pdata->irq_gpio, GPIOF_DIR_IN, "stmpe"); + ret = devm_gpio_request_one(ci->dev, pdata->irq_gpio, + GPIOF_DIR_IN, "stmpe"); if (ret) { dev_err(stmpe->dev, "failed to request IRQ GPIO: %d\n", ret); - goto out_free; + return ret; } stmpe->irq = gpio_to_irq(pdata->irq_gpio); @@ -1105,48 +1106,37 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum) dev_err(stmpe->dev, "%s does not support no-irq mode!\n", stmpe->variant->name); - ret = -ENODEV; - goto free_gpio; + return -ENODEV; } stmpe->variant = stmpe_noirq_variant_info[stmpe->partnum]; } ret = stmpe_chip_init(stmpe); if (ret) - goto free_gpio; + return ret; if (stmpe->irq >= 0) { ret = stmpe_irq_init(stmpe, np); if (ret) - goto free_gpio; + return ret; - ret = request_threaded_irq(stmpe->irq, NULL, stmpe_irq, - pdata->irq_trigger | IRQF_ONESHOT, + ret = devm_request_threaded_irq(ci->dev, stmpe->irq, NULL, + stmpe_irq, pdata->irq_trigger | IRQF_ONESHOT, "stmpe", stmpe); if (ret) { dev_err(stmpe->dev, "failed to request IRQ: %d\n", ret); - goto free_gpio; + return ret; } } ret = stmpe_devices_init(stmpe); - if (ret) { - dev_err(stmpe->dev, "failed to add children\n"); - goto out_removedevs; - } + if (!ret) + return 0; - return 0; - -out_removedevs: + dev_err(stmpe->dev, "failed to add children\n"); mfd_remove_devices(stmpe->dev); - if (stmpe->irq >= 0) - free_irq(stmpe->irq, stmpe); -free_gpio: - if (pdata->irq_over_gpio) - gpio_free(pdata->irq_gpio); -out_free: - kfree(stmpe); + return ret; } @@ -1154,14 +1144,6 @@ int stmpe_remove(struct stmpe *stmpe) { mfd_remove_devices(stmpe->dev); - if (stmpe->irq >= 0) - free_irq(stmpe->irq, stmpe); - - if (stmpe->pdata->irq_over_gpio) - gpio_free(stmpe->pdata->irq_gpio); - - kfree(stmpe); - return 0; } From acad189b08456722ca4a8984218d6f38f4563cbc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 22 Nov 2012 11:12:04 +0100 Subject: [PATCH 090/119] mfd: Add an AS3711 PMIC MFD driver AS3711 is a PMIC with multiple DCDC and LDO power supplies, GPIOs, an RTC, a battery charger and a general purpose ADC. This patch adds support for the MFD with support for a regulator driver and a backlight driver. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 9 ++ drivers/mfd/Makefile | 1 + drivers/mfd/as3711.c | 217 +++++++++++++++++++++++++++++++++++++ include/linux/mfd/as3711.h | 126 +++++++++++++++++++++ 4 files changed, 353 insertions(+) create mode 100644 drivers/mfd/as3711.c create mode 100644 include/linux/mfd/as3711.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f5b839b718aa..475c26696b95 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1103,6 +1103,15 @@ config MFD_RETU Retu is a multi-function device found on Nokia Internet Tablets (770, N800 and N810). +config MFD_AS3711 + bool "Support for AS3711" + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + depends on I2C=y + help + Support for the AS3711 PMIC from AMS + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 2689c8a0d781..f2216dfd963c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -146,3 +146,4 @@ obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o obj-$(CONFIG_MFD_RETU) += retu-mfd.o +obj-$(CONFIG_MFD_AS3711) += as3711.o diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c new file mode 100644 index 000000000000..e994c9691124 --- /dev/null +++ b/drivers/mfd/as3711.c @@ -0,0 +1,217 @@ +/* + * AS3711 PMIC MFC driver + * + * Copyright (C) 2012 Renesas Electronics Corporation + * Author: Guennadi Liakhovetski, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License as + * published by the Free Software Foundation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + AS3711_REGULATOR, + AS3711_BACKLIGHT, +}; + +/* + * Ok to have it static: it is only used during probing and multiple I2C devices + * cannot be probed simultaneously. Just make sure to avoid stale data. + */ +static struct mfd_cell as3711_subdevs[] = { + [AS3711_REGULATOR] = {.name = "as3711-regulator",}, + [AS3711_BACKLIGHT] = {.name = "as3711-backlight",}, +}; + +static bool as3711_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AS3711_GPIO_SIGNAL_IN: + case AS3711_INTERRUPT_STATUS_1: + case AS3711_INTERRUPT_STATUS_2: + case AS3711_INTERRUPT_STATUS_3: + case AS3711_CHARGER_STATUS_1: + case AS3711_CHARGER_STATUS_2: + case AS3711_REG_STATUS: + return true; + } + return false; +} + +static bool as3711_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AS3711_INTERRUPT_STATUS_1: + case AS3711_INTERRUPT_STATUS_2: + case AS3711_INTERRUPT_STATUS_3: + return true; + } + return false; +} + +static bool as3711_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AS3711_SD_1_VOLTAGE: + case AS3711_SD_2_VOLTAGE: + case AS3711_SD_3_VOLTAGE: + case AS3711_SD_4_VOLTAGE: + case AS3711_LDO_1_VOLTAGE: + case AS3711_LDO_2_VOLTAGE: + case AS3711_LDO_3_VOLTAGE: + case AS3711_LDO_4_VOLTAGE: + case AS3711_LDO_5_VOLTAGE: + case AS3711_LDO_6_VOLTAGE: + case AS3711_LDO_7_VOLTAGE: + case AS3711_LDO_8_VOLTAGE: + case AS3711_SD_CONTROL: + case AS3711_GPIO_SIGNAL_OUT: + case AS3711_GPIO_SIGNAL_IN: + case AS3711_SD_CONTROL_1: + case AS3711_SD_CONTROL_2: + case AS3711_CURR_CONTROL: + case AS3711_CURR1_VALUE: + case AS3711_CURR2_VALUE: + case AS3711_CURR3_VALUE: + case AS3711_STEPUP_CONTROL_1: + case AS3711_STEPUP_CONTROL_2: + case AS3711_STEPUP_CONTROL_4: + case AS3711_STEPUP_CONTROL_5: + case AS3711_REG_STATUS: + case AS3711_INTERRUPT_STATUS_1: + case AS3711_INTERRUPT_STATUS_2: + case AS3711_INTERRUPT_STATUS_3: + case AS3711_CHARGER_STATUS_1: + case AS3711_CHARGER_STATUS_2: + case AS3711_ASIC_ID_1: + case AS3711_ASIC_ID_2: + return true; + } + return false; +} + +static const struct regmap_config as3711_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = as3711_volatile_reg, + .readable_reg = as3711_readable_reg, + .precious_reg = as3711_precious_reg, + .max_register = AS3711_MAX_REGS, + .num_reg_defaults_raw = AS3711_MAX_REGS, + .cache_type = REGCACHE_RBTREE, +}; + +static int as3711_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct as3711 *as3711; + struct as3711_platform_data *pdata = client->dev.platform_data; + unsigned int id1, id2; + int ret; + + if (!pdata) + dev_dbg(&client->dev, "Platform data not found\n"); + + as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL); + if (!as3711) { + dev_err(&client->dev, "Memory allocation failed\n"); + return -ENOMEM; + } + + as3711->dev = &client->dev; + i2c_set_clientdata(client, as3711); + + if (client->irq) + dev_notice(&client->dev, "IRQ not supported yet\n"); + + as3711->regmap = devm_regmap_init_i2c(client, &as3711_regmap_config); + if (IS_ERR(as3711->regmap)) { + ret = PTR_ERR(as3711->regmap); + dev_err(&client->dev, "regmap initialization failed: %d\n", ret); + return ret; + } + + ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_1, &id1); + if (!ret) + ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_2, &id2); + if (ret < 0) { + dev_err(&client->dev, "regmap_read() failed: %d\n", ret); + return ret; + } + if (id1 != 0x8b) + return -ENODEV; + dev_info(as3711->dev, "AS3711 detected: %x:%x\n", id1, id2); + + /* We can reuse as3711_subdevs[], it will be copied in mfd_add_devices() */ + if (pdata) { + as3711_subdevs[AS3711_REGULATOR].platform_data = &pdata->regulator; + as3711_subdevs[AS3711_REGULATOR].pdata_size = sizeof(pdata->regulator); + as3711_subdevs[AS3711_BACKLIGHT].platform_data = &pdata->backlight; + as3711_subdevs[AS3711_BACKLIGHT].pdata_size = sizeof(pdata->backlight); + } else { + as3711_subdevs[AS3711_REGULATOR].platform_data = NULL; + as3711_subdevs[AS3711_REGULATOR].pdata_size = 0; + as3711_subdevs[AS3711_BACKLIGHT].platform_data = NULL; + as3711_subdevs[AS3711_BACKLIGHT].pdata_size = 0; + } + + ret = mfd_add_devices(as3711->dev, -1, as3711_subdevs, + ARRAY_SIZE(as3711_subdevs), NULL, 0, NULL); + if (ret < 0) + dev_err(&client->dev, "add mfd devices failed: %d\n", ret); + + return ret; +} + +static int as3711_i2c_remove(struct i2c_client *client) +{ + struct as3711 *as3711 = i2c_get_clientdata(client); + + mfd_remove_devices(as3711->dev); + return 0; +} + +static const struct i2c_device_id as3711_i2c_id[] = { + {.name = "as3711", .driver_data = 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, as3711_i2c_id); + +static struct i2c_driver as3711_i2c_driver = { + .driver = { + .name = "as3711", + .owner = THIS_MODULE, + }, + .probe = as3711_i2c_probe, + .remove = as3711_i2c_remove, + .id_table = as3711_i2c_id, +}; + +static int __init as3711_i2c_init(void) +{ + return i2c_add_driver(&as3711_i2c_driver); +} +/* Initialise early */ +subsys_initcall(as3711_i2c_init); + +static void __exit as3711_i2c_exit(void) +{ + i2c_del_driver(&as3711_i2c_driver); +} +module_exit(as3711_i2c_exit); + +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_DESCRIPTION("AS3711 PMIC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/as3711.h b/include/linux/mfd/as3711.h new file mode 100644 index 000000000000..38452ce1e892 --- /dev/null +++ b/include/linux/mfd/as3711.h @@ -0,0 +1,126 @@ +/* + * AS3711 PMIC MFC driver header + * + * Copyright (C) 2012 Renesas Electronics Corporation + * Author: Guennadi Liakhovetski, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License as + * published by the Free Software Foundation + */ + +#ifndef MFD_AS3711_H +#define MFD_AS3711_H + +/* + * Client data + */ + +/* Register addresses */ +#define AS3711_SD_1_VOLTAGE 0 /* Digital Step-Down */ +#define AS3711_SD_2_VOLTAGE 1 +#define AS3711_SD_3_VOLTAGE 2 +#define AS3711_SD_4_VOLTAGE 3 +#define AS3711_LDO_1_VOLTAGE 4 /* Analog LDO */ +#define AS3711_LDO_2_VOLTAGE 5 +#define AS3711_LDO_3_VOLTAGE 6 /* Digital LDO */ +#define AS3711_LDO_4_VOLTAGE 7 +#define AS3711_LDO_5_VOLTAGE 8 +#define AS3711_LDO_6_VOLTAGE 9 +#define AS3711_LDO_7_VOLTAGE 0xa +#define AS3711_LDO_8_VOLTAGE 0xb +#define AS3711_SD_CONTROL 0x10 +#define AS3711_GPIO_SIGNAL_OUT 0x20 +#define AS3711_GPIO_SIGNAL_IN 0x21 +#define AS3711_SD_CONTROL_1 0x30 +#define AS3711_SD_CONTROL_2 0x31 +#define AS3711_CURR_CONTROL 0x40 +#define AS3711_CURR1_VALUE 0x43 +#define AS3711_CURR2_VALUE 0x44 +#define AS3711_CURR3_VALUE 0x45 +#define AS3711_STEPUP_CONTROL_1 0x50 +#define AS3711_STEPUP_CONTROL_2 0x51 +#define AS3711_STEPUP_CONTROL_4 0x53 +#define AS3711_STEPUP_CONTROL_5 0x54 +#define AS3711_REG_STATUS 0x73 +#define AS3711_INTERRUPT_STATUS_1 0x77 +#define AS3711_INTERRUPT_STATUS_2 0x78 +#define AS3711_INTERRUPT_STATUS_3 0x79 +#define AS3711_CHARGER_STATUS_1 0x86 +#define AS3711_CHARGER_STATUS_2 0x87 +#define AS3711_ASIC_ID_1 0x90 +#define AS3711_ASIC_ID_2 0x91 + +#define AS3711_MAX_REGS 0x92 + +/* Regulators */ +enum { + AS3711_REGULATOR_SD_1, + AS3711_REGULATOR_SD_2, + AS3711_REGULATOR_SD_3, + AS3711_REGULATOR_SD_4, + AS3711_REGULATOR_LDO_1, + AS3711_REGULATOR_LDO_2, + AS3711_REGULATOR_LDO_3, + AS3711_REGULATOR_LDO_4, + AS3711_REGULATOR_LDO_5, + AS3711_REGULATOR_LDO_6, + AS3711_REGULATOR_LDO_7, + AS3711_REGULATOR_LDO_8, + + AS3711_REGULATOR_MAX, +}; + +struct device; +struct regmap; + +struct as3711 { + struct device *dev; + struct regmap *regmap; +}; + +#define AS3711_MAX_STEPDOWN 4 +#define AS3711_MAX_STEPUP 2 +#define AS3711_MAX_LDO 8 + +enum as3711_su2_feedback { + AS3711_SU2_VOLTAGE, + AS3711_SU2_CURR1, + AS3711_SU2_CURR2, + AS3711_SU2_CURR3, + AS3711_SU2_CURR_AUTO, +}; + +enum as3711_su2_fbprot { + AS3711_SU2_LX_SD4, + AS3711_SU2_GPIO2, + AS3711_SU2_GPIO3, + AS3711_SU2_GPIO4, +}; + +/* + * Platform data + */ + +struct as3711_regulator_pdata { + struct regulator_init_data *init_data[AS3711_REGULATOR_MAX]; +}; + +struct as3711_bl_pdata { + const char *su1_fb; + int su1_max_uA; + const char *su2_fb; + int su2_max_uA; + enum as3711_su2_feedback su2_feedback; + enum as3711_su2_fbprot su2_fbprot; + bool su2_auto_curr1; + bool su2_auto_curr2; + bool su2_auto_curr3; +}; + +struct as3711_platform_data { + struct as3711_regulator_pdata regulator; + struct as3711_bl_pdata backlight; +}; + +#endif From 916a871c48ef3ecbc4cac8f185c03160d0b42bfd Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 22 Nov 2012 11:35:40 +0100 Subject: [PATCH 091/119] mfd: ab8500-core: Add abx500-clk as an mfd child device Hierarchically, the abx500-clk shall be considered as a child of the ab8500 core. The abx500-clk is intiated at arch init and thus the clks will be available when clients needs them. Cc: Samuel Ortiz Signed-off-by: Ulf Hansson Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 2a69dc2c1f84..5b8a4de4328b 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -951,6 +951,10 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = { .name = "ab8500-regulator", .of_compatible = "stericsson,ab8500-regulator", }, + { + .name = "abx500-clk", + .of_compatible = "stericsson,abx500-clk", + }, { .name = "ab8500-gpadc", .of_compatible = "stericsson,ab8500-gpadc", From cc899bc3dce9101920b120abe413299f84233e29 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 22 Nov 2012 12:24:24 +0000 Subject: [PATCH 092/119] Documentation: Fix historical inconsistency in STMPE DT doc Previously a generic binding 'i2c-client-wake' was created which enabled I2C devices to register themselves as wake-up devices. This binding was later over-thrown by 'wakeup-source'. The STMPE driver was fixed-up, but the document was neglected. This patch aims to rectify that. Cc: Samuel Ortiz Cc: devicetree-discuss@lists.ozlabs.org Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- Documentation/devicetree/bindings/mfd/stmpe.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt index 8f0aeda2e326..c98450887d5e 100644 --- a/Documentation/devicetree/bindings/mfd/stmpe.txt +++ b/Documentation/devicetree/bindings/mfd/stmpe.txt @@ -8,7 +8,7 @@ Optional properties: - interrupts : The interrupt outputs from the controller - interrupt-controller : Marks the device node as an interrupt controller - interrupt-parent : Specifies which IRQ controller we're connected to - - i2c-client-wake : Marks the input device as wakable + - wakeup-source : Marks the input device as wakable - st,autosleep-timeout : Valid entries (ms); 4, 16, 32, 64, 128, 256, 512 and 1024 Example: @@ -20,6 +20,6 @@ Example: interrupt-parent = <&gpio6>; interrupt-controller; - i2c-client-wake; + wakeup-source; st,autosleep-timeout = <1024>; }; From fee546ce8cfd9dea1f53175f627e17ef5ff05df4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 23 Nov 2012 12:05:33 +0900 Subject: [PATCH 093/119] mfd: wm8994: Add support for WM1811 rev E This is supported identically to the previous revisions. Signed-off-by: Mark Brown Cc: stable@vger.kernel.org Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 8fefc961ec06..f1ac2888d488 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -557,6 +557,7 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) case 1: case 2: case 3: + case 4: regmap_patch = wm1811_reva_patch; patch_regs = ARRAY_SIZE(wm1811_reva_patch); break; From 5e393a2227ba97408ffb98d62cf362dfe2a59baa Mon Sep 17 00:00:00 2001 From: Inderpal Singh Date: Wed, 17 Oct 2012 11:48:55 +0530 Subject: [PATCH 094/119] mfd: sec: Fix reg_offset for interrupt registers reg_offset is offset of the status/mask registers. Now, since status_base and mask_base are pointing to corresponding first registers, reg_offset should start from 0 otheriwse regmap_add_irq_chip will fail during probe. Signed-off-by: Inderpal Singh Signed-off-by: Samuel Ortiz --- drivers/mfd/sec-irq.c | 102 +++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c index c901fa50fea1..0dd84e99081e 100644 --- a/drivers/mfd/sec-irq.c +++ b/drivers/mfd/sec-irq.c @@ -24,67 +24,67 @@ static struct regmap_irq s2mps11_irqs[] = { [S2MPS11_IRQ_PWRONF] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S2MPS11_IRQ_PWRONF_MASK, }, [S2MPS11_IRQ_PWRONR] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S2MPS11_IRQ_PWRONR_MASK, }, [S2MPS11_IRQ_JIGONBF] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S2MPS11_IRQ_JIGONBF_MASK, }, [S2MPS11_IRQ_JIGONBR] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S2MPS11_IRQ_JIGONBR_MASK, }, [S2MPS11_IRQ_ACOKBF] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S2MPS11_IRQ_ACOKBF_MASK, }, [S2MPS11_IRQ_ACOKBR] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S2MPS11_IRQ_ACOKBR_MASK, }, [S2MPS11_IRQ_PWRON1S] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S2MPS11_IRQ_PWRON1S_MASK, }, [S2MPS11_IRQ_MRB] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S2MPS11_IRQ_MRB_MASK, }, [S2MPS11_IRQ_RTC60S] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S2MPS11_IRQ_RTC60S_MASK, }, [S2MPS11_IRQ_RTCA1] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S2MPS11_IRQ_RTCA1_MASK, }, [S2MPS11_IRQ_RTCA2] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S2MPS11_IRQ_RTCA2_MASK, }, [S2MPS11_IRQ_SMPL] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S2MPS11_IRQ_SMPL_MASK, }, [S2MPS11_IRQ_RTC1S] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S2MPS11_IRQ_RTC1S_MASK, }, [S2MPS11_IRQ_WTSR] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S2MPS11_IRQ_WTSR_MASK, }, [S2MPS11_IRQ_INT120C] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S2MPS11_IRQ_INT120C_MASK, }, [S2MPS11_IRQ_INT140C] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S2MPS11_IRQ_INT140C_MASK, }, }; @@ -92,146 +92,146 @@ static struct regmap_irq s2mps11_irqs[] = { static struct regmap_irq s5m8767_irqs[] = { [S5M8767_IRQ_PWRR] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8767_IRQ_PWRR_MASK, }, [S5M8767_IRQ_PWRF] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8767_IRQ_PWRF_MASK, }, [S5M8767_IRQ_PWR1S] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8767_IRQ_PWR1S_MASK, }, [S5M8767_IRQ_JIGR] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8767_IRQ_JIGR_MASK, }, [S5M8767_IRQ_JIGF] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8767_IRQ_JIGF_MASK, }, [S5M8767_IRQ_LOWBAT2] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8767_IRQ_LOWBAT2_MASK, }, [S5M8767_IRQ_LOWBAT1] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8767_IRQ_LOWBAT1_MASK, }, [S5M8767_IRQ_MRB] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S5M8767_IRQ_MRB_MASK, }, [S5M8767_IRQ_DVSOK2] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S5M8767_IRQ_DVSOK2_MASK, }, [S5M8767_IRQ_DVSOK3] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S5M8767_IRQ_DVSOK3_MASK, }, [S5M8767_IRQ_DVSOK4] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S5M8767_IRQ_DVSOK4_MASK, }, [S5M8767_IRQ_RTC60S] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8767_IRQ_RTC60S_MASK, }, [S5M8767_IRQ_RTCA1] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8767_IRQ_RTCA1_MASK, }, [S5M8767_IRQ_RTCA2] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8767_IRQ_RTCA2_MASK, }, [S5M8767_IRQ_SMPL] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8767_IRQ_SMPL_MASK, }, [S5M8767_IRQ_RTC1S] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8767_IRQ_RTC1S_MASK, }, [S5M8767_IRQ_WTSR] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8767_IRQ_WTSR_MASK, }, }; static struct regmap_irq s5m8763_irqs[] = { [S5M8763_IRQ_DCINF] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8763_IRQ_DCINF_MASK, }, [S5M8763_IRQ_DCINR] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8763_IRQ_DCINR_MASK, }, [S5M8763_IRQ_JIGF] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8763_IRQ_JIGF_MASK, }, [S5M8763_IRQ_JIGR] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8763_IRQ_JIGR_MASK, }, [S5M8763_IRQ_PWRONF] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8763_IRQ_PWRONF_MASK, }, [S5M8763_IRQ_PWRONR] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8763_IRQ_PWRONR_MASK, }, [S5M8763_IRQ_WTSREVNT] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S5M8763_IRQ_WTSREVNT_MASK, }, [S5M8763_IRQ_SMPLEVNT] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S5M8763_IRQ_SMPLEVNT_MASK, }, [S5M8763_IRQ_ALARM1] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S5M8763_IRQ_ALARM1_MASK, }, [S5M8763_IRQ_ALARM0] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S5M8763_IRQ_ALARM0_MASK, }, [S5M8763_IRQ_ONKEY1S] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8763_IRQ_ONKEY1S_MASK, }, [S5M8763_IRQ_TOPOFFR] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8763_IRQ_TOPOFFR_MASK, }, [S5M8763_IRQ_DCINOVPR] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8763_IRQ_DCINOVPR_MASK, }, [S5M8763_IRQ_CHGRSTF] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8763_IRQ_CHGRSTF_MASK, }, [S5M8763_IRQ_DONER] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8763_IRQ_DONER_MASK, }, [S5M8763_IRQ_CHGFAULT] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8763_IRQ_CHGFAULT_MASK, }, [S5M8763_IRQ_LOBAT1] = { - .reg_offset = 4, + .reg_offset = 3, .mask = S5M8763_IRQ_LOBAT1_MASK, }, [S5M8763_IRQ_LOBAT2] = { - .reg_offset = 4, + .reg_offset = 3, .mask = S5M8763_IRQ_LOBAT2_MASK, }, }; From 3c39c9c6e9bda4d234bd24aaf34606479f581f4a Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Tue, 6 Nov 2012 13:39:03 +0530 Subject: [PATCH 095/119] MFD: ti_am335x_tscadc: Pass correct error message Pass on the correct error message from platform_get_irq() instead of hard coding it to "EINVAL". Also change label from "err" to "ret" for better readability and update the same in error path. Signed-off-by: Patil, Rachna Signed-off-by: Samuel Ortiz --- drivers/mfd/ti_am335x_tscadc.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index e947dd8bbcc3..8ca3bf023fb2 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -65,7 +65,6 @@ static int __devinit ti_tscadc_probe(struct platform_device *pdev) struct clk *clk; struct mfd_tscadc_board *pdata = pdev->dev.platform_data; struct mfd_cell *cell; - int irq; int err, ctrl; int clk_value, clock_rate; int tsc_wires, adc_channels = 0, total_channels; @@ -92,12 +91,6 @@ static int __devinit ti_tscadc_probe(struct platform_device *pdev) return -EINVAL; } - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no irq ID is specified.\n"); - return -EINVAL; - } - /* Allocate memory for device */ tscadc = devm_kzalloc(&pdev->dev, sizeof(struct ti_tscadc_dev), GFP_KERNEL); @@ -106,22 +99,26 @@ static int __devinit ti_tscadc_probe(struct platform_device *pdev) return -ENOMEM; } tscadc->dev = &pdev->dev; - tscadc->irq = irq; + + err = platform_get_irq(pdev, 0); + if (err < 0) { + dev_err(&pdev->dev, "no irq ID is specified.\n"); + goto ret; + } else + tscadc->irq = err; res = devm_request_mem_region(&pdev->dev, res->start, resource_size(res), pdev->name); if (!res) { dev_err(&pdev->dev, "failed to reserve registers.\n"); - err = -EBUSY; - goto err; + return -EBUSY; } tscadc->tscadc_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!tscadc->tscadc_base) { dev_err(&pdev->dev, "failed to map registers.\n"); - err = -ENOMEM; - goto err; + return -ENOMEM; } tscadc->regmap_tscadc = devm_regmap_init_mmio(&pdev->dev, @@ -129,7 +126,7 @@ static int __devinit ti_tscadc_probe(struct platform_device *pdev) if (IS_ERR(tscadc->regmap_tscadc)) { dev_err(&pdev->dev, "regmap init failed\n"); err = PTR_ERR(tscadc->regmap_tscadc); - goto err; + goto ret; } pm_runtime_enable(&pdev->dev); @@ -201,7 +198,7 @@ static int __devinit ti_tscadc_probe(struct platform_device *pdev) err_disable_clk: pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); -err: +ret: return err; } From 302b95621dc96651187c21fd66e5a44860e4c3b1 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Mon, 26 Nov 2012 11:24:53 +0100 Subject: [PATCH 096/119] mfd: viperboard: Do version query in dma memory The query for the viperboard version was done with memory buffer on the stack but usb transfers need dma capable memory buffer. This is fixed now. Signed-off-by: Lars Poeschel Signed-off-by: Samuel Ortiz --- drivers/mfd/viperboard.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/viperboard.c b/drivers/mfd/viperboard.c index 276122b37199..af2a6703f34f 100644 --- a/drivers/mfd/viperboard.c +++ b/drivers/mfd/viperboard.c @@ -56,7 +56,6 @@ static int vprbrd_probe(struct usb_interface *interface, u16 version = 0; int pipe, ret; - unsigned char buf[1]; /* allocate memory for our device state and initialize it */ vb = kzalloc(sizeof(*vb), GFP_KERNEL); @@ -76,17 +75,17 @@ static int vprbrd_probe(struct usb_interface *interface, /* get version information, major first, minor then */ pipe = usb_rcvctrlpipe(vb->usb_dev, 0); ret = usb_control_msg(vb->usb_dev, pipe, VPRBRD_USB_REQUEST_MAJOR, - VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, buf, 1, + VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, vb->buf, 1, VPRBRD_USB_TIMEOUT_MS); if (ret == 1) - version = buf[0]; + version = vb->buf[0]; ret = usb_control_msg(vb->usb_dev, pipe, VPRBRD_USB_REQUEST_MINOR, - VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, buf, 1, + VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, vb->buf, 1, VPRBRD_USB_TIMEOUT_MS); if (ret == 1) { version <<= 8; - version = version | buf[0]; + version = version | vb->buf[0]; } dev_info(&interface->dev, From 3f9be35bd9090eaa2f68ed9b24efdbf3abcf4b28 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 20 Nov 2012 10:34:56 +0800 Subject: [PATCH 097/119] mfd: rc5t583: Fix array subscript is above array bounds I got below build warning while compiling this driver. It's obviously RC5T583_MAX_INTERRUPT_MASK_REGS is 9 but irq_en_add array only has 8 elements. CC drivers/mfd/rc5t583-irq.o drivers/mfd/rc5t583-irq.c: In function 'rc5t583_irq_sync_unlock': drivers/mfd/rc5t583-irq.c:227: warning: array subscript is above array bounds drivers/mfd/rc5t583-irq.c: In function 'rc5t583_irq_init': drivers/mfd/rc5t583-irq.c:349: warning: array subscript is above array bounds Since the number of interrupt enable registers is 8, this patch adds define for RC5T583_MAX_INTERRUPT_EN_REGS to fix this bug. Signed-off-by: Axel Lin Acked-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/rc5t583-irq.c | 2 +- include/linux/mfd/rc5t583.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/rc5t583-irq.c b/drivers/mfd/rc5t583-irq.c index fe00cdd6f83d..b41db5968706 100644 --- a/drivers/mfd/rc5t583-irq.c +++ b/drivers/mfd/rc5t583-irq.c @@ -345,7 +345,7 @@ int rc5t583_irq_init(struct rc5t583 *rc5t583, int irq, int irq_base) mutex_init(&rc5t583->irq_lock); /* Initailize all int register to 0 */ - for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++) { + for (i = 0; i < RC5T583_MAX_INTERRUPT_EN_REGS; i++) { ret = rc5t583_write(rc5t583->dev, irq_en_add[i], rc5t583->irq_en_reg[i]); if (ret < 0) diff --git a/include/linux/mfd/rc5t583.h b/include/linux/mfd/rc5t583.h index 36c242e52ef1..fd413ccab915 100644 --- a/include/linux/mfd/rc5t583.h +++ b/include/linux/mfd/rc5t583.h @@ -33,6 +33,7 @@ /* Maximum number of main interrupts */ #define MAX_MAIN_INTERRUPT 5 #define RC5T583_MAX_GPEDGE_REG 2 +#define RC5T583_MAX_INTERRUPT_EN_REGS 8 #define RC5T583_MAX_INTERRUPT_MASK_REGS 9 /* Interrupt enable register */ @@ -304,7 +305,7 @@ struct rc5t583 { uint8_t intc_inten_reg; /* For group interrupt bits and address */ - uint8_t irq_en_reg[RC5T583_MAX_INTERRUPT_MASK_REGS]; + uint8_t irq_en_reg[RC5T583_MAX_INTERRUPT_EN_REGS]; /* For gpio edge */ uint8_t gpedge_reg[RC5T583_MAX_GPEDGE_REG]; From b20a43715e675815d0a426f05a7607ba099136aa Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 23 Nov 2012 15:19:29 +0000 Subject: [PATCH 098/119] mfd: Simplify IRQ domain registration code in STMPE Historically, a driver would have to decide whether it required a Linear or Legacy IRQ domain when registering one. This can end up as quite a lot of code. A new Simple call now exists which simplifies this process. Let's make use of it here. Reviewed-by: Viresh Kumar Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/stmpe.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index c0df4b9d51c3..0061d1bc9719 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -888,18 +888,14 @@ static struct irq_domain_ops stmpe_irq_ops = { static int __devinit stmpe_irq_init(struct stmpe *stmpe, struct device_node *np) { - int base = stmpe->irq_base; + int base = 0; int num_irqs = stmpe->variant->num_irqs; - if (base) { - stmpe->domain = irq_domain_add_legacy( - np, num_irqs, base, 0, &stmpe_irq_ops, stmpe); - } - else { - stmpe->domain = irq_domain_add_linear( - np, num_irqs, &stmpe_irq_ops, stmpe); - } + if (!np) + base = stmpe->irq_base; + stmpe->domain = irq_domain_add_simple(np, num_irqs, base, + &stmpe_irq_ops, stmpe); if (!stmpe->domain) { dev_err(stmpe->dev, "Failed to create irqdomain\n"); return -ENOSYS; From 32533983858f5f6899f39d54230d9640ef4547cd Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 23 Nov 2012 00:26:19 +0530 Subject: [PATCH 099/119] mfd: stmpe-i2c: Move .driver structure fields inside {} in stmpe_i2c_driver Currently, few fields in stmpe_i2c_driver are initialized as: .driver.owner = THIS_MODULE, Group them under {}, like: .driver = { .owner = THIS_MODULE, ... }, Signed-off-by: Viresh Kumar Signed-off-by: Samuel Ortiz --- drivers/mfd/stmpe-i2c.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c index 947a06a1845f..c734dc33fbfd 100644 --- a/drivers/mfd/stmpe-i2c.c +++ b/drivers/mfd/stmpe-i2c.c @@ -82,11 +82,13 @@ static const struct i2c_device_id stmpe_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, stmpe_id); static struct i2c_driver stmpe_i2c_driver = { - .driver.name = "stmpe-i2c", - .driver.owner = THIS_MODULE, + .driver = { + .name = "stmpe-i2c", + .owner = THIS_MODULE, #ifdef CONFIG_PM - .driver.pm = &stmpe_dev_pm_ops, + .pm = &stmpe_dev_pm_ops, #endif + }, .probe = stmpe_i2c_probe, .remove = __devexit_p(stmpe_i2c_remove), .id_table = stmpe_i2c_id, From 931cbaf352e06b649a5033af8dd17eed53efc2c7 Mon Sep 17 00:00:00 2001 From: Devendra Naga Date: Sat, 24 Nov 2012 06:14:38 -0500 Subject: [PATCH 100/119] mfd: jz4740-adc: Use devm_kzalloc Use devm_kzalloc and remove the error path free'ing and unload free'ing as the devm resource functions free them. Signed-off-by: Devendra Naga Signed-off-by: Samuel Ortiz --- drivers/mfd/jz4740-adc.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c index c6b6d7dda517..8619508f3481 100644 --- a/drivers/mfd/jz4740-adc.c +++ b/drivers/mfd/jz4740-adc.c @@ -211,7 +211,7 @@ static int __devinit jz4740_adc_probe(struct platform_device *pdev) int ret; int irq_base; - adc = kmalloc(sizeof(*adc), GFP_KERNEL); + adc = devm_kzalloc(&pdev->dev, sizeof(*adc), GFP_KERNEL); if (!adc) { dev_err(&pdev->dev, "Failed to allocate driver structure\n"); return -ENOMEM; @@ -221,30 +221,27 @@ static int __devinit jz4740_adc_probe(struct platform_device *pdev) if (adc->irq < 0) { ret = adc->irq; dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); - goto err_free; + return ret; } irq_base = platform_get_irq(pdev, 1); if (irq_base < 0) { - ret = irq_base; - dev_err(&pdev->dev, "Failed to get irq base: %d\n", ret); - goto err_free; + dev_err(&pdev->dev, "Failed to get irq base: %d\n", irq_base); + return irq_base; } mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem_base) { - ret = -ENOENT; dev_err(&pdev->dev, "Failed to get platform mmio resource\n"); - goto err_free; + return -ENOENT; } /* Only request the shared registers for the MFD driver */ adc->mem = request_mem_region(mem_base->start, JZ_REG_ADC_STATUS, pdev->name); if (!adc->mem) { - ret = -EBUSY; dev_err(&pdev->dev, "Failed to request mmio memory region\n"); - goto err_free; + return -EBUSY; } adc->base = ioremap_nocache(adc->mem->start, resource_size(adc->mem)); @@ -301,9 +298,6 @@ err_iounmap: iounmap(adc->base); err_release_mem_region: release_mem_region(adc->mem->start, resource_size(adc->mem)); -err_free: - kfree(adc); - return ret; } @@ -325,8 +319,6 @@ static int __devexit jz4740_adc_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - kfree(adc); - return 0; } From 20d5c7defc228cdaeff3ce3442f3a4e86af293c1 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 12 Nov 2012 09:20:49 -0800 Subject: [PATCH 101/119] mfd: Fix stmpe.c build when OF is not enabled Fix build errors when CONFIG_OF is not enabled by including (needs to be added in any case). An alternative fix could be to make the driver depend on OF. drivers/mfd/stmpe.c:1025:2: error: implicit declaration of function 'of_property_read_u32' drivers/mfd/stmpe.c:1030:2: error: implicit declaration of function 'for_each_child_of_node' drivers/mfd/stmpe.c:1030:36: error: expected ';' before '{' token Signed-off-by: Randy Dunlap Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/stmpe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 0061d1bc9719..f9f7de796029 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include From 64cdfe256aae6d43d472331ba03bd4ccbfcb1abb Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 27 Nov 2012 15:15:17 +0800 Subject: [PATCH 102/119] mfd: tps65090: MFD_TPS65090 needs to select REGMAP_IRQ This fixes below build error: drivers/built-in.o: In function `tps65090_i2c_probe': drivers/mfd/tps65090.c:180: undefined reference to `regmap_add_irq_chip' drivers/mfd/tps65090.c:190: undefined reference to `regmap_irq_chip_get_base' drivers/mfd/tps65090.c:203: undefined reference to `regmap_del_irq_chip' drivers/built-in.o: In function `tps65090_i2c_remove': drivers/mfd/tps65090.c:213: undefined reference to `regmap_del_irq_chip' make: *** [vmlinux] Error 1 Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 475c26696b95..5ba95d4bff5c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1016,6 +1016,7 @@ config MFD_TPS65090 depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C + select REGMAP_IRQ help If you say yes here you get support for the TPS65090 series of Power Management chips. From d54e17f9f1d14706e6df1d5509e7eb5878bcd766 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 27 Nov 2012 15:23:30 +0000 Subject: [PATCH 103/119] mfd: wm8994: Make current device behaviour the default As the wm8994 series of devices are now very mature make the current behaviour of the devices the default behaviour, any future revisions are likely to have only minor updates. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-core.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index f1ac2888d488..be1a424cbdeb 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -529,11 +529,10 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) break; case 2: case 3: + default: regmap_patch = wm8994_revc_patch; patch_regs = ARRAY_SIZE(wm8994_revc_patch); break; - default: - break; } break; @@ -552,18 +551,9 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) /* Revision C did not change the relevant layer */ if (wm8994->revision > 1) wm8994->revision++; - switch (wm8994->revision) { - case 0: - case 1: - case 2: - case 3: - case 4: - regmap_patch = wm1811_reva_patch; - patch_regs = ARRAY_SIZE(wm1811_reva_patch); - break; - default: - break; - } + + regmap_patch = wm1811_reva_patch; + patch_regs = ARRAY_SIZE(wm1811_reva_patch); break; default: From b2e2558edf5e68f4efbdec0e09152050b9d79c07 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 27 Nov 2012 17:37:13 +0000 Subject: [PATCH 104/119] mfd: arizona: Register haptics devices Both WM5102 and WM5110 support haptics, register the device. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/arizona-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 1b48f2094806..75619711a9e7 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -272,6 +272,7 @@ static struct mfd_cell early_devs[] = { static struct mfd_cell wm5102_devs[] = { { .name = "arizona-extcon" }, { .name = "arizona-gpio" }, + { .name = "arizona-haptics" }, { .name = "arizona-micsupp" }, { .name = "arizona-pwm" }, { .name = "wm5102-codec" }, @@ -280,6 +281,7 @@ static struct mfd_cell wm5102_devs[] = { static struct mfd_cell wm5110_devs[] = { { .name = "arizona-extcon" }, { .name = "arizona-gpio" }, + { .name = "arizona-haptics" }, { .name = "arizona-micsupp" }, { .name = "arizona-pwm" }, { .name = "wm5110-codec" }, From 27dff048ccdc15e6680c06ab77433be948492d05 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 28 Nov 2012 18:47:28 +0000 Subject: [PATCH 105/119] mfd: wm5102: Correct default for LDO1_CONTROL_2 Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 5cb3374cde54..fa91e25f1e55 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -807,6 +807,7 @@ static const struct reg_default wm5102_reg_default[] = { { 0x000001AA, 0x0004 }, /* R426 - FLL2 GPIO Clock */ { 0x00000200, 0x0006 }, /* R512 - Mic Charge Pump 1 */ { 0x00000210, 0x00D4 }, /* R528 - LDO1 Control 1 */ + { 0x00000212, 0x0001 }, /* R530 - LDO1 Control 2 */ { 0x00000213, 0x0344 }, /* R531 - LDO2 Control 1 */ { 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */ { 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */ From 13ea5813238239930d12d28bd7504a3ac6ba14d1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 28 Nov 2012 19:04:16 +0000 Subject: [PATCH 106/119] mfd: wm5102: Mark DSP memory regions as volatile We can cache some of them but this is simpler for now. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index fa91e25f1e55..ccbdd58cf9be 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -2330,6 +2330,9 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) static bool wm5102_volatile_register(struct device *dev, unsigned int reg) { + if (reg > 0xffff) + return true; + switch (reg) { case ARIZONA_SOFTWARE_RESET: case ARIZONA_DEVICE_REVISION: From 4e1328be4d4e4967d54bf7ae3b0805ec53fc4f91 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Fri, 23 Nov 2012 17:48:43 +0530 Subject: [PATCH 107/119] mfd: ab8500-core: Fix invalid free of devm_ allocated data The objects allocated by devm_* APIs are managed by devres and are freed when the device is detached. Hence there is no need to remove them explicitly in remove function. Signed-off-by: Tushar Behera Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 5b8a4de4328b..660a09bf4564 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1437,11 +1437,6 @@ static int __devexit ab8500_remove(struct platform_device *pdev) sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group); mfd_remove_devices(ab8500->dev); - free_irq(ab8500->irq, ab8500); - - kfree(ab8500->oldmask); - kfree(ab8500->mask); - kfree(ab8500); return 0; } From 12a5105e04143569b3e9e5ef03cf9cad8862473a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 29 Nov 2012 00:17:15 +0530 Subject: [PATCH 108/119] mfd: stmpe: Get rid of irq_invert_polarity Since the very first patch, stmpe core driver is using irq_invert_polarity as part of platform data. But, nobody is actually using it in kernel till now. Also, this is not something part of hardware specs, but is included to cater some board mistakes or quirks. So, better get rid of it. This is earlier discussed here: https://lkml.org/lkml/2012/11/27/636 Signed-off-by: Viresh Kumar Acked-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/stmpe.c | 7 ------- include/linux/mfd/stmpe.h | 2 -- 2 files changed, 9 deletions(-) diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index f9f7de796029..90c6151bc52e 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -953,13 +953,6 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe) else icr |= STMPE_ICR_LSB_HIGH; } - - if (stmpe->pdata->irq_invert_polarity) { - if (id == STMPE801_ID) - icr ^= STMPE801_REG_SYS_CTRL_INT_HI; - else - icr ^= STMPE_ICR_LSB_HIGH; - } } if (stmpe->pdata->autosleep) { diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index 15dac790365f..383ac1512a39 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -190,7 +190,6 @@ struct stmpe_ts_platform_data { * @id: device id to distinguish between multiple STMPEs on the same board * @blocks: bitmask of blocks to enable (use STMPE_BLOCK_*) * @irq_trigger: IRQ trigger to use for the interrupt to the host - * @irq_invert_polarity: IRQ line is connected with reversed polarity * @autosleep: bool to enable/disable stmpe autosleep * @autosleep_timeout: inactivity timeout in milliseconds for autosleep * @irq_base: base IRQ number. %STMPE_NR_IRQS irqs will be used, or @@ -207,7 +206,6 @@ struct stmpe_platform_data { unsigned int blocks; int irq_base; unsigned int irq_trigger; - bool irq_invert_polarity; bool autosleep; bool irq_over_gpio; int irq_gpio; From 0e5fca8106199f5c680bb93e75c16381c4c256ce Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Thu, 29 Nov 2012 08:48:26 +0000 Subject: [PATCH 109/119] mfd: tps65910: Remove unused data The 'io_mutex' is not used anywhere. The regmap API supports the mutex internally, so no additional mutex is required. And 'domain' private data is unnecessary because the irq domain is already registered by using regmap_add_irq_chip(). Signed-off-by: Milo(Woogyom) Kim Acked-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65910.c | 1 - include/linux/mfd/tps65910.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index ca3783350ccf..c160c2d76f79 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -486,7 +486,6 @@ static __devinit int tps65910_i2c_probe(struct i2c_client *i2c, tps65910->dev = &i2c->dev; tps65910->i2c_client = i2c; tps65910->id = chip_id; - mutex_init(&tps65910->io_mutex); tps65910->regmap = devm_regmap_init_i2c(i2c, &tps65910_regmap_config); if (IS_ERR(tps65910->regmap)) { diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h index 0b16903f7e88..20e433e551e3 100644 --- a/include/linux/mfd/tps65910.h +++ b/include/linux/mfd/tps65910.h @@ -893,7 +893,6 @@ struct tps65910 { struct device *dev; struct i2c_client *i2c_client; struct regmap *regmap; - struct mutex io_mutex; unsigned int id; /* Client devices */ @@ -907,7 +906,6 @@ struct tps65910 { /* IRQ Handling */ int chip_irq; struct regmap_irq_chip_data *irq_data; - struct irq_domain *domain; }; struct tps65910_platform_data { From 0582c0fafc36e4a1f2067ea8377c9902cc7997bf Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Thu, 29 Nov 2012 06:42:12 +0000 Subject: [PATCH 110/119] mfd: tps65910: Fix wrong ack_base register The interrupt status registers of TPS65910/1 should be cleared when the associated interrupt event occurs. This work is done in the regmap irq thread - using 'ack_base' register. The ACK registers should be fixed as status register, not mask registers. This patch fixes the infinite interrupt event problem by clearing interrupt status registers. Tested on the AM3517 Craneboard. Signed-off-by: Milo(Woogyom) Kim Acked-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65910.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index c160c2d76f79..d5ef3a5b16b1 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -211,7 +211,7 @@ static struct regmap_irq_chip tps65911_irq_chip = { .irq_reg_stride = 2, .status_base = TPS65910_INT_STS, .mask_base = TPS65910_INT_MSK, - .ack_base = TPS65910_INT_MSK, + .ack_base = TPS65910_INT_STS, }; static struct regmap_irq_chip tps65910_irq_chip = { @@ -222,7 +222,7 @@ static struct regmap_irq_chip tps65910_irq_chip = { .irq_reg_stride = 2, .status_base = TPS65910_INT_STS, .mask_base = TPS65910_INT_MSK, - .ack_base = TPS65910_INT_MSK, + .ack_base = TPS65910_INT_STS, }; static int tps65910_irq_init(struct tps65910 *tps65910, int irq, From 62d62b59bd100b8f146ea941dad273656371a386 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 2 Dec 2012 11:41:46 +0900 Subject: [PATCH 111/119] mfd: arizona: Defer patch initialistation until after first device boot Make sure that we don't race with the initial device boot by only doing the initialisation after we've waited for the boot to complete. The runtime PM code already waits for the boot to complete before it syncs the register patches so in most systems if a race does occur we will power down very soon afterwards and recover anyway. Reported-by: Charles Keepax Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/arizona-core.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 75619711a9e7..f59773da8adf 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -292,6 +292,7 @@ int __devinit arizona_dev_init(struct arizona *arizona) struct device *dev = arizona->dev; const char *type_name; unsigned int reg, val; + int (*apply_patch)(struct arizona *) = NULL; int ret, i; dev_set_drvdata(arizona->dev, arizona); @@ -391,7 +392,7 @@ int __devinit arizona_dev_init(struct arizona *arizona) arizona->type); arizona->type = WM5102; } - ret = wm5102_patch(arizona); + apply_patch = wm5102_patch; break; #endif #ifdef CONFIG_MFD_WM5110 @@ -402,7 +403,7 @@ int __devinit arizona_dev_init(struct arizona *arizona) arizona->type); arizona->type = WM5110; } - ret = wm5110_patch(arizona); + apply_patch = wm5110_patch; break; #endif default: @@ -412,9 +413,6 @@ int __devinit arizona_dev_init(struct arizona *arizona) dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A'); - if (ret != 0) - dev_err(arizona->dev, "Failed to apply patch: %d\n", ret); - /* If we have a /RESET GPIO we'll already be reset */ if (!arizona->pdata.reset) { ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0); @@ -430,6 +428,15 @@ int __devinit arizona_dev_init(struct arizona *arizona) goto err_reset; } + if (apply_patch) { + ret = apply_patch(arizona); + if (ret != 0) { + dev_err(arizona->dev, "Failed to apply patch: %d\n", + ret); + goto err_reset; + } + } + for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) { if (!arizona->pdata.gpio_defaults[i]) continue; From 3748f193552ba21a8239c56eb616d946c47607e7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 2 Dec 2012 11:41:47 +0900 Subject: [PATCH 112/119] mfd: wm5102: Add tuning for revision B Evaluation of revision B of WM5102 suggests updates to the register patch for optimal performance, and make this the default behaviour for new devices. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index ccbdd58cf9be..005de63d01d6 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -573,6 +573,18 @@ static const struct reg_default wm5102_reva_patch[] = { { 0x80, 0x0000 }, }; +static const struct reg_default wm5102_revb_patch[] = { + { 0x80, 0x0003 }, + { 0x081, 0xE022 }, + { 0x410, 0x6080 }, + { 0x418, 0x6080 }, + { 0x420, 0x6080 }, + { 0x428, 0xC000 }, + { 0x441, 0x8014 }, + { 0x458, 0x000b }, + { 0x80, 0x0000 }, +}; + /* We use a function so we can use ARRAY_SIZE() */ int wm5102_patch(struct arizona *arizona) { @@ -582,7 +594,9 @@ int wm5102_patch(struct arizona *arizona) wm5102_reva_patch, ARRAY_SIZE(wm5102_reva_patch)); default: - return 0; + return regmap_register_patch(arizona->regmap, + wm5102_revb_patch, + ARRAY_SIZE(wm5102_revb_patch)); } } From 709edecd4eaaa210ea9296c6d8ec5e9cedf1abe3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 2 Dec 2012 08:36:22 -0500 Subject: [PATCH 113/119] mfd: sta2x11: Fix potential NULL pointer dereference in __sta2x11_mfd_mask() The dereference to 'mfd' should be moved below the NULL test. Signed-off-by: Wei Yongjun Signed-off-by: Samuel Ortiz --- drivers/mfd/sta2x11-mfd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index 6fb0938a9665..009b4b7721eb 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -116,12 +116,14 @@ u32 __sta2x11_mfd_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val, struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); u32 r; unsigned long flags; - void __iomem *regs = mfd->regs[index]; + void __iomem *regs; if (!mfd) { dev_warn(&pdev->dev, ": can't access sctl regs\n"); return 0; } + + regs = mfd->regs[index]; if (!regs) { dev_warn(&pdev->dev, ": system ctl not initialized\n"); return 0; From f69b01c5ce4f8ecc95605e4115d653e65c2e4dcc Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 5 Dec 2012 20:59:00 +0800 Subject: [PATCH 114/119] mfd: tps80031: Add terminating entry for tps80031_id_table The i2c_device_id table is supposed to be zero-terminated. Signed-off-by: Axel Lin Acked-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/tps80031.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/tps80031.c b/drivers/mfd/tps80031.c index f64005efa6fa..10b51f7dfff3 100644 --- a/drivers/mfd/tps80031.c +++ b/drivers/mfd/tps80031.c @@ -543,6 +543,7 @@ static int __devexit tps80031_remove(struct i2c_client *client) static const struct i2c_device_id tps80031_id_table[] = { { "tps80031", TPS80031 }, { "tps80032", TPS80032 }, + { } }; MODULE_DEVICE_TABLE(i2c, tps80031_id_table); From c2ace4fdd0efce3e2fbd930300524191dd7180ae Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 5 Dec 2012 21:19:48 +0800 Subject: [PATCH 115/119] mfd: tps80031: MFD_TPS80031 needs to select REGMAP_IRQ This driver uses regmap_irq APIs, thus need to select REGMAP_IRQ. IRQ_DOMAIN will be selected if select REGMAP_IRQ, thus remove it here. This fixes below build errors: drivers/built-in.o: In function `tps80031_remove': drivers/mfd/tps80031.c:534: undefined reference to `regmap_del_irq_chip' drivers/built-in.o: In function `tps80031_irq_init': drivers/mfd/tps80031.c:305: undefined reference to `regmap_add_irq_chip' drivers/built-in.o: In function `tps80031_probe': drivers/mfd/tps80031.c:496: undefined reference to `regmap_irq_get_domain' drivers/mfd/tps80031.c:512: undefined reference to `regmap_del_irq_chip' make: *** [vmlinux] Error 1 Signed-off-by: Axel Lin Acked-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 5ba95d4bff5c..50bbe88d1664 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -270,7 +270,7 @@ config MFD_TPS80031 depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C - select IRQ_DOMAIN + select REGMAP_IRQ help If you say yes here you get support for the Texas Instruments TPS80031/ TPS80032 Fully Integrated Power Management with Power From b7dea5dc5baf6021f1d007db774569ad04741af9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 5 Dec 2012 11:46:26 +0900 Subject: [PATCH 116/119] mfd: arizona: Log if we fail to create the primary IRQ domain This is the only thing in probe for which we don't log an error. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/arizona-irq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index ef0f2d001df2..7db56ab47594 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -223,6 +223,7 @@ int arizona_irq_init(struct arizona *arizona) arizona->virq = irq_domain_add_linear(NULL, 2, &arizona_domain_ops, arizona); if (!arizona->virq) { + dev_err(arizona->dev, "Failed to add core IRQ domain\n"); ret = -EINVAL; goto err; } From be2f6f5a7833b99bdee97c4b877dcd2afc6cdd00 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 9 Dec 2012 15:40:30 +0100 Subject: [PATCH 117/119] mfd: wm5102: Add readback of DSP status 3 register Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 2 ++ include/linux/mfd/arizona/registers.h | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 005de63d01d6..8844b2f9de87 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -2336,6 +2336,7 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_CLOCKING_1: case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: + case ARIZONA_DSP1_STATUS_3: return true; default: return false; @@ -2386,6 +2387,7 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_AOD_IRQ_RAW_STATUS: case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: + case ARIZONA_DSP1_STATUS_3: case ARIZONA_HEADPHONE_DETECT_2: case ARIZONA_MIC_DETECT_3: return true; diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 7671a287dfee..81bca23c9a92 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -979,6 +979,7 @@ #define ARIZONA_DSP1_CLOCKING_1 0x1101 #define ARIZONA_DSP1_STATUS_1 0x1104 #define ARIZONA_DSP1_STATUS_2 0x1105 +#define ARIZONA_DSP1_STATUS_3 0x1106 #define ARIZONA_DSP2_CONTROL_1 0x1200 #define ARIZONA_DSP2_CLOCKING_1 0x1201 #define ARIZONA_DSP2_STATUS_1 0x1204 From ac713cc9fcb8a5f9503af08421772b85ca4685f1 Mon Sep 17 00:00:00 2001 From: Vipul Kumar Samar Date: Fri, 7 Dec 2012 20:29:37 +0530 Subject: [PATCH 118/119] mfd: stmpe: Update DT support for stmpe driver This patch extends existing DT support for stmpe devices. This updates: - missing header files in stmpe.c - stmpe_of_probe() with pwm, rotator and new bindings. - Bindings are updated in binding document. Acked-by: Lee Jones Acked-by: Linus Walleij Signed-off-by: Vipul Kumar Samar Signed-off-by: Viresh Kumar Signed-off-by: Samuel Ortiz --- .../devicetree/bindings/mfd/stmpe.txt | 9 +++++--- drivers/mfd/stmpe.c | 21 +++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt index c98450887d5e..56edb5520685 100644 --- a/Documentation/devicetree/bindings/mfd/stmpe.txt +++ b/Documentation/devicetree/bindings/mfd/stmpe.txt @@ -1,8 +1,11 @@ -* STMPE Multi-Functional Device +* ST Microelectronics STMPE Multi-Functional Device + +STMPE is an MFD device which may expose the following inbuilt devices: gpio, +keypad, touchscreen, adc, pwm, rotator. Required properties: - - compatible : "st,stmpe[811|1601|2401|2403]" - - reg : I2C address of the device + - compatible : "st,stmpe[610|801|811|1601|2401|2403]" + - reg : I2C/SPI address of the device Optional properties: - interrupts : The interrupt outputs from the controller diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 90c6151bc52e..b18cc1a1b162 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -7,6 +7,7 @@ * Author: Rabin Vincent for ST-Ericsson */ +#include #include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1012,6 +1014,9 @@ void __devinit stmpe_of_probe(struct stmpe_platform_data *pdata, { struct device_node *child; + pdata->id = -1; + pdata->irq_trigger = IRQF_TRIGGER_NONE; + of_property_read_u32(np, "st,autosleep-timeout", &pdata->autosleep_timeout); @@ -1020,15 +1025,16 @@ void __devinit stmpe_of_probe(struct stmpe_platform_data *pdata, for_each_child_of_node(np, child) { if (!strcmp(child->name, "stmpe_gpio")) { pdata->blocks |= STMPE_BLOCK_GPIO; - } - if (!strcmp(child->name, "stmpe_keypad")) { + } else if (!strcmp(child->name, "stmpe_keypad")) { pdata->blocks |= STMPE_BLOCK_KEYPAD; - } - if (!strcmp(child->name, "stmpe_touchscreen")) { + } else if (!strcmp(child->name, "stmpe_touchscreen")) { pdata->blocks |= STMPE_BLOCK_TOUCHSCREEN; - } - if (!strcmp(child->name, "stmpe_adc")) { + } else if (!strcmp(child->name, "stmpe_adc")) { pdata->blocks |= STMPE_BLOCK_ADC; + } else if (!strcmp(child->name, "stmpe_pwm")) { + pdata->blocks |= STMPE_BLOCK_PWM; + } else if (!strcmp(child->name, "stmpe_rotator")) { + pdata->blocks |= STMPE_BLOCK_ROTATOR; } } } @@ -1099,6 +1105,9 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum) return -ENODEV; } stmpe->variant = stmpe_noirq_variant_info[stmpe->partnum]; + } else if (pdata->irq_trigger == IRQF_TRIGGER_NONE) { + pdata->irq_trigger = + irqd_get_trigger_type(irq_get_irq_data(stmpe->irq)); } ret = stmpe_chip_init(stmpe); From 1881b68b8961a86d40c3c5c205e533515a2dc9c6 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 9 Dec 2012 20:25:55 +0800 Subject: [PATCH 119/119] mfd: tps6507x: Convert to devm_kzalloc Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/tps6507x.c | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c index 1b203499c744..409afa23d5dc 100644 --- a/drivers/mfd/tps6507x.c +++ b/drivers/mfd/tps6507x.c @@ -86,9 +86,9 @@ static int tps6507x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct tps6507x_dev *tps6507x; - int ret = 0; - tps6507x = kzalloc(sizeof(struct tps6507x_dev), GFP_KERNEL); + tps6507x = devm_kzalloc(&i2c->dev, sizeof(struct tps6507x_dev), + GFP_KERNEL); if (tps6507x == NULL) return -ENOMEM; @@ -98,19 +98,8 @@ static int tps6507x_i2c_probe(struct i2c_client *i2c, tps6507x->read_dev = tps6507x_i2c_read_device; tps6507x->write_dev = tps6507x_i2c_write_device; - ret = mfd_add_devices(tps6507x->dev, -1, - tps6507x_devs, ARRAY_SIZE(tps6507x_devs), - NULL, 0, NULL); - - if (ret < 0) - goto err; - - return ret; - -err: - mfd_remove_devices(tps6507x->dev); - kfree(tps6507x); - return ret; + return mfd_add_devices(tps6507x->dev, -1, tps6507x_devs, + ARRAY_SIZE(tps6507x_devs), NULL, 0, NULL); } static int tps6507x_i2c_remove(struct i2c_client *i2c) @@ -118,8 +107,6 @@ static int tps6507x_i2c_remove(struct i2c_client *i2c) struct tps6507x_dev *tps6507x = i2c_get_clientdata(i2c); mfd_remove_devices(tps6507x->dev); - kfree(tps6507x); - return 0; }