From 6e20a0a429bd4dc07d6de16d9c247270e04e4aa0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 14 Jun 2012 19:40:41 -0700 Subject: gpio: pcf857x: enable gpio_to_irq() support pcf857x chip has some pins, but interrupt pin is only 1 pin. And gpiolib requests 1 interrupt per 1 gpio pin. Thus, this patch added demuxed irq to pcf857x driver, and enabled gpio_to_irq(). Signed-off-by: Kuninori Morimoto Signed-off-by: Linus Walleij --- include/linux/i2c/pcf857x.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/linux/i2c') diff --git a/include/linux/i2c/pcf857x.h b/include/linux/i2c/pcf857x.h index 0767a2a6b2f1..781e6bd06c34 100644 --- a/include/linux/i2c/pcf857x.h +++ b/include/linux/i2c/pcf857x.h @@ -10,6 +10,7 @@ * @setup: optional callback issued once the GPIOs are valid * @teardown: optional callback issued before the GPIOs are invalidated * @context: optional parameter passed to setup() and teardown() + * @irq: optional interrupt number * * In addition to the I2C_BOARD_INFO() state appropriate to each chip, * the i2c_board_info used with the pcf875x driver must provide its @@ -39,6 +40,8 @@ struct pcf857x_platform_data { int gpio, unsigned ngpio, void *context); void *context; + + int irq; }; #endif /* __LINUX_PCF857X_H */ -- cgit v1.2.3 From a940d9a1cb2ea0833421fd57e47f8ce2a6d9953b Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 4 Sep 2012 17:43:30 -0700 Subject: ARM: OMAP2+: Remove hardcoded twl4030 gpio_base, irq_base and irq_end We can't use hardcoded interrupts for SPARSE_IRQ, and can replace the hardcoded gpio_base with twl_gpiochip.base after it's been allocated. Cc: Grant Likely Cc: Samuel Ortiz Cc: Peter Ujfalusi Acked-by: Linus Walleij Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/board-2430sdp.c | 3 --- arch/arm/mach-omap2/board-3430sdp.c | 3 --- arch/arm/mach-omap2/board-4430sdp.c | 1 - arch/arm/mach-omap2/board-cm-t35.c | 3 --- arch/arm/mach-omap2/board-devkit8000.c | 3 --- arch/arm/mach-omap2/board-igep0020.c | 3 --- arch/arm/mach-omap2/board-ldp.c | 3 --- arch/arm/mach-omap2/board-omap3beagle.c | 3 --- arch/arm/mach-omap2/board-omap3evm.c | 3 --- arch/arm/mach-omap2/board-omap3logic.c | 3 --- arch/arm/mach-omap2/board-omap3pandora.c | 3 --- arch/arm/mach-omap2/board-omap3stalker.c | 3 --- arch/arm/mach-omap2/board-omap3touchbook.c | 3 --- arch/arm/mach-omap2/board-omap4panda.c | 1 - arch/arm/mach-omap2/board-overo.c | 3 --- arch/arm/mach-omap2/board-rm680.c | 3 --- arch/arm/mach-omap2/board-rx51-peripherals.c | 3 --- arch/arm/mach-omap2/board-zoom-peripherals.c | 3 --- drivers/gpio/gpio-twl4030.c | 15 ++++++++++----- include/linux/i2c/twl.h | 3 --- include/linux/mfd/twl6040.h | 1 - 21 files changed, 10 insertions(+), 59 deletions(-) (limited to 'include/linux/i2c') diff --git a/arch/arm/mach-omap2/board-2430sdp.c b/arch/arm/mach-omap2/board-2430sdp.c index 36eee4b512f6..cacc49889129 100644 --- a/arch/arm/mach-omap2/board-2430sdp.c +++ b/arch/arm/mach-omap2/board-2430sdp.c @@ -211,9 +211,6 @@ static struct regulator_init_data sdp2430_vmmc1 = { }; static struct twl4030_gpio_platform_data sdp2430_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, }; static struct twl4030_platform_data sdp2430_twldata = { diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c index 0f78cdbec5c1..c843d01aedda 100644 --- a/arch/arm/mach-omap2/board-3430sdp.c +++ b/arch/arm/mach-omap2/board-3430sdp.c @@ -229,9 +229,6 @@ static int sdp3430_twl_gpio_setup(struct device *dev, } static struct twl4030_gpio_platform_data sdp3430_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .pulldowns = BIT(2) | BIT(6) | BIT(8) | BIT(13) | BIT(16) | BIT(17), .setup = sdp3430_twl_gpio_setup, diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index 04e857419bb6..ee8260481273 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -543,7 +543,6 @@ static struct twl6040_platform_data twl6040_data = { .codec = &twl6040_codec, .vibra = &twl6040_vibra, .audpwron_gpio = 127, - .irq_base = TWL6040_CODEC_IRQ_BASE, }; static struct twl4030_platform_data sdp4430_twldata = { diff --git a/arch/arm/mach-omap2/board-cm-t35.c b/arch/arm/mach-omap2/board-cm-t35.c index d94a640fe41c..ea3410953d2c 100644 --- a/arch/arm/mach-omap2/board-cm-t35.c +++ b/arch/arm/mach-omap2/board-cm-t35.c @@ -469,9 +469,6 @@ static int cm_t35_twl_gpio_setup(struct device *dev, unsigned gpio, } static struct twl4030_gpio_platform_data cm_t35_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .setup = cm_t35_twl_gpio_setup, }; diff --git a/arch/arm/mach-omap2/board-devkit8000.c b/arch/arm/mach-omap2/board-devkit8000.c index 870a2a55a032..9032807702b5 100644 --- a/arch/arm/mach-omap2/board-devkit8000.c +++ b/arch/arm/mach-omap2/board-devkit8000.c @@ -235,9 +235,6 @@ static int devkit8000_twl_gpio_setup(struct device *dev, } static struct twl4030_gpio_platform_data devkit8000_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, .pulldowns = BIT(1) | BIT(2) | BIT(6) | BIT(8) | BIT(13) | BIT(15) | BIT(16) | BIT(17), diff --git a/arch/arm/mach-omap2/board-igep0020.c b/arch/arm/mach-omap2/board-igep0020.c index 01103b38f77f..577554933862 100644 --- a/arch/arm/mach-omap2/board-igep0020.c +++ b/arch/arm/mach-omap2/board-igep0020.c @@ -424,9 +424,6 @@ static int igep_twl_gpio_setup(struct device *dev, }; static struct twl4030_gpio_platform_data igep_twl4030_gpio_pdata = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, .setup = igep_twl_gpio_setup, }; diff --git a/arch/arm/mach-omap2/board-ldp.c b/arch/arm/mach-omap2/board-ldp.c index c37c2a17c410..bea2e3a4c00f 100644 --- a/arch/arm/mach-omap2/board-ldp.c +++ b/arch/arm/mach-omap2/board-ldp.c @@ -274,9 +274,6 @@ static int ldp_twl_gpio_setup(struct device *dev, unsigned gpio, unsigned ngpio) } static struct twl4030_gpio_platform_data ldp_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .setup = ldp_twl_gpio_setup, }; diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index 293e3b26c471..9d9b2abaf7d9 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -296,9 +296,6 @@ static int beagle_twl_gpio_setup(struct device *dev, } static struct twl4030_gpio_platform_data beagle_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, .pullups = BIT(1), .pulldowns = BIT(2) | BIT(6) | BIT(7) | BIT(8) | BIT(13) diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c index 851aec6ecb8a..493bd96746b9 100644 --- a/arch/arm/mach-omap2/board-omap3evm.c +++ b/arch/arm/mach-omap2/board-omap3evm.c @@ -388,9 +388,6 @@ static int omap3evm_twl_gpio_setup(struct device *dev, } static struct twl4030_gpio_platform_data omap3evm_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, .setup = omap3evm_twl_gpio_setup, }; diff --git a/arch/arm/mach-omap2/board-omap3logic.c b/arch/arm/mach-omap2/board-omap3logic.c index a63a49d51fc8..8fe7f0cd9b0f 100644 --- a/arch/arm/mach-omap2/board-omap3logic.c +++ b/arch/arm/mach-omap2/board-omap3logic.c @@ -77,9 +77,6 @@ static struct regulator_init_data omap3logic_vmmc1 = { }; static struct twl4030_gpio_platform_data omap3logic_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, .pullups = BIT(1), .pulldowns = BIT(2) | BIT(6) | BIT(7) | BIT(8) diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c index 633c445c7cc4..38521d43b6dd 100644 --- a/arch/arm/mach-omap2/board-omap3pandora.c +++ b/arch/arm/mach-omap2/board-omap3pandora.c @@ -320,9 +320,6 @@ static int omap3pandora_twl_gpio_setup(struct device *dev, } static struct twl4030_gpio_platform_data omap3pandora_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .setup = omap3pandora_twl_gpio_setup, }; diff --git a/arch/arm/mach-omap2/board-omap3stalker.c b/arch/arm/mach-omap2/board-omap3stalker.c index 421fb8e76f27..87aa5b1d4c88 100644 --- a/arch/arm/mach-omap2/board-omap3stalker.c +++ b/arch/arm/mach-omap2/board-omap3stalker.c @@ -278,9 +278,6 @@ omap3stalker_twl_gpio_setup(struct device *dev, } static struct twl4030_gpio_platform_data omap3stalker_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, .setup = omap3stalker_twl_gpio_setup, }; diff --git a/arch/arm/mach-omap2/board-omap3touchbook.c b/arch/arm/mach-omap2/board-omap3touchbook.c index d3556c938900..88dc913b6325 100644 --- a/arch/arm/mach-omap2/board-omap3touchbook.c +++ b/arch/arm/mach-omap2/board-omap3touchbook.c @@ -138,9 +138,6 @@ static int touchbook_twl_gpio_setup(struct device *dev, } static struct twl4030_gpio_platform_data touchbook_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, .pullups = BIT(1), .pulldowns = BIT(2) | BIT(6) | BIT(7) | BIT(8) | BIT(13) diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c index 3911c13a342a..e37ee6749bed 100644 --- a/arch/arm/mach-omap2/board-omap4panda.c +++ b/arch/arm/mach-omap2/board-omap4panda.c @@ -262,7 +262,6 @@ static struct twl6040_codec_data twl6040_codec = { static struct twl6040_platform_data twl6040_data = { .codec = &twl6040_codec, .audpwron_gpio = 127, - .irq_base = TWL6040_CODEC_IRQ_BASE, }; /* Panda board uses the common PMIC configuration */ diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c index 193d1608b39a..4754f051b91a 100644 --- a/arch/arm/mach-omap2/board-overo.c +++ b/arch/arm/mach-omap2/board-overo.c @@ -398,9 +398,6 @@ static int overo_twl_gpio_setup(struct device *dev, } static struct twl4030_gpio_platform_data overo_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, .setup = overo_twl_gpio_setup, }; diff --git a/arch/arm/mach-omap2/board-rm680.c b/arch/arm/mach-omap2/board-rm680.c index 0ad1bb3bdb98..12411b9fa88c 100644 --- a/arch/arm/mach-omap2/board-rm680.c +++ b/arch/arm/mach-omap2/board-rm680.c @@ -72,9 +72,6 @@ static struct platform_device *rm680_peripherals_devices[] __initdata = { /* TWL */ static struct twl4030_gpio_platform_data rm680_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .pullups = BIT(0), .pulldowns = BIT(1) | BIT(2) | BIT(8) | BIT(15), }; diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c index 3b9fc6105329..e8b6dda85945 100644 --- a/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -773,9 +773,6 @@ static int rx51_twlgpio_setup(struct device *dev, unsigned gpio, unsigned n) } static struct twl4030_gpio_platform_data rx51_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .pulldowns = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(8) | BIT(9) | BIT(10) | BIT(11) diff --git a/arch/arm/mach-omap2/board-zoom-peripherals.c b/arch/arm/mach-omap2/board-zoom-peripherals.c index b797cb279618..00f73b7792e0 100644 --- a/arch/arm/mach-omap2/board-zoom-peripherals.c +++ b/arch/arm/mach-omap2/board-zoom-peripherals.c @@ -251,9 +251,6 @@ static void zoom2_set_hs_extmute(int mute) } static struct twl4030_gpio_platform_data zoom_gpio_data = { - .gpio_base = OMAP_MAX_GPIO_LINES, - .irq_base = TWL4030_GPIO_IRQ_BASE, - .irq_end = TWL4030_GPIO_IRQ_END, .setup = zoom_twl_gpio_setup, }; diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index 94256fe7bf36..f030880bc9bb 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -51,6 +51,7 @@ static struct gpio_chip twl_gpiochip; +static int twl4030_gpio_base; static int twl4030_gpio_irq_base; /* genirq interfaces are not available to modules */ @@ -428,8 +429,6 @@ no_irqs: twl_gpiochip.dev = &pdev->dev; if (pdata) { - twl_gpiochip.base = pdata->gpio_base; - /* * NOTE: boards may waste power if they don't set pullups * and pulldowns correctly ... default for non-ULPI pins is @@ -461,15 +460,21 @@ no_irqs: dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret); twl_gpiochip.ngpio = 0; gpio_twl4030_remove(pdev); - } else if (pdata && pdata->setup) { + goto out; + } + + twl4030_gpio_base = twl_gpiochip.base; + + if (pdata && pdata->setup) { int status; status = pdata->setup(&pdev->dev, - pdata->gpio_base, TWL4030_GPIO_MAX); + twl4030_gpio_base, TWL4030_GPIO_MAX); if (status) dev_dbg(&pdev->dev, "setup --> %d\n", status); } +out: return ret; } @@ -481,7 +486,7 @@ static int gpio_twl4030_remove(struct platform_device *pdev) if (pdata && pdata->teardown) { status = pdata->teardown(&pdev->dev, - pdata->gpio_base, TWL4030_GPIO_MAX); + twl4030_gpio_base, TWL4030_GPIO_MAX); if (status) { dev_dbg(&pdev->dev, "teardown --> %d\n", status); return status; diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 7ea898c55a60..a12a38107c1a 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -561,9 +561,6 @@ struct twl4030_bci_platform_data { /* TWL4030_GPIO_MAX (18) GPIOs, with interrupts */ struct twl4030_gpio_platform_data { - int gpio_base; - unsigned irq_base, irq_end; - /* package the two LED signals as output-only GPIOs? */ bool use_leds; diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index eaad49f7c130..ba43d4806b83 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -194,7 +194,6 @@ struct twl6040_vibra_data { struct twl6040_platform_data { int audpwron_gpio; /* audio power-on gpio */ - unsigned int irq_base; struct twl6040_codec_data *codec; struct twl6040_vibra_data *vibra; -- cgit v1.2.3 From 2275c544cb824b469a386e411cbcaa01a19d4f55 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 10 Sep 2012 13:46:22 +0300 Subject: mfd: twl-core: Add API to query the HFCLK rate CFG_BOOT register's HFCLK_FREQ field hold information about the used HFCLK frequency. Add possibility for users to get the configured rate based on this register. This register was configured during boot, without it the chip would not operate correctly, so we can trust on this information. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 32 ++++++++++++++++++++++++++++++++ include/linux/i2c/twl.h | 1 + 2 files changed, 33 insertions(+) (limited to 'include/linux/i2c') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 1c32afed28aa..f162b68e78a8 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -552,6 +552,38 @@ int twl_get_version(void) } EXPORT_SYMBOL_GPL(twl_get_version); +/** + * twl_get_hfclk_rate - API to get TWL external HFCLK clock rate. + * + * Api to get the TWL HFCLK rate based on BOOT_CFG register. + */ +int twl_get_hfclk_rate(void) +{ + u8 ctrl; + int rate; + + twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &ctrl, R_CFG_BOOT); + + switch (ctrl & 0x3) { + case HFCLK_FREQ_19p2_MHZ: + rate = 19200000; + break; + case HFCLK_FREQ_26_MHZ: + rate = 26000000; + break; + case HFCLK_FREQ_38p4_MHZ: + rate = 38400000; + break; + default: + pr_err("TWL4030: HFCLK is not configured\n"); + rate = -EINVAL; + break; + } + + return rate; +} +EXPORT_SYMBOL_GPL(twl_get_hfclk_rate); + static struct device * add_numbered_child(unsigned chip, const char *name, int num, void *pdata, unsigned pdata_len, diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 7ea898c55a60..ac6488c9f250 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -188,6 +188,7 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); int twl_get_type(void); int twl_get_version(void); +int twl_get_hfclk_rate(void); int twl6030_interrupt_unmask(u8 bit_mask, u8 offset); int twl6030_interrupt_mask(u8 bit_mask, u8 offset); -- cgit v1.2.3 From 4cd7a2f1aedb2008004e110d31ce831895139fa0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 10 Sep 2012 13:46:22 +0300 Subject: mfd: twl-core: Add API to query the HFCLK rate CFG_BOOT register's HFCLK_FREQ field hold information about the used HFCLK frequency. Add possibility for users to get the configured rate based on this register. This register was configured during boot, without it the chip would not operate correctly, so we can trust on this information. Signed-off-by: Peter Ujfalusi Acked-by: Samuel Ortiz Signed-off-by: Mark Brown --- drivers/mfd/twl-core.c | 32 ++++++++++++++++++++++++++++++++ include/linux/i2c/twl.h | 1 + 2 files changed, 33 insertions(+) (limited to 'include/linux/i2c') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 1c32afed28aa..f162b68e78a8 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -552,6 +552,38 @@ int twl_get_version(void) } EXPORT_SYMBOL_GPL(twl_get_version); +/** + * twl_get_hfclk_rate - API to get TWL external HFCLK clock rate. + * + * Api to get the TWL HFCLK rate based on BOOT_CFG register. + */ +int twl_get_hfclk_rate(void) +{ + u8 ctrl; + int rate; + + twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &ctrl, R_CFG_BOOT); + + switch (ctrl & 0x3) { + case HFCLK_FREQ_19p2_MHZ: + rate = 19200000; + break; + case HFCLK_FREQ_26_MHZ: + rate = 26000000; + break; + case HFCLK_FREQ_38p4_MHZ: + rate = 38400000; + break; + default: + pr_err("TWL4030: HFCLK is not configured\n"); + rate = -EINVAL; + break; + } + + return rate; +} +EXPORT_SYMBOL_GPL(twl_get_hfclk_rate); + static struct device * add_numbered_child(unsigned chip, const char *name, int num, void *pdata, unsigned pdata_len, diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 7ea898c55a60..ac6488c9f250 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -188,6 +188,7 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); int twl_get_type(void); int twl_get_version(void); +int twl_get_hfclk_rate(void); int twl6030_interrupt_unmask(u8 bit_mask, u8 offset); int twl6030_interrupt_mask(u8 bit_mask, u8 offset); -- cgit v1.2.3 From 281ecd1611654cdcdec0ffcb55e8f285b8199727 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 10 Sep 2012 13:46:27 +0300 Subject: ASoC: twl4030: Move hs_extmute GPIO handling to driver The external mute (if it is in use) is handled by a GPIO line. Prepare to remove the set_hs_extmute callback and replace it with: hs_extmute_gpio: the GPIO number to use for external mute When the users of set_hs_extmute has been converted the callback can be removed. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- include/linux/i2c/twl.h | 4 +++- sound/soc/codecs/twl4030.c | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) (limited to 'include/linux/i2c') diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index ac6488c9f250..2040309a46b7 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -667,7 +667,9 @@ struct twl4030_codec_data { unsigned int check_defaults:1; unsigned int reset_registers:1; unsigned int hs_extmute:1; - void (*set_hs_extmute)(int mute); + void (*set_hs_extmute)(int mute); /* Deprecated, use hs_extmute_gpio and + hs_extmute_disable_level */ + int hs_extmute_gpio; }; struct twl4030_vibra_data { diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 962341df7ddc..0c83c9263f4f 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -302,6 +303,22 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) u8 reg, byte; int i = 0; + if (pdata && pdata->hs_extmute && + gpio_is_valid(pdata->hs_extmute_gpio)) { + int ret; + + if (!pdata->hs_extmute_gpio) + dev_warn(codec->dev, + "Extmute GPIO is 0 is this correct?\n"); + + ret = gpio_request_one(pdata->hs_extmute_gpio, + GPIOF_OUT_INIT_LOW, "hs_extmute"); + if (ret) { + dev_err(codec->dev, "Failed to get hs_extmute GPIO\n"); + pdata->hs_extmute_gpio = -1; + } + } + /* Check defaults, if instructed before anything else */ if (pdata && pdata->check_defaults) twl4030_check_defaults(codec); @@ -748,7 +765,10 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) /* Enable external mute control, this dramatically reduces * the pop-noise */ if (pdata && pdata->hs_extmute) { - if (pdata->set_hs_extmute) { + if (gpio_is_valid(pdata->hs_extmute_gpio)) { + gpio_set_value(pdata->hs_extmute_gpio, 1); + } else if (pdata->set_hs_extmute) { + dev_warn(codec->dev, "set_hs_extmute is deprecated\n"); pdata->set_hs_extmute(1); } else { hs_pop |= TWL4030_EXTMUTE; @@ -786,7 +806,10 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) /* Disable external mute */ if (pdata && pdata->hs_extmute) { - if (pdata->set_hs_extmute) { + if (gpio_is_valid(pdata->hs_extmute_gpio)) { + gpio_set_value(pdata->hs_extmute_gpio, 0); + } else if (pdata->set_hs_extmute) { + dev_warn(codec->dev, "set_hs_extmute is deprecated\n"); pdata->set_hs_extmute(0); } else { hs_pop &= ~TWL4030_EXTMUTE; @@ -2236,12 +2259,17 @@ static int twl4030_soc_probe(struct snd_soc_codec *codec) static int twl4030_soc_remove(struct snd_soc_codec *codec) { + struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev); struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); /* Reset registers to their chip default before leaving */ twl4030_reset_registers(codec); twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); kfree(twl4030); + + if (pdata && pdata->hs_extmute && gpio_is_valid(pdata->hs_extmute_gpio)) + gpio_free(pdata->hs_extmute_gpio); + return 0; } -- cgit v1.2.3 From d0b3847b40f8da4b90b22db0f3678ba68bcd1b4e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 10 Sep 2012 13:46:29 +0300 Subject: ASoC/mfd: twl4030: Remove set_hs_extmute callback from platform data We no longer have users for the set_hs_extmute callback which has been replaced by hs_extmute_gpio so the codec driver can handle the external mute if it is needed by the board. Signed-off-by: Peter Ujfalusi Acked-by: Samuel Ortiz Signed-off-by: Mark Brown --- include/linux/i2c/twl.h | 2 -- sound/soc/codecs/twl4030.c | 6 ------ 2 files changed, 8 deletions(-) (limited to 'include/linux/i2c') diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 2040309a46b7..a4885a6cd10d 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -667,8 +667,6 @@ struct twl4030_codec_data { unsigned int check_defaults:1; unsigned int reset_registers:1; unsigned int hs_extmute:1; - void (*set_hs_extmute)(int mute); /* Deprecated, use hs_extmute_gpio and - hs_extmute_disable_level */ int hs_extmute_gpio; }; diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 0c83c9263f4f..aa96788c8673 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -767,9 +767,6 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) if (pdata && pdata->hs_extmute) { if (gpio_is_valid(pdata->hs_extmute_gpio)) { gpio_set_value(pdata->hs_extmute_gpio, 1); - } else if (pdata->set_hs_extmute) { - dev_warn(codec->dev, "set_hs_extmute is deprecated\n"); - pdata->set_hs_extmute(1); } else { hs_pop |= TWL4030_EXTMUTE; twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); @@ -808,9 +805,6 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) if (pdata && pdata->hs_extmute) { if (gpio_is_valid(pdata->hs_extmute_gpio)) { gpio_set_value(pdata->hs_extmute_gpio, 0); - } else if (pdata->set_hs_extmute) { - dev_warn(codec->dev, "set_hs_extmute is deprecated\n"); - pdata->set_hs_extmute(0); } else { hs_pop &= ~TWL4030_EXTMUTE; twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); -- cgit v1.2.3 From eee543e8248150e8fb833943c71f40c7b1724600 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 5 Oct 2012 22:23:51 +0200 Subject: i2c-mux: Add support for device auto-detection Let I2C bus segments behind multiplexers have a class. This allows for device auto-detection on these segments. As long as parent segments don't share the same class, it should be fine. I implemented support in drivers i2c-mux-gpio and i2c-mux-pca954x. I left i2c-mux-pca9541 and i2c-mux-pinctrl alone for the moment as I don't know if this feature makes sense for the use cases of these drivers. Signed-off-by: Jean Delvare Cc: Peter Korsgaard Cc: David Daney Cc: Michael Lawnick Cc: Rodolfo Giometti --- drivers/i2c/i2c-mux.c | 22 ++++++++++++++++++++++ drivers/i2c/muxes/i2c-mux-gpio.c | 4 +++- drivers/i2c/muxes/i2c-mux-pca9541.c | 2 +- drivers/i2c/muxes/i2c-mux-pca954x.c | 10 ++++++---- drivers/i2c/muxes/i2c-mux-pinctrl.c | 2 +- include/linux/i2c-mux-gpio.h | 2 ++ include/linux/i2c-mux.h | 1 + include/linux/i2c/pca954x.h | 1 + 8 files changed, 37 insertions(+), 7 deletions(-) (limited to 'include/linux/i2c') diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 1038c381aea5..d94e0ce78277 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -88,9 +88,23 @@ static u32 i2c_mux_functionality(struct i2c_adapter *adap) return parent->algo->functionality(parent); } +/* Return all parent classes, merged */ +static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent) +{ + unsigned int class = 0; + + do { + class |= parent->class; + parent = i2c_parent_is_i2c_adapter(parent); + } while (parent); + + return class; +} + struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, struct device *mux_dev, void *mux_priv, u32 force_nr, u32 chan_id, + unsigned int class, int (*select) (struct i2c_adapter *, void *, u32), int (*deselect) (struct i2c_adapter *, @@ -127,6 +141,14 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, priv->adap.algo_data = priv; priv->adap.dev.parent = &parent->dev; + /* Sanity check on class */ + if (i2c_mux_parent_classes(parent) & class) + dev_err(&parent->dev, + "Segment %d behind mux can't share classes with ancestors\n", + chan_id); + else + priv->adap.class = class; + /* * Try to populate the mux adapter's of_node, expands to * nothing if !CONFIG_OF. diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c index 68b1f8ec3436..56889e00c76e 100644 --- a/drivers/i2c/muxes/i2c-mux-gpio.c +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -104,8 +104,10 @@ static int __devinit i2c_mux_gpio_probe(struct platform_device *pdev) for (i = 0; i < pdata->n_values; i++) { u32 nr = pdata->base_nr ? (pdata->base_nr + i) : 0; + unsigned int class = pdata->classes ? pdata->classes[i] : 0; - mux->adap[i] = i2c_add_mux_adapter(parent, &pdev->dev, mux, nr, i, + mux->adap[i] = i2c_add_mux_adapter(parent, &pdev->dev, mux, nr, + i, class, i2c_mux_gpio_select, deselect); if (!mux->adap[i]) { ret = -ENODEV; diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index f8f72f39e0b5..f3b8f9a6a89b 100644 --- a/drivers/i2c/muxes/i2c-mux-pca9541.c +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -354,7 +354,7 @@ static int pca9541_probe(struct i2c_client *client, if (pdata) force = pdata->modes[0].adap_id; data->mux_adap = i2c_add_mux_adapter(adap, &client->dev, client, - force, 0, + force, 0, 0, pca9541_select_chan, pca9541_release_chan); diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index f2dfe0d8fcce..8e4387235b69 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -186,7 +186,7 @@ static int pca954x_probe(struct i2c_client *client, { struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); struct pca954x_platform_data *pdata = client->dev.platform_data; - int num, force; + int num, force, class; struct pca954x *data; int ret = -ENODEV; @@ -216,18 +216,20 @@ static int pca954x_probe(struct i2c_client *client, /* Now create an adapter for each channel */ for (num = 0; num < chips[data->type].nchans; num++) { force = 0; /* dynamic adap number */ + class = 0; /* no class by default */ if (pdata) { - if (num < pdata->num_modes) + if (num < pdata->num_modes) { /* force static number */ force = pdata->modes[num].adap_id; - else + class = pdata->modes[num].class; + } else /* discard unconfigured channels */ break; } data->virt_adaps[num] = i2c_add_mux_adapter(adap, &client->dev, client, - force, num, pca954x_select_chan, + force, num, class, pca954x_select_chan, (pdata && pdata->modes[num].deselect_on_exit) ? pca954x_deselect_mux : NULL); diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c index 46a669763476..5f097f309b9f 100644 --- a/drivers/i2c/muxes/i2c-mux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c @@ -221,7 +221,7 @@ static int __devinit i2c_mux_pinctrl_probe(struct platform_device *pdev) (mux->pdata->base_bus_num + i) : 0; mux->busses[i] = i2c_add_mux_adapter(mux->parent, &pdev->dev, - mux, bus, i, + mux, bus, i, 0, i2c_mux_pinctrl_select, deselect); if (!mux->busses[i]) { diff --git a/include/linux/i2c-mux-gpio.h b/include/linux/i2c-mux-gpio.h index a36343a37ebc..4ea1cc7392bd 100644 --- a/include/linux/i2c-mux-gpio.h +++ b/include/linux/i2c-mux-gpio.h @@ -21,6 +21,7 @@ * @values: Array of bitmasks of GPIO settings (low/high) for each * position * @n_values: Number of multiplexer positions (busses to instantiate) + * @classes: Optional I2C auto-detection classes * @gpios: Array of GPIO numbers used to control MUX * @n_gpios: Number of GPIOs used to control MUX * @idle: Bitmask to write to MUX when idle or GPIO_I2CMUX_NO_IDLE if not used @@ -30,6 +31,7 @@ struct i2c_mux_gpio_platform_data { int base_nr; const unsigned *values; int n_values; + const unsigned *classes; const unsigned *gpios; int n_gpios; unsigned idle; diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h index c79083830014..40cb05a97b46 100644 --- a/include/linux/i2c-mux.h +++ b/include/linux/i2c-mux.h @@ -36,6 +36,7 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, struct device *mux_dev, void *mux_priv, u32 force_nr, u32 chan_id, + unsigned int class, int (*select) (struct i2c_adapter *, void *mux_dev, u32 chan_id), int (*deselect) (struct i2c_adapter *, diff --git a/include/linux/i2c/pca954x.h b/include/linux/i2c/pca954x.h index 28f1f8d5ab1f..1712677d5904 100644 --- a/include/linux/i2c/pca954x.h +++ b/include/linux/i2c/pca954x.h @@ -36,6 +36,7 @@ struct pca954x_platform_mode { int adap_id; unsigned int deselect_on_exit:1; + unsigned int class; }; /* Per mux/switch data, used with i2c_register_board_info */ -- cgit v1.2.3 From 6ccbe607132bd823abbad8d32b13af89161707da Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Sep 2012 23:44:25 -0700 Subject: i2c: add Renesas R-Car I2C driver R-Car I2C is similar with SH7760 I2C. But the SH7760 I2C driver had many workaround operations, since H/W had bugs. Thus, it was pointless to keep compatible between SH7760 and R-Car I2C drivers. This patch creates new Renesas R-Car I2C driver. Signed-off-by: Kuninori Morimoto Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-rcar.c | 709 ++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/i2c-rcar.h | 10 + 4 files changed, 730 insertions(+) create mode 100644 drivers/i2c/busses/i2c-rcar.c create mode 100644 include/linux/i2c/i2c-rcar.h (limited to 'include/linux/i2c') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index da77c37caf1e..c8c11b21778b 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -712,6 +712,16 @@ config I2C_XLR This driver can also be built as a module. If so, the module will be called i2c-xlr. +config I2C_RCAR + tristate "Renesas R-Car I2C Controller" + depends on ARCH_SHMOBILE && I2C + help + If you say yes to this option, support will be included for the + R-Car I2C controller. + + This driver can also be built as a module. If so, the module + will be called i2c-rcar. + comment "External I2C/SMBus adapter drivers" config I2C_DIOLAN_U2C diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index ce3c2be7fb40..e98ff51e9d9c 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o +obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o # External I2C/SMBus adapter drivers obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c new file mode 100644 index 000000000000..f9399d163af2 --- /dev/null +++ b/drivers/i2c/busses/i2c-rcar.c @@ -0,0 +1,709 @@ +/* + * drivers/i2c/busses/i2c-rcar.c + * + * Copyright (C) 2012 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This file is based on the drivers/i2c/busses/i2c-sh7760.c + * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss + * + * This file used out-of-tree driver i2c-rcar.c + * Copyright (C) 2011-2012 Renesas Electronics Corporation + * + * 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 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* register offsets */ +#define ICSCR 0x00 /* slave ctrl */ +#define ICMCR 0x04 /* master ctrl */ +#define ICSSR 0x08 /* slave status */ +#define ICMSR 0x0C /* master status */ +#define ICSIER 0x10 /* slave irq enable */ +#define ICMIER 0x14 /* master irq enable */ +#define ICCCR 0x18 /* clock dividers */ +#define ICSAR 0x1C /* slave address */ +#define ICMAR 0x20 /* master address */ +#define ICRXTX 0x24 /* data port */ + +/* ICMCR */ +#define MDBS (1 << 7) /* non-fifo mode switch */ +#define FSCL (1 << 6) /* override SCL pin */ +#define FSDA (1 << 5) /* override SDA pin */ +#define OBPC (1 << 4) /* override pins */ +#define MIE (1 << 3) /* master if enable */ +#define TSBE (1 << 2) +#define FSB (1 << 1) /* force stop bit */ +#define ESG (1 << 0) /* en startbit gen */ + +/* ICMSR */ +#define MNR (1 << 6) /* nack received */ +#define MAL (1 << 5) /* arbitration lost */ +#define MST (1 << 4) /* sent a stop */ +#define MDE (1 << 3) +#define MDT (1 << 2) +#define MDR (1 << 1) +#define MAT (1 << 0) /* slave addr xfer done */ + +/* ICMIE */ +#define MNRE (1 << 6) /* nack irq en */ +#define MALE (1 << 5) /* arblos irq en */ +#define MSTE (1 << 4) /* stop irq en */ +#define MDEE (1 << 3) +#define MDTE (1 << 2) +#define MDRE (1 << 1) +#define MATE (1 << 0) /* address sent irq en */ + + +enum { + RCAR_BUS_PHASE_ADDR, + RCAR_BUS_PHASE_DATA, + RCAR_BUS_PHASE_STOP, +}; + +enum { + RCAR_IRQ_CLOSE, + RCAR_IRQ_OPEN_FOR_SEND, + RCAR_IRQ_OPEN_FOR_RECV, + RCAR_IRQ_OPEN_FOR_STOP, +}; + +/* + * flags + */ +#define ID_LAST_MSG (1 << 0) +#define ID_IOERROR (1 << 1) +#define ID_DONE (1 << 2) +#define ID_ARBLOST (1 << 3) +#define ID_NACK (1 << 4) + +struct rcar_i2c_priv { + void __iomem *io; + struct i2c_adapter adap; + struct i2c_msg *msg; + + spinlock_t lock; + wait_queue_head_t wait; + + int pos; + int irq; + u32 icccr; + u32 flags; +}; + +#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent) +#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD) + +#define rcar_i2c_flags_set(p, f) ((p)->flags |= (f)) +#define rcar_i2c_flags_has(p, f) ((p)->flags & (f)) + +#define LOOP_TIMEOUT 1024 + +/* + * basic functions + */ +static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val) +{ + writel(val, priv->io + reg); +} + +static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg) +{ + return readl(priv->io + reg); +} + +static void rcar_i2c_init(struct rcar_i2c_priv *priv) +{ + /* + * reset slave mode. + * slave mode is not used on this driver + */ + rcar_i2c_write(priv, ICSIER, 0); + rcar_i2c_write(priv, ICSAR, 0); + rcar_i2c_write(priv, ICSCR, 0); + rcar_i2c_write(priv, ICSSR, 0); + + /* reset master mode */ + rcar_i2c_write(priv, ICMIER, 0); + rcar_i2c_write(priv, ICMCR, 0); + rcar_i2c_write(priv, ICMSR, 0); + rcar_i2c_write(priv, ICMAR, 0); +} + +static void rcar_i2c_irq_mask(struct rcar_i2c_priv *priv, int open) +{ + u32 val = MNRE | MALE | MSTE | MATE; /* default */ + + switch (open) { + case RCAR_IRQ_OPEN_FOR_SEND: + val |= MDEE; /* default + send */ + break; + case RCAR_IRQ_OPEN_FOR_RECV: + val |= MDRE; /* default + read */ + break; + case RCAR_IRQ_OPEN_FOR_STOP: + val = MSTE; /* stop irq only */ + break; + case RCAR_IRQ_CLOSE: + default: + val = 0; /* all close */ + break; + } + rcar_i2c_write(priv, ICMIER, val); +} + +static void rcar_i2c_set_addr(struct rcar_i2c_priv *priv, u32 recv) +{ + rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | recv); +} + +/* + * bus control functions + */ +static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv) +{ + int i; + + for (i = 0; i < LOOP_TIMEOUT; i++) { + /* make sure that bus is not busy */ + if (!(rcar_i2c_read(priv, ICMCR) & FSDA)) + return 0; + udelay(1); + } + + return -EBUSY; +} + +static void rcar_i2c_bus_phase(struct rcar_i2c_priv *priv, int phase) +{ + switch (phase) { + case RCAR_BUS_PHASE_ADDR: + rcar_i2c_write(priv, ICMCR, MDBS | MIE | ESG); + break; + case RCAR_BUS_PHASE_DATA: + rcar_i2c_write(priv, ICMCR, MDBS | MIE); + break; + case RCAR_BUS_PHASE_STOP: + rcar_i2c_write(priv, ICMCR, MDBS | MIE | FSB); + break; + } +} + +/* + * clock function + */ +static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv, + u32 bus_speed, + struct device *dev) +{ + struct clk *clkp = clk_get(NULL, "peripheral_clk"); + u32 scgd, cdf; + u32 round, ick; + u32 scl; + + if (!clkp) { + dev_err(dev, "there is no peripheral_clk\n"); + return -EIO; + } + + /* + * calculate SCL clock + * see + * ICCCR + * + * ick = clkp / (1 + CDF) + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick]) + * + * ick : I2C internal clock < 20 MHz + * ticf : I2C SCL falling time = 35 ns here + * tr : I2C SCL rising time = 200 ns here + * intd : LSI internal delay = 50 ns here + * clkp : peripheral_clk + * F[] : integer up-valuation + */ + for (cdf = 0; cdf < 4; cdf++) { + ick = clk_get_rate(clkp) / (1 + cdf); + if (ick < 20000000) + goto ick_find; + } + dev_err(dev, "there is no best CDF\n"); + return -EIO; + +ick_find: + /* + * it is impossible to calculate large scale + * number on u32. separate it + * + * F[(ticf + tr + intd) * ick] + * = F[(35 + 200 + 50)ns * ick] + * = F[285 * ick / 1000000000] + * = F[(ick / 1000000) * 285 / 1000] + */ + round = (ick + 500000) / 1000000 * 285; + round = (round + 500) / 1000; + + /* + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick]) + * + * Calculation result (= SCL) should be less than + * bus_speed for hardware safety + */ + for (scgd = 0; scgd < 0x40; scgd++) { + scl = ick / (20 + (scgd * 8) + round); + if (scl <= bus_speed) + goto scgd_find; + } + dev_err(dev, "it is impossible to calculate best SCL\n"); + return -EIO; + +scgd_find: + dev_dbg(dev, "clk %d/%d(%lu), round %u, CDF:0x%x, SCGD: 0x%x\n", + scl, bus_speed, clk_get_rate(clkp), round, cdf, scgd); + + /* + * keep icccr value + */ + priv->icccr = (scgd << 2 | cdf); + + return 0; +} + +static void rcar_i2c_clock_start(struct rcar_i2c_priv *priv) +{ + rcar_i2c_write(priv, ICCCR, priv->icccr); +} + +/* + * status functions + */ +static u32 rcar_i2c_status_get(struct rcar_i2c_priv *priv) +{ + return rcar_i2c_read(priv, ICMSR); +} + +#define rcar_i2c_status_clear(priv) rcar_i2c_status_bit_clear(priv, 0xffffffff) +static void rcar_i2c_status_bit_clear(struct rcar_i2c_priv *priv, u32 bit) +{ + rcar_i2c_write(priv, ICMSR, ~bit); +} + +/* + * recv/send functions + */ +static int rcar_i2c_recv(struct rcar_i2c_priv *priv) +{ + rcar_i2c_set_addr(priv, 1); + rcar_i2c_status_clear(priv); + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR); + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_RECV); + + return 0; +} + +static int rcar_i2c_send(struct rcar_i2c_priv *priv) +{ + int ret; + + /* + * It should check bus status when send case + */ + ret = rcar_i2c_bus_barrier(priv); + if (ret < 0) + return ret; + + rcar_i2c_set_addr(priv, 0); + rcar_i2c_status_clear(priv); + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR); + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_SEND); + + return 0; +} + +#define rcar_i2c_send_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDE)) +#define rcar_i2c_recv_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDR)) + +/* + * interrupt functions + */ +static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr) +{ + struct i2c_msg *msg = priv->msg; + + /* + * FIXME + * sometimes, unknown interrupt happened. + * Do nothing + */ + if (!(msr & MDE)) + return 0; + + /* + * If address transfer phase finished, + * goto data phase. + */ + if (msr & MAT) + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA); + + if (priv->pos < msg->len) { + /* + * Prepare next data to ICRXTX register. + * This data will go to _SHIFT_ register. + * + * * + * [ICRXTX] -> [SHIFT] -> [I2C bus] + */ + rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]); + priv->pos++; + + } else { + /* + * The last data was pushed to ICRXTX on _PREV_ empty irq. + * It is on _SHIFT_ register, and will sent to I2C bus. + * + * * + * [ICRXTX] -> [SHIFT] -> [I2C bus] + */ + + if (priv->flags & ID_LAST_MSG) + /* + * If current msg is the _LAST_ msg, + * prepare stop condition here. + * ID_DONE will be set on STOP irq. + */ + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP); + else + /* + * If current msg is _NOT_ last msg, + * it doesn't call stop phase. + * thus, there is no STOP irq. + * return ID_DONE here. + */ + return ID_DONE; + } + + rcar_i2c_send_restart(priv); + + return 0; +} + +static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr) +{ + struct i2c_msg *msg = priv->msg; + + /* + * FIXME + * sometimes, unknown interrupt happened. + * Do nothing + */ + if (!(msr & MDR)) + return 0; + + if (msr & MAT) { + /* + * Address transfer phase finished, + * but, there is no data at this point. + * Do nothing. + */ + } else if (priv->pos < msg->len) { + /* + * get received data + */ + msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX); + priv->pos++; + } + + /* + * If next received data is the _LAST_, + * go to STOP phase, + * otherwise, go to DATA phase. + */ + if (priv->pos + 1 >= msg->len) + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP); + else + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA); + + rcar_i2c_recv_restart(priv); + + return 0; +} + +static irqreturn_t rcar_i2c_irq(int irq, void *ptr) +{ + struct rcar_i2c_priv *priv = ptr; + struct device *dev = rcar_i2c_priv_to_dev(priv); + u32 msr; + + /*-------------- spin lock -----------------*/ + spin_lock(&priv->lock); + + msr = rcar_i2c_status_get(priv); + + /* + * Arbitration lost + */ + if (msr & MAL) { + /* + * CAUTION + * + * When arbitration lost, device become _slave_ mode. + */ + dev_dbg(dev, "Arbitration Lost\n"); + rcar_i2c_flags_set(priv, (ID_DONE | ID_ARBLOST)); + goto out; + } + + /* + * Stop + */ + if (msr & MST) { + dev_dbg(dev, "Stop\n"); + rcar_i2c_flags_set(priv, ID_DONE); + goto out; + } + + /* + * Nack + */ + if (msr & MNR) { + dev_dbg(dev, "Nack\n"); + + /* go to stop phase */ + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP); + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_STOP); + rcar_i2c_flags_set(priv, ID_NACK); + goto out; + } + + /* + * recv/send + */ + if (rcar_i2c_is_recv(priv)) + rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr)); + else + rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr)); + +out: + if (rcar_i2c_flags_has(priv, ID_DONE)) { + rcar_i2c_irq_mask(priv, RCAR_IRQ_CLOSE); + rcar_i2c_status_clear(priv); + wake_up(&priv->wait); + } + + spin_unlock(&priv->lock); + /*-------------- spin unlock -----------------*/ + + return IRQ_HANDLED; +} + +static int rcar_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, + int num) +{ + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap); + struct device *dev = rcar_i2c_priv_to_dev(priv); + unsigned long flags; + int i, ret, timeout; + + pm_runtime_get_sync(dev); + + /*-------------- spin lock -----------------*/ + spin_lock_irqsave(&priv->lock, flags); + + rcar_i2c_init(priv); + rcar_i2c_clock_start(priv); + + spin_unlock_irqrestore(&priv->lock, flags); + /*-------------- spin unlock -----------------*/ + + ret = -EINVAL; + for (i = 0; i < num; i++) { + /*-------------- spin lock -----------------*/ + spin_lock_irqsave(&priv->lock, flags); + + /* init each data */ + priv->msg = &msgs[i]; + priv->pos = 0; + priv->flags = 0; + if (priv->msg == &msgs[num - 1]) + rcar_i2c_flags_set(priv, ID_LAST_MSG); + + /* start send/recv */ + if (rcar_i2c_is_recv(priv)) + ret = rcar_i2c_recv(priv); + else + ret = rcar_i2c_send(priv); + + spin_unlock_irqrestore(&priv->lock, flags); + /*-------------- spin unlock -----------------*/ + + if (ret < 0) + break; + + /* + * wait result + */ + timeout = wait_event_timeout(priv->wait, + rcar_i2c_flags_has(priv, ID_DONE), + 5 * HZ); + if (!timeout) { + ret = -ETIMEDOUT; + break; + } + + /* + * error handling + */ + if (rcar_i2c_flags_has(priv, ID_NACK)) { + ret = -EREMOTEIO; + break; + } + + if (rcar_i2c_flags_has(priv, ID_ARBLOST)) { + ret = -EAGAIN; + break; + } + + if (rcar_i2c_flags_has(priv, ID_IOERROR)) { + ret = -EIO; + break; + } + + ret = i + 1; /* The number of transfer */ + } + + pm_runtime_put(dev); + + if (ret < 0) + dev_err(dev, "error %d : %x\n", ret, priv->flags); + + return ret; +} + +static u32 rcar_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm rcar_i2c_algo = { + .master_xfer = rcar_i2c_master_xfer, + .functionality = rcar_i2c_func, +}; + +static int __devinit rcar_i2c_probe(struct platform_device *pdev) +{ + struct i2c_rcar_platform_data *pdata = pdev->dev.platform_data; + struct rcar_i2c_priv *priv; + struct i2c_adapter *adap; + struct resource *res; + struct device *dev = &pdev->dev; + u32 bus_speed; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no mmio resources\n"); + return -ENODEV; + } + + priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL); + if (!priv) { + dev_err(dev, "no mem for private data\n"); + return -ENOMEM; + } + + bus_speed = 100000; /* default 100 kHz */ + if (pdata && pdata->bus_speed) + bus_speed = pdata->bus_speed; + ret = rcar_i2c_clock_calculate(priv, bus_speed, dev); + if (ret < 0) + return ret; + + priv->io = devm_ioremap(dev, res->start, resource_size(res)); + if (!priv->io) { + dev_err(dev, "cannot ioremap\n"); + return -ENODEV; + } + + priv->irq = platform_get_irq(pdev, 0); + init_waitqueue_head(&priv->wait); + spin_lock_init(&priv->lock); + + adap = &priv->adap; + adap->nr = pdev->id; + adap->algo = &rcar_i2c_algo; + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adap->retries = 3; + adap->dev.parent = dev; + i2c_set_adapdata(adap, priv); + strlcpy(adap->name, pdev->name, sizeof(adap->name)); + + ret = devm_request_irq(dev, priv->irq, rcar_i2c_irq, 0, + dev_name(dev), priv); + if (ret < 0) { + dev_err(dev, "cannot get irq %d\n", priv->irq); + return ret; + } + + ret = i2c_add_numbered_adapter(adap); + if (ret < 0) { + dev_err(dev, "reg adap failed: %d\n", ret); + return ret; + } + + pm_runtime_enable(dev); + platform_set_drvdata(pdev, priv); + + dev_info(dev, "probed\n"); + + return 0; +} + +static int __devexit rcar_i2c_remove(struct platform_device *pdev) +{ + struct rcar_i2c_priv *priv = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + i2c_del_adapter(&priv->adap); + pm_runtime_disable(dev); + + return 0; +} + +static struct platform_driver rcar_i2c_drv = { + .driver = { + .name = "i2c-rcar", + .owner = THIS_MODULE, + }, + .probe = rcar_i2c_probe, + .remove = __devexit_p(rcar_i2c_remove), +}; + +module_platform_driver(rcar_i2c_drv); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Renesas R-Car I2C bus driver"); +MODULE_AUTHOR("Kuninori Morimoto "); diff --git a/include/linux/i2c/i2c-rcar.h b/include/linux/i2c/i2c-rcar.h new file mode 100644 index 000000000000..496f5c2b23c9 --- /dev/null +++ b/include/linux/i2c/i2c-rcar.h @@ -0,0 +1,10 @@ +#ifndef __I2C_R_CAR_H__ +#define __I2C_R_CAR_H__ + +#include + +struct i2c_rcar_platform_data { + u32 bus_speed; +}; + +#endif /* __I2C_R_CAR_H__ */ -- cgit v1.2.3