From 760efe6910d5743084b586d3d0a3b65aea96fb2f Mon Sep 17 00:00:00 2001 From: Mac Lin Date: Thu, 25 Nov 2010 23:58:00 +0800 Subject: USB: cns3xxx: Add EHCI and OHCI bus glue for cns3xxx SOCs The CNS3XXX SOC has include USB EHCI and OHCI compatible controllers. This patch adds the necessary glue logic to allow ehci-hcd and ohci-hcd drivers to work on CNS3XXX The EHCI and OHCI controllers share a common clock control and reset bit, therefore additional check for the timming of enabling and disabling is required. The USB bit of PLL Power Down Control is also shared by OTG, 24MHzUART clock, Crypto clock, PCIe reference clock, and Clock Scale Generator. Therefore we only ensure it is enabled, while not disabling it. Signed-off-by: Mac Lin Acked-by: Alan Stern Acked-by: Greg Kroah-Hartman Signed-off-by: Anton Vorontsov --- drivers/usb/Kconfig | 2 + drivers/usb/host/Kconfig | 15 ++++ drivers/usb/host/ehci-cns3xxx.c | 171 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci-hcd.c | 5 ++ drivers/usb/host/ohci-cns3xxx.c | 165 ++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ohci-hcd.c | 5 ++ 6 files changed, 363 insertions(+) create mode 100644 drivers/usb/host/ehci-cns3xxx.c create mode 100644 drivers/usb/host/ohci-cns3xxx.c (limited to 'drivers') diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 67eb3770868..5a7c8f1d76c 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -41,6 +41,7 @@ config USB_ARCH_HAS_OHCI default y if MFD_TC6393XB default y if ARCH_W90X900 default y if ARCH_DAVINCI_DA8XX + default y if ARCH_CNS3XXX # PPC: default y if STB03xxx default y if PPC_MPC52xx @@ -66,6 +67,7 @@ config USB_ARCH_HAS_EHCI default y if ARCH_AT91SAM9G45 default y if ARCH_MXC default y if ARCH_OMAP3 + default y if ARCH_CNS3XXX default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 6f4f8e6a40c..f8970d151d2 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -147,6 +147,14 @@ config USB_W90X900_EHCI ---help--- Enables support for the W90X900 USB controller +config USB_CNS3XXX_EHCI + bool "Cavium CNS3XXX EHCI Module" + depends on USB_EHCI_HCD && ARCH_CNS3XXX + ---help--- + Enable support for the CNS3XXX SOC's on-chip EHCI controller. + It is needed for high-speed (480Mbit/sec) USB 2.0 device + support. + config USB_OXU210HP_HCD tristate "OXU210HP HCD support" depends on USB @@ -286,6 +294,13 @@ config USB_OHCI_HCD_SSB If unsure, say N. +config USB_CNS3XXX_OHCI + bool "Cavium CNS3XXX OHCI Module" + depends on USB_OHCI_HCD && ARCH_CNS3XXX + ---help--- + Enable support for the CNS3XXX SOC's on-chip OHCI controller. + It is needed for low-speed USB 1.0 device support. + config USB_OHCI_BIG_ENDIAN_DESC bool depends on USB_OHCI_HCD diff --git a/drivers/usb/host/ehci-cns3xxx.c b/drivers/usb/host/ehci-cns3xxx.c new file mode 100644 index 00000000000..708a05b5d25 --- /dev/null +++ b/drivers/usb/host/ehci-cns3xxx.c @@ -0,0 +1,171 @@ +/* + * Copyright 2008 Cavium Networks + * + * This file 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 + +static int cns3xxx_ehci_init(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + /* + * EHCI and OHCI share the same clock and power, + * resetting twice would cause the 1st controller been reset. + * Therefore only do power up at the first up device, and + * power down at the last down device. + * + * Set USB AHB INCR length to 16 + */ + if (atomic_inc_return(&usb_pwr_ref) == 1) { + cns3xxx_pwr_power_up(1 << PM_PLL_HM_PD_CTRL_REG_OFFSET_PLL_USB); + cns3xxx_pwr_clk_en(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST); + cns3xxx_pwr_soft_rst(1 << PM_SOFT_RST_REG_OFFST_USB_HOST); + __raw_writel((__raw_readl(MISC_CHIP_CONFIG_REG) | (0X2 << 24)), + MISC_CHIP_CONFIG_REG); + } + + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + hcd->has_tt = 0; + ehci_reset(ehci); + + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci_port_power(ehci, 0); + + return retval; +} + +static const struct hc_driver cns3xxx_ehci_hc_driver = { + .description = hcd_name, + .product_desc = "CNS3XXX EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + .reset = cns3xxx_ehci_init, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + .get_frame_number = ehci_get_frame, + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +#endif + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int cns3xxx_ehci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb_hcd *hcd; + const struct hc_driver *driver = &cns3xxx_ehci_hc_driver; + struct resource *res; + int irq; + int retval; + + if (usb_disabled()) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "Found HC with no IRQ.\n"); + return -ENODEV; + } + irq = res->start; + + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "Found HC with no register addr.\n"); + retval = -ENODEV; + goto err1; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + dev_dbg(dev, "controller already in use\n"); + retval = -EBUSY; + goto err1; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (hcd->regs == NULL) { + dev_dbg(dev, "error mapping memory\n"); + retval = -EFAULT; + goto err2; + } + + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval == 0) + return retval; + + iounmap(hcd->regs); +err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err1: + usb_put_hcd(hcd); + + return retval; +} + +static int cns3xxx_ehci_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + /* + * EHCI and OHCI share the same clock and power, + * resetting twice would cause the 1st controller been reset. + * Therefore only do power up at the first up device, and + * power down at the last down device. + */ + if (atomic_dec_return(&usb_pwr_ref) == 0) + cns3xxx_pwr_clk_dis(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST); + + usb_put_hcd(hcd); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +MODULE_ALIAS("platform:cns3xxx-ehci"); + +static struct platform_driver cns3xxx_ehci_driver = { + .probe = cns3xxx_ehci_probe, + .remove = cns3xxx_ehci_remove, + .driver = { + .name = "cns3xxx-ehci", + }, +}; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 502a7e6fef4..06535405408 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1216,6 +1216,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_octeon_driver #endif +#ifdef CONFIG_USB_CNS3XXX_EHCI +#include "ehci-cns3xxx.c" +#define PLATFORM_DRIVER cns3xxx_ehci_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ !defined(XILINX_OF_PLATFORM_DRIVER) diff --git a/drivers/usb/host/ohci-cns3xxx.c b/drivers/usb/host/ohci-cns3xxx.c new file mode 100644 index 00000000000..f05ef87e934 --- /dev/null +++ b/drivers/usb/host/ohci-cns3xxx.c @@ -0,0 +1,165 @@ +/* + * Copyright 2008 Cavium Networks + * + * This file 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 + +static int __devinit +cns3xxx_ohci_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; + + /* + * EHCI and OHCI share the same clock and power, + * resetting twice would cause the 1st controller been reset. + * Therefore only do power up at the first up device, and + * power down at the last down device. + * + * Set USB AHB INCR length to 16 + */ + if (atomic_inc_return(&usb_pwr_ref) == 1) { + cns3xxx_pwr_power_up(1 << PM_PLL_HM_PD_CTRL_REG_OFFSET_PLL_USB); + cns3xxx_pwr_clk_en(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST); + cns3xxx_pwr_soft_rst(1 << PM_SOFT_RST_REG_OFFST_USB_HOST); + __raw_writel((__raw_readl(MISC_CHIP_CONFIG_REG) | (0X2 << 24)), + MISC_CHIP_CONFIG_REG); + } + + ret = ohci_init(ohci); + if (ret < 0) + return ret; + + ohci->num_ports = 1; + + ret = ohci_run(ohci); + if (ret < 0) { + err("can't start %s", hcd->self.bus_name); + ohci_stop(hcd); + return ret; + } + return 0; +} + +static const struct hc_driver cns3xxx_ohci_hc_driver = { + .description = hcd_name, + .product_desc = "CNS3XXX OHCI Host controller", + .hcd_priv_size = sizeof(struct ohci_hcd), + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + .start = cns3xxx_ohci_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + .get_frame_number = ohci_get_frame, + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + .start_port_reset = ohci_start_port_reset, +}; + +static int cns3xxx_ohci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb_hcd *hcd; + const struct hc_driver *driver = &cns3xxx_ohci_hc_driver; + struct resource *res; + int irq; + int retval; + + if (usb_disabled()) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "Found HC with no IRQ.\n"); + return -ENODEV; + } + irq = res->start; + + hcd = usb_create_hcd(driver, dev, dev_name(dev)); + if (!hcd) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "Found HC with no register addr.\n"); + retval = -ENODEV; + goto err1; + } + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + dev_dbg(dev, "controller already in use\n"); + retval = -EBUSY; + goto err1; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + dev_dbg(dev, "error mapping memory\n"); + retval = -EFAULT; + goto err2; + } + + ohci_hcd_init(hcd_to_ohci(hcd)); + + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval == 0) + return retval; + + iounmap(hcd->regs); +err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err1: + usb_put_hcd(hcd); + return retval; +} + +static int cns3xxx_ohci_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + /* + * EHCI and OHCI share the same clock and power, + * resetting twice would cause the 1st controller been reset. + * Therefore only do power up at the first up device, and + * power down at the last down device. + */ + if (atomic_dec_return(&usb_pwr_ref) == 0) + cns3xxx_pwr_clk_dis(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST); + + usb_put_hcd(hcd); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +MODULE_ALIAS("platform:cns3xxx-ohci"); + +static struct platform_driver ohci_hcd_cns3xxx_driver = { + .probe = cns3xxx_ohci_probe, + .remove = cns3xxx_ohci_remove, + .driver = { + .name = "cns3xxx-ohci", + }, +}; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 5179acb7aa2..5cb6731ba44 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1111,6 +1111,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ohci_octeon_driver #endif +#ifdef CONFIG_USB_CNS3XXX_OHCI +#include "ohci-cns3xxx.c" +#define PLATFORM_DRIVER ohci_hcd_cns3xxx_driver +#endif + #if !defined(PCI_DRIVER) && \ !defined(PLATFORM_DRIVER) && \ !defined(OMAP1_PLATFORM_DRIVER) && \ -- cgit v1.2.3-70-g09d2 From 5b28aa319bba96987316425a1131813d87cbab35 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 6 Oct 2010 15:41:15 +0200 Subject: dmaengine i.MX SDMA: Allow to run without firmware The SDMA firmware consists of a ROM part and a RAM part. The ROM part is always present in the SDMA engine and is sufficient for many cases. This patch allows to pass in platform data containing the script addresses in ROM, so loading a firmware is optional now. Signed-off-by: Sascha Hauer Acked-by: Dan Williams --- arch/arm/plat-mxc/include/mach/sdma.h | 46 +++++++++ drivers/dma/imx-sdma.c | 174 +++++++++++++++------------------- 2 files changed, 124 insertions(+), 96 deletions(-) (limited to 'drivers') diff --git a/arch/arm/plat-mxc/include/mach/sdma.h b/arch/arm/plat-mxc/include/mach/sdma.h index 9be112227ac..913e0432e40 100644 --- a/arch/arm/plat-mxc/include/mach/sdma.h +++ b/arch/arm/plat-mxc/include/mach/sdma.h @@ -1,17 +1,63 @@ #ifndef __MACH_MXC_SDMA_H__ #define __MACH_MXC_SDMA_H__ +/** + * struct sdma_script_start_addrs - SDMA script start pointers + * + * start addresses of the different functions in the physical + * address space of the SDMA engine. + */ +struct sdma_script_start_addrs { + s32 ap_2_ap_addr; + s32 ap_2_bp_addr; + s32 ap_2_ap_fixed_addr; + s32 bp_2_ap_addr; + s32 loopback_on_dsp_side_addr; + s32 mcu_interrupt_only_addr; + s32 firi_2_per_addr; + s32 firi_2_mcu_addr; + s32 per_2_firi_addr; + s32 mcu_2_firi_addr; + s32 uart_2_per_addr; + s32 uart_2_mcu_addr; + s32 per_2_app_addr; + s32 mcu_2_app_addr; + s32 per_2_per_addr; + s32 uartsh_2_per_addr; + s32 uartsh_2_mcu_addr; + s32 per_2_shp_addr; + s32 mcu_2_shp_addr; + s32 ata_2_mcu_addr; + s32 mcu_2_ata_addr; + s32 app_2_per_addr; + s32 app_2_mcu_addr; + s32 shp_2_per_addr; + s32 shp_2_mcu_addr; + s32 mshc_2_mcu_addr; + s32 mcu_2_mshc_addr; + s32 spdif_2_mcu_addr; + s32 mcu_2_spdif_addr; + s32 asrc_2_mcu_addr; + s32 ext_mem_2_ipu_addr; + s32 descrambler_addr; + s32 dptc_dvfs_addr; + s32 utra_addr; + s32 ram_code_start_addr; +}; + /** * struct sdma_platform_data - platform specific data for SDMA engine * * @sdma_version The version of this SDMA engine * @cpu_name used to generate the firmware name * @to_version CPU Tape out version + * @script_addrs SDMA scripts addresses in SDMA ROM */ struct sdma_platform_data { int sdma_version; char *cpu_name; int to_version; + struct sdma_script_start_addrs *script_addrs; }; #endif /* __MACH_MXC_SDMA_H__ */ diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 0834323a059..01166a9c324 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -273,50 +273,6 @@ struct sdma_channel { #define MXC_SDMA_MIN_PRIORITY 1 #define MXC_SDMA_MAX_PRIORITY 7 -/** - * struct sdma_script_start_addrs - SDMA script start pointers - * - * start addresses of the different functions in the physical - * address space of the SDMA engine. - */ -struct sdma_script_start_addrs { - u32 ap_2_ap_addr; - u32 ap_2_bp_addr; - u32 ap_2_ap_fixed_addr; - u32 bp_2_ap_addr; - u32 loopback_on_dsp_side_addr; - u32 mcu_interrupt_only_addr; - u32 firi_2_per_addr; - u32 firi_2_mcu_addr; - u32 per_2_firi_addr; - u32 mcu_2_firi_addr; - u32 uart_2_per_addr; - u32 uart_2_mcu_addr; - u32 per_2_app_addr; - u32 mcu_2_app_addr; - u32 per_2_per_addr; - u32 uartsh_2_per_addr; - u32 uartsh_2_mcu_addr; - u32 per_2_shp_addr; - u32 mcu_2_shp_addr; - u32 ata_2_mcu_addr; - u32 mcu_2_ata_addr; - u32 app_2_per_addr; - u32 app_2_mcu_addr; - u32 shp_2_per_addr; - u32 shp_2_mcu_addr; - u32 mshc_2_mcu_addr; - u32 mcu_2_mshc_addr; - u32 spdif_2_mcu_addr; - u32 mcu_2_spdif_addr; - u32 asrc_2_mcu_addr; - u32 ext_mem_2_ipu_addr; - u32 descrambler_addr; - u32 dptc_dvfs_addr; - u32 utra_addr; - u32 ram_code_start_addr; -}; - #define SDMA_FIRMWARE_MAGIC 0x414d4453 /** @@ -1127,8 +1083,74 @@ static void sdma_issue_pending(struct dma_chan *chan) */ } -static int __init sdma_init(struct sdma_engine *sdma, - void *ram_code, int ram_code_size) +#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 34 + +static void sdma_add_scripts(struct sdma_engine *sdma, + const struct sdma_script_start_addrs *addr) +{ + s32 *addr_arr = (u32 *)addr; + s32 *saddr_arr = (u32 *)sdma->script_addrs; + int i; + + for (i = 0; i < SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1; i++) + if (addr_arr[i] > 0) + saddr_arr[i] = addr_arr[i]; +} + +static int __init sdma_get_firmware(struct sdma_engine *sdma, + const char *cpu_name, int to_version) +{ + const struct firmware *fw; + char *fwname; + const struct sdma_firmware_header *header; + int ret; + const struct sdma_script_start_addrs *addr; + unsigned short *ram_code; + + fwname = kasprintf(GFP_KERNEL, "sdma-%s-to%d.bin", cpu_name, to_version); + if (!fwname) + return -ENOMEM; + + ret = request_firmware(&fw, fwname, sdma->dev); + if (ret) { + kfree(fwname); + return ret; + } + kfree(fwname); + + if (fw->size < sizeof(*header)) + goto err_firmware; + + header = (struct sdma_firmware_header *)fw->data; + + if (header->magic != SDMA_FIRMWARE_MAGIC) + goto err_firmware; + if (header->ram_code_start + header->ram_code_size > fw->size) + goto err_firmware; + + addr = (void *)header + header->script_addrs_start; + ram_code = (void *)header + header->ram_code_start; + + clk_enable(sdma->clk); + /* download the RAM image for SDMA */ + sdma_load_script(sdma, ram_code, + header->ram_code_size, + sdma->script_addrs->ram_code_start_addr); + clk_disable(sdma->clk); + + sdma_add_scripts(sdma, addr); + + dev_info(sdma->dev, "loaded firmware %d.%d\n", + header->version_major, + header->version_minor); + +err_firmware: + release_firmware(fw); + + return ret; +} + +static int __init sdma_init(struct sdma_engine *sdma) { int i, ret; dma_addr_t ccb_phys; @@ -1192,11 +1214,6 @@ static int __init sdma_init(struct sdma_engine *sdma, __raw_writel(ccb_phys, sdma->regs + SDMA_H_C0PTR); - /* download the RAM image for SDMA */ - sdma_load_script(sdma, ram_code, - ram_code_size, - sdma->script_addrs->ram_code_start_addr); - /* Set bits of CONFIG register with given context switching mode */ __raw_writel(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG); @@ -1216,14 +1233,9 @@ err_dma_alloc: static int __init sdma_probe(struct platform_device *pdev) { int ret; - const struct firmware *fw; - const struct sdma_firmware_header *header; - const struct sdma_script_start_addrs *addr; int irq; - unsigned short *ram_code; struct resource *iores; struct sdma_platform_data *pdata = pdev->dev.platform_data; - char *fwname; int i; dma_cap_mask_t mask; struct sdma_engine *sdma; @@ -1262,38 +1274,9 @@ static int __init sdma_probe(struct platform_device *pdev) if (ret) goto err_request_irq; - fwname = kasprintf(GFP_KERNEL, "sdma-%s-to%d.bin", - pdata->cpu_name, pdata->to_version); - if (!fwname) { - ret = -ENOMEM; - goto err_cputype; - } - - ret = request_firmware(&fw, fwname, &pdev->dev); - if (ret) { - dev_err(&pdev->dev, "request firmware \"%s\" failed with %d\n", - fwname, ret); - kfree(fwname); - goto err_cputype; - } - kfree(fwname); - - if (fw->size < sizeof(*header)) - goto err_firmware; - - header = (struct sdma_firmware_header *)fw->data; - - if (header->magic != SDMA_FIRMWARE_MAGIC) - goto err_firmware; - if (header->ram_code_start + header->ram_code_size > fw->size) - goto err_firmware; - - addr = (void *)header + header->script_addrs_start; - ram_code = (void *)header + header->ram_code_start; - sdma->script_addrs = kmalloc(sizeof(*addr), GFP_KERNEL); + sdma->script_addrs = kzalloc(sizeof(*sdma->script_addrs), GFP_KERNEL); if (!sdma->script_addrs) - goto err_firmware; - memcpy(sdma->script_addrs, addr, sizeof(*addr)); + goto err_alloc; sdma->version = pdata->sdma_version; @@ -1316,10 +1299,15 @@ static int __init sdma_probe(struct platform_device *pdev) list_add_tail(&sdmac->chan.device_node, &sdma->dma_device.channels); } - ret = sdma_init(sdma, ram_code, header->ram_code_size); + ret = sdma_init(sdma); if (ret) goto err_init; + if (pdata->script_addrs) + sdma_add_scripts(sdma, pdata->script_addrs); + + sdma_get_firmware(sdma, pdata->cpu_name, pdata->to_version); + sdma->dma_device.dev = &pdev->dev; sdma->dma_device.device_alloc_chan_resources = sdma_alloc_chan_resources; @@ -1336,10 +1324,6 @@ static int __init sdma_probe(struct platform_device *pdev) goto err_init; } - dev_info(&pdev->dev, "initialized (firmware %d.%d)\n", - header->version_major, - header->version_minor); - /* request channel 0. This is an internal control channel * to the SDMA engine and not available to clients. */ @@ -1347,15 +1331,13 @@ static int __init sdma_probe(struct platform_device *pdev) dma_cap_set(DMA_SLAVE, mask); dma_request_channel(mask, NULL, NULL); - release_firmware(fw); + dev_info(sdma->dev, "initialized\n"); return 0; err_init: kfree(sdma->script_addrs); -err_firmware: - release_firmware(fw); -err_cputype: +err_alloc: free_irq(irq, sdma); err_request_irq: iounmap(sdma->regs); -- cgit v1.2.3-70-g09d2 From 29bb6afcb077bd153c7738e73840dde808132fb5 Mon Sep 17 00:00:00 2001 From: Eric Bénard Date: Sat, 27 Nov 2010 09:15:38 +0100 Subject: plat-mxc/ehci.c: fix compile breakage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commits 2eb42d5c287f5e883a4b3ebe668ba880caa351e5 and 9e1dde33876ba83ad586c336647fff133d0f5472 renamed some defines but didn't fix all the places where these defines are used leading to a compile failure for USB on i.MX31, 35 and 27. Signed-off-by: Eric Bénard Signed-off-by: Sascha Hauer --- arch/arm/plat-mxc/ehci.c | 12 ++++++------ drivers/usb/gadget/fsl_mxc_udc.c | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/arch/arm/plat-mxc/ehci.c b/arch/arm/plat-mxc/ehci.c index 4bac3d5545d..b79c8090197 100644 --- a/arch/arm/plat-mxc/ehci.c +++ b/arch/arm/plat-mxc/ehci.c @@ -115,7 +115,7 @@ int mxc_initialize_usb_hw(int port, unsigned int flags) #endif /* if defined(CONFIG_SOC_IMX25) */ #if defined(CONFIG_ARCH_MX3) if (cpu_is_mx31()) { - v = readl(MX31_IO_ADDRESS(MX31_OTG_BASE_ADDR + + v = readl(MX31_IO_ADDRESS(MX31_USB_BASE_ADDR + USBCTRL_OTGBASE_OFFSET)); switch (port) { @@ -153,13 +153,13 @@ int mxc_initialize_usb_hw(int port, unsigned int flags) return -EINVAL; } - writel(v, MX31_IO_ADDRESS(MX31_OTG_BASE_ADDR + + writel(v, MX31_IO_ADDRESS(MX31_USB_BASE_ADDR + USBCTRL_OTGBASE_OFFSET)); return 0; } if (cpu_is_mx35()) { - v = readl(MX35_IO_ADDRESS(MX35_OTG_BASE_ADDR + + v = readl(MX35_IO_ADDRESS(MX35_USB_BASE_ADDR + USBCTRL_OTGBASE_OFFSET)); switch (port) { @@ -196,7 +196,7 @@ int mxc_initialize_usb_hw(int port, unsigned int flags) return -EINVAL; } - writel(v, MX35_IO_ADDRESS(MX35_OTG_BASE_ADDR + + writel(v, MX35_IO_ADDRESS(MX35_USB_BASE_ADDR + USBCTRL_OTGBASE_OFFSET)); return 0; } @@ -206,7 +206,7 @@ int mxc_initialize_usb_hw(int port, unsigned int flags) /* On i.MX27 we can use the i.MX31 USBCTRL bits, they * are identical */ - v = readl(MX27_IO_ADDRESS(MX27_OTG_BASE_ADDR + + v = readl(MX27_IO_ADDRESS(MX27_USB_BASE_ADDR + USBCTRL_OTGBASE_OFFSET)); switch (port) { case 0: /* OTG port */ @@ -241,7 +241,7 @@ int mxc_initialize_usb_hw(int port, unsigned int flags) default: return -EINVAL; } - writel(v, MX27_IO_ADDRESS(MX27_OTG_BASE_ADDR + + writel(v, MX27_IO_ADDRESS(MX27_USB_BASE_ADDR + USBCTRL_OTGBASE_OFFSET)); return 0; } diff --git a/drivers/usb/gadget/fsl_mxc_udc.c b/drivers/usb/gadget/fsl_mxc_udc.c index 5bdbfe61985..77b1eb57702 100644 --- a/drivers/usb/gadget/fsl_mxc_udc.c +++ b/drivers/usb/gadget/fsl_mxc_udc.c @@ -93,9 +93,9 @@ void fsl_udc_clk_finalize(struct platform_device *pdev) /* workaround ENGcm09152 for i.MX35 */ if (pdata->workaround & FLS_USB2_WORKAROUND_ENGCM09152) { - v = readl(MX35_IO_ADDRESS(MX35_OTG_BASE_ADDR + + v = readl(MX35_IO_ADDRESS(MX35_USB_BASE_ADDR + USBPHYCTRL_OTGBASE_OFFSET)); - writel(v | USBPHYCTRL_EVDO, MX35_IO_ADDRESS(MX35_OTG_BASE_ADDR + + writel(v | USBPHYCTRL_EVDO, MX35_IO_ADDRESS(MX35_USB_BASE_ADDR + USBPHYCTRL_OTGBASE_OFFSET)); } #endif -- cgit v1.2.3-70-g09d2 From 2c1f4672f0711e7f23ae49cbb7541088126fe576 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 7 Dec 2010 14:16:04 -0200 Subject: watchdog: imx: use clk_get to acquire the watchdog clock Use clk_get to acquire the watchdog clock and also avoid hardcoding the clock name. Signed-off-by: Fabio Estevam Signed-off-by: Sascha Hauer --- arch/arm/mach-imx/clock-imx21.c | 2 +- arch/arm/mach-imx/clock-imx25.c | 2 +- arch/arm/mach-imx/clock-imx27.c | 2 +- arch/arm/mach-mx3/clock-imx31.c | 2 +- arch/arm/mach-mx3/clock-imx35.c | 2 +- arch/arm/mach-mx5/clock-mx51-mx53.c | 4 ++-- arch/arm/plat-mxc/system.c | 2 +- drivers/watchdog/imx2_wdt.c | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-imx/clock-imx21.c b/arch/arm/mach-imx/clock-imx21.c index cf15ea516a7..c63a4f5ffcb 100644 --- a/arch/arm/mach-imx/clock-imx21.c +++ b/arch/arm/mach-imx/clock-imx21.c @@ -1185,7 +1185,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "brom", brom_clk) _REGISTER_CLOCK(NULL, "emma", emma_clk[0]) _REGISTER_CLOCK(NULL, "slcdc", slcdc_clk[0]) - _REGISTER_CLOCK("imx-wdt.0", NULL, wdog_clk) + _REGISTER_CLOCK("imx2-wdt.0", NULL, wdog_clk) _REGISTER_CLOCK(NULL, "gpio", gpio_clk) _REGISTER_CLOCK("imx-i2c.0", NULL, i2c_clk) _REGISTER_CLOCK("mxc-keypad", NULL, kpp_clk) diff --git a/arch/arm/mach-imx/clock-imx25.c b/arch/arm/mach-imx/clock-imx25.c index 9e4a5578c2f..21ef34c501e 100644 --- a/arch/arm/mach-imx/clock-imx25.c +++ b/arch/arm/mach-imx/clock-imx25.c @@ -296,7 +296,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK("fec.0", NULL, fec_clk) _REGISTER_CLOCK("imxdi_rtc.0", NULL, dryice_clk) _REGISTER_CLOCK("imx-fb.0", NULL, lcdc_clk) - _REGISTER_CLOCK("imx-wdt.0", NULL, wdt_clk) + _REGISTER_CLOCK("imx2-wdt.0", NULL, wdt_clk) _REGISTER_CLOCK("imx-ssi.0", NULL, ssi1_clk) _REGISTER_CLOCK("imx-ssi.1", NULL, ssi2_clk) _REGISTER_CLOCK("sdhci-esdhc-imx.0", NULL, esdhc1_clk) diff --git a/arch/arm/mach-imx/clock-imx27.c b/arch/arm/mach-imx/clock-imx27.c index 2202b88667b..f32f3b8e8ba 100644 --- a/arch/arm/mach-imx/clock-imx27.c +++ b/arch/arm/mach-imx/clock-imx27.c @@ -667,7 +667,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "sahara2", sahara2_clk) _REGISTER_CLOCK(NULL, "ata", ata_clk) _REGISTER_CLOCK(NULL, "mstick", mstick_clk) - _REGISTER_CLOCK("imx-wdt.0", NULL, wdog_clk) + _REGISTER_CLOCK("imx2-wdt.0", NULL, wdog_clk) _REGISTER_CLOCK(NULL, "gpio", gpio_clk) _REGISTER_CLOCK("imx-i2c.0", NULL, i2c1_clk) _REGISTER_CLOCK("imx-i2c.1", NULL, i2c2_clk) diff --git a/arch/arm/mach-mx3/clock-imx31.c b/arch/arm/mach-mx3/clock-imx31.c index 7cf6d29f376..4193cf5a263 100644 --- a/arch/arm/mach-mx3/clock-imx31.c +++ b/arch/arm/mach-mx3/clock-imx31.c @@ -530,7 +530,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK("imx31-cspi.2", NULL, cspi3_clk) _REGISTER_CLOCK(NULL, "gpt", gpt_clk) _REGISTER_CLOCK(NULL, "pwm", pwm_clk) - _REGISTER_CLOCK("imx-wdt.0", NULL, wdog_clk) + _REGISTER_CLOCK("imx2-wdt.0", NULL, wdog_clk) _REGISTER_CLOCK(NULL, "rtc", rtc_clk) _REGISTER_CLOCK(NULL, "epit", epit1_clk) _REGISTER_CLOCK(NULL, "epit", epit2_clk) diff --git a/arch/arm/mach-mx3/clock-imx35.c b/arch/arm/mach-mx3/clock-imx35.c index 61e4a318980..22259d95583 100644 --- a/arch/arm/mach-mx3/clock-imx35.c +++ b/arch/arm/mach-mx3/clock-imx35.c @@ -495,7 +495,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK("mxc-ehci.2", "usb", usbotg_clk) _REGISTER_CLOCK("fsl-usb2-udc", "usb", usbotg_clk) _REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", usbahb_clk) - _REGISTER_CLOCK("imx-wdt.0", NULL, wdog_clk) + _REGISTER_CLOCK("imx2-wdt.0", NULL, wdog_clk) _REGISTER_CLOCK(NULL, "max", max_clk) _REGISTER_CLOCK(NULL, "audmux", audmux_clk) _REGISTER_CLOCK(NULL, "csi", csi_clk) diff --git a/arch/arm/mach-mx5/clock-mx51-mx53.c b/arch/arm/mach-mx5/clock-mx51-mx53.c index b04d54daddb..ed26de68a99 100644 --- a/arch/arm/mach-mx5/clock-mx51-mx53.c +++ b/arch/arm/mach-mx5/clock-mx51-mx53.c @@ -1110,8 +1110,8 @@ static struct clk_lookup mx51_lookups[] = { _REGISTER_CLOCK("sdhci-esdhc-imx.1", NULL, esdhc2_clk) _REGISTER_CLOCK(NULL, "cpu_clk", cpu_clk) _REGISTER_CLOCK(NULL, "iim_clk", iim_clk) - _REGISTER_CLOCK("imx-wdt.0", NULL, dummy_clk) - _REGISTER_CLOCK("imx-wdt.1", NULL, dummy_clk) + _REGISTER_CLOCK("imx2-wdt.0", NULL, dummy_clk) + _REGISTER_CLOCK("imx2-wdt.1", NULL, dummy_clk) }; static struct clk_lookup mx53_lookups[] = { diff --git a/arch/arm/plat-mxc/system.c b/arch/arm/plat-mxc/system.c index c3972c58384..3455fc0575a 100644 --- a/arch/arm/plat-mxc/system.c +++ b/arch/arm/plat-mxc/system.c @@ -55,7 +55,7 @@ void arch_reset(char mode, const char *cmd) } else { struct clk *clk; - clk = clk_get_sys("imx-wdt.0", NULL); + clk = clk_get_sys("imx2-wdt.0", NULL); if (!IS_ERR(clk)) clk_enable(clk); wcr_enable = (1 << 2); diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 2ee7dac55a3..86f7cac1026 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -270,7 +270,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) return -ENOMEM; } - imx2_wdt.clk = clk_get_sys("imx-wdt.0", NULL); + imx2_wdt.clk = clk_get(&pdev->dev, NULL); if (IS_ERR(imx2_wdt.clk)) { dev_err(&pdev->dev, "can't get Watchdog clock\n"); return PTR_ERR(imx2_wdt.clk); -- cgit v1.2.3-70-g09d2 From ad68bb9f7a3cd47396635a5e3895215af57579da Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 3 Nov 2010 16:29:35 +0100 Subject: ARM: pxa: Access SMEMC via virtual addresses This is important because on PXA3xx, the physical mapping of SMEMC registers differs from the one on PXA2xx. In order to get PCMCIA working on both PXA2xx and PXA320, the PCMCIA driver was adjusted accordingly as well. Also, various places in the kernel had to be patched to use __raw_read/__raw_write. Signed-off-by: Marek Vasut Acked-by: Haojian Zhuang Signed-off-by: Eric Miao --- arch/arm/mach-pxa/cm-x2xx.c | 13 ++--- arch/arm/mach-pxa/cpufreq-pxa2xx.c | 10 ++-- arch/arm/mach-pxa/csb726.c | 7 +-- arch/arm/mach-pxa/generic.c | 1 + arch/arm/mach-pxa/h5000.c | 9 ++-- arch/arm/mach-pxa/include/mach/pxa2xx-regs.h | 55 --------------------- arch/arm/mach-pxa/include/mach/smemc.h | 74 ++++++++++++++++++++++++++++ arch/arm/mach-pxa/lpd270.c | 3 +- arch/arm/mach-pxa/lubbock.c | 3 +- arch/arm/mach-pxa/mainstone.c | 3 +- arch/arm/mach-pxa/pxa25x.c | 5 +- arch/arm/mach-pxa/pxa27x.c | 11 +++-- arch/arm/mach-pxa/pxa3xx.c | 5 +- arch/arm/mach-pxa/sleep.S | 2 +- arch/arm/mach-pxa/smemc.c | 51 +++++++------------ arch/arm/mach-pxa/spitz.c | 6 ++- arch/arm/mach-pxa/stargate2.c | 3 +- arch/arm/mach-pxa/tosa.c | 7 ++- arch/arm/mach-pxa/trizeps4.c | 3 +- arch/arm/mach-pxa/xcep.c | 5 +- arch/arm/mach-pxa/zeus.c | 8 ++- drivers/pcmcia/pxa2xx_base.c | 28 ++++++++--- 22 files changed, 176 insertions(+), 136 deletions(-) create mode 100644 arch/arm/mach-pxa/include/mach/smemc.h (limited to 'drivers') diff --git a/arch/arm/mach-pxa/cm-x2xx.c b/arch/arm/mach-pxa/cm-x2xx.c index 47e242005fa..b734d846816 100644 --- a/arch/arm/mach-pxa/cm-x2xx.c +++ b/arch/arm/mach-pxa/cm-x2xx.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -392,9 +393,9 @@ static int cmx2xx_suspend(struct sys_device *dev, pm_message_t state) cmx2xx_pci_suspend(); /* save MSC registers */ - sleep_save_msc[0] = MSC0; - sleep_save_msc[1] = MSC1; - sleep_save_msc[2] = MSC2; + sleep_save_msc[0] = __raw_readl(MSC0); + sleep_save_msc[1] = __raw_readl(MSC1); + sleep_save_msc[2] = __raw_readl(MSC2); /* setup power saving mode registers */ PCFR = 0x0; @@ -416,9 +417,9 @@ static int cmx2xx_resume(struct sys_device *dev) cmx2xx_pci_resume(); /* restore MSC registers */ - MSC0 = sleep_save_msc[0]; - MSC1 = sleep_save_msc[1]; - MSC2 = sleep_save_msc[2]; + __raw_writel(sleep_save_msc[0], MSC0); + __raw_writel(sleep_save_msc[1], MSC1); + __raw_writel(sleep_save_msc[2], MSC2); return 0; } diff --git a/arch/arm/mach-pxa/cpufreq-pxa2xx.c b/arch/arm/mach-pxa/cpufreq-pxa2xx.c index 58093d9e07b..6a7aeab42f6 100644 --- a/arch/arm/mach-pxa/cpufreq-pxa2xx.c +++ b/arch/arm/mach-pxa/cpufreq-pxa2xx.c @@ -38,8 +38,10 @@ #include #include #include +#include #include +#include #ifdef DEBUG static unsigned int freq_debug; @@ -242,7 +244,7 @@ static void pxa27x_guess_max_freq(void) static void init_sdram_rows(void) { - uint32_t mdcnfg = MDCNFG; + uint32_t mdcnfg = __raw_readl(MDCNFG); unsigned int drac2 = 0, drac0 = 0; if (mdcnfg & (MDCNFG_DE2 | MDCNFG_DE3)) @@ -331,8 +333,8 @@ static int pxa_set_target(struct cpufreq_policy *policy, * we need to preset the smaller DRI before the change. If we're * speeding up we need to set the larger DRI value after the change. */ - preset_mdrefr = postset_mdrefr = MDREFR; - if ((MDREFR & MDREFR_DRI_MASK) > mdrefr_dri(new_freq_mem)) { + preset_mdrefr = postset_mdrefr = __raw_readl(MDREFR); + if ((preset_mdrefr & MDREFR_DRI_MASK) > mdrefr_dri(new_freq_mem)) { preset_mdrefr = (preset_mdrefr & ~MDREFR_DRI_MASK); preset_mdrefr |= mdrefr_dri(new_freq_mem); } @@ -370,7 +372,7 @@ static int pxa_set_target(struct cpufreq_policy *policy, 3: nop \n\ " : "=&r" (unused) - : "r" (&MDREFR), "r" (cclkcfg), + : "r" (MDREFR), "r" (cclkcfg), "r" (preset_mdrefr), "r" (postset_mdrefr) : "r4", "r5"); local_irq_restore(flags); diff --git a/arch/arm/mach-pxa/csb726.c b/arch/arm/mach-pxa/csb726.c index 225a2a11781..a305424a967 100644 --- a/arch/arm/mach-pxa/csb726.c +++ b/arch/arm/mach-pxa/csb726.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "generic.h" #include "devices.h" @@ -255,9 +256,9 @@ static struct platform_device *devices[] __initdata = { static void __init csb726_init(void) { pxa2xx_mfp_config(ARRAY_AND_SIZE(csb726_pin_config)); -/* MSC1 = 0x7ffc3ffc; *//* LAN9215/EXP_CS */ -/* MSC2 = 0x06697ff4; *//* none/SM501 */ - MSC2 = (MSC2 & ~0xffff) | 0x7ff4; /* SM501 */ +/* __raw_writel(0x7ffc3ffc, MSC1); *//* LAN9215/EXP_CS */ +/* __raw_writel(0x06697ff4, MSC2); *//* none/SM501 */ + __raw_writel((__raw_readl(MSC2) & ~0xffff) | 0x7ff4, MSC2); /* SM501 */ pxa_set_ffuart_info(NULL); pxa_set_btuart_info(NULL); diff --git a/arch/arm/mach-pxa/generic.c b/arch/arm/mach-pxa/generic.c index 6a5f3793537..d4ce8f9233d 100644 --- a/arch/arm/mach-pxa/generic.c +++ b/arch/arm/mach-pxa/generic.c @@ -28,6 +28,7 @@ #include #include +#include #include "generic.h" diff --git a/arch/arm/mach-pxa/h5000.c b/arch/arm/mach-pxa/h5000.c index 0ef16351bf9..657db469de1 100644 --- a/arch/arm/mach-pxa/h5000.c +++ b/arch/arm/mach-pxa/h5000.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "generic.h" @@ -172,11 +173,11 @@ static unsigned long h5000_pin_config[] __initdata = { static void fix_msc(void) { - MSC0 = 0x129c24f2; - MSC1 = 0x7ff424fa; - MSC2 = 0x7ff47ff4; + __raw_writel(0x129c24f2, MSC0); + __raw_writel(0x7ff424fa, MSC1); + __raw_writel(0x7ff47ff4, MSC2); - MDREFR |= 0x02080000; + __raw_writel(__raw_readl(MDREFR) | 0x02080000, MDREFR); } /* diff --git a/arch/arm/mach-pxa/include/mach/pxa2xx-regs.h b/arch/arm/mach-pxa/include/mach/pxa2xx-regs.h index dd0fc1c95cc..ee6ced1cea7 100644 --- a/arch/arm/mach-pxa/include/mach/pxa2xx-regs.h +++ b/arch/arm/mach-pxa/include/mach/pxa2xx-regs.h @@ -16,61 +16,6 @@ #include -/* - * Memory controller - */ - -#define MDCNFG __REG(0x48000000) /* SDRAM Configuration Register 0 */ -#define MDREFR __REG(0x48000004) /* SDRAM Refresh Control Register */ -#define MSC0 __REG(0x48000008) /* Static Memory Control Register 0 */ -#define MSC1 __REG(0x4800000C) /* Static Memory Control Register 1 */ -#define MSC2 __REG(0x48000010) /* Static Memory Control Register 2 */ -#define MECR __REG(0x48000014) /* Expansion Memory (PCMCIA/Compact Flash) Bus Configuration */ -#define SXLCR __REG(0x48000018) /* LCR value to be written to SDRAM-Timing Synchronous Flash */ -#define SXCNFG __REG(0x4800001C) /* Synchronous Static Memory Control Register */ -#define SXMRS __REG(0x48000024) /* MRS value to be written to Synchronous Flash or SMROM */ -#define MCMEM0 __REG(0x48000028) /* Card interface Common Memory Space Socket 0 Timing */ -#define MCMEM1 __REG(0x4800002C) /* Card interface Common Memory Space Socket 1 Timing */ -#define MCATT0 __REG(0x48000030) /* Card interface Attribute Space Socket 0 Timing Configuration */ -#define MCATT1 __REG(0x48000034) /* Card interface Attribute Space Socket 1 Timing Configuration */ -#define MCIO0 __REG(0x48000038) /* Card interface I/O Space Socket 0 Timing Configuration */ -#define MCIO1 __REG(0x4800003C) /* Card interface I/O Space Socket 1 Timing Configuration */ -#define MDMRS __REG(0x48000040) /* MRS value to be written to SDRAM */ -#define BOOT_DEF __REG(0x48000044) /* Read-Only Boot-Time Register. Contains BOOT_SEL and PKG_SEL */ - -/* - * More handy macros for PCMCIA - * - * Arg is socket number - */ -#define MCMEM(s) __REG2(0x48000028, (s)<<2 ) /* Card interface Common Memory Space Socket s Timing */ -#define MCATT(s) __REG2(0x48000030, (s)<<2 ) /* Card interface Attribute Space Socket s Timing Configuration */ -#define MCIO(s) __REG2(0x48000038, (s)<<2 ) /* Card interface I/O Space Socket s Timing Configuration */ - -/* MECR register defines */ -#define MECR_NOS (1 << 0) /* Number Of Sockets: 0 -> 1 sock, 1 -> 2 sock */ -#define MECR_CIT (1 << 1) /* Card Is There: 0 -> no card, 1 -> card inserted */ - -#define MDCNFG_DE0 (1 << 0) /* SDRAM Bank 0 Enable */ -#define MDCNFG_DE1 (1 << 1) /* SDRAM Bank 1 Enable */ -#define MDCNFG_DE2 (1 << 16) /* SDRAM Bank 2 Enable */ -#define MDCNFG_DE3 (1 << 17) /* SDRAM Bank 3 Enable */ - -#define MDREFR_K0DB4 (1 << 29) /* SDCLK0 Divide by 4 Control/Status */ -#define MDREFR_K2FREE (1 << 25) /* SDRAM Free-Running Control */ -#define MDREFR_K1FREE (1 << 24) /* SDRAM Free-Running Control */ -#define MDREFR_K0FREE (1 << 23) /* SDRAM Free-Running Control */ -#define MDREFR_SLFRSH (1 << 22) /* SDRAM Self-Refresh Control/Status */ -#define MDREFR_APD (1 << 20) /* SDRAM/SSRAM Auto-Power-Down Enable */ -#define MDREFR_K2DB2 (1 << 19) /* SDCLK2 Divide by 2 Control/Status */ -#define MDREFR_K2RUN (1 << 18) /* SDCLK2 Run Control/Status */ -#define MDREFR_K1DB2 (1 << 17) /* SDCLK1 Divide by 2 Control/Status */ -#define MDREFR_K1RUN (1 << 16) /* SDCLK1 Run Control/Status */ -#define MDREFR_E1PIN (1 << 15) /* SDCKE1 Level Control/Status */ -#define MDREFR_K0DB2 (1 << 14) /* SDCLK0 Divide by 2 Control/Status */ -#define MDREFR_K0RUN (1 << 13) /* SDCLK0 Run Control/Status */ -#define MDREFR_E0PIN (1 << 12) /* SDCKE0 Level Control/Status */ - /* * Power Manager */ diff --git a/arch/arm/mach-pxa/include/mach/smemc.h b/arch/arm/mach-pxa/include/mach/smemc.h new file mode 100644 index 00000000000..654adc90c9a --- /dev/null +++ b/arch/arm/mach-pxa/include/mach/smemc.h @@ -0,0 +1,74 @@ +/* + * Static memory controller register definitions for PXA CPUs + * + * Copyright (C) 2010 Marek Vasut + * + * 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. + */ + +#ifndef __SMEMC_REGS_H +#define __SMEMC_REGS_H + +#define PXA2XX_SMEMC_BASE 0x48000000 +#define PXA3XX_SMEMC_BASE 0x4a000000 +#define SMEMC_VIRT 0xf6000000 + +#define MDCNFG (SMEMC_VIRT + 0x00) /* SDRAM Configuration Register 0 */ +#define MDREFR (SMEMC_VIRT + 0x04) /* SDRAM Refresh Control Register */ +#define MSC0 (SMEMC_VIRT + 0x08) /* Static Memory Control Register 0 */ +#define MSC1 (SMEMC_VIRT + 0x0C) /* Static Memory Control Register 1 */ +#define MSC2 (SMEMC_VIRT + 0x10) /* Static Memory Control Register 2 */ +#define MECR (SMEMC_VIRT + 0x14) /* Expansion Memory (PCMCIA/Compact Flash) Bus Configuration */ +#define SXLCR (SMEMC_VIRT + 0x18) /* LCR value to be written to SDRAM-Timing Synchronous Flash */ +#define SXCNFG (SMEMC_VIRT + 0x1C) /* Synchronous Static Memory Control Register */ +#define SXMRS (SMEMC_VIRT + 0x24) /* MRS value to be written to Synchronous Flash or SMROM */ +#define MCMEM0 (SMEMC_VIRT + 0x28) /* Card interface Common Memory Space Socket 0 Timing */ +#define MCMEM1 (SMEMC_VIRT + 0x2C) /* Card interface Common Memory Space Socket 1 Timing */ +#define MCATT0 (SMEMC_VIRT + 0x30) /* Card interface Attribute Space Socket 0 Timing Configuration */ +#define MCATT1 (SMEMC_VIRT + 0x34) /* Card interface Attribute Space Socket 1 Timing Configuration */ +#define MCIO0 (SMEMC_VIRT + 0x38) /* Card interface I/O Space Socket 0 Timing Configuration */ +#define MCIO1 (SMEMC_VIRT + 0x3C) /* Card interface I/O Space Socket 1 Timing Configuration */ +#define MDMRS (SMEMC_VIRT + 0x40) /* MRS value to be written to SDRAM */ +#define BOOT_DEF (SMEMC_VIRT + 0x44) /* Read-Only Boot-Time Register. Contains BOOT_SEL and PKG_SEL */ +#define MEMCLKCFG (SMEMC_VIRT + 0x68) /* Clock Configuration */ +#define CSADRCFG0 (SMEMC_VIRT + 0x80) /* Address Configuration Register for CS0 */ +#define CSADRCFG1 (SMEMC_VIRT + 0x84) /* Address Configuration Register for CS1 */ +#define CSADRCFG2 (SMEMC_VIRT + 0x88) /* Address Configuration Register for CS2 */ +#define CSADRCFG3 (SMEMC_VIRT + 0x8C) /* Address Configuration Register for CS3 */ + +/* + * More handy macros for PCMCIA + * + * Arg is socket number + */ +#define MCMEM(s) (SMEMC_VIRT + 0x28 + ((s)<<2)) /* Card interface Common Memory Space Socket s Timing */ +#define MCATT(s) (SMEMC_VIRT + 0x30 + ((s)<<2)) /* Card interface Attribute Space Socket s Timing Configuration */ +#define MCIO(s) (SMEMC_VIRT + 0x38 + ((s)<<2)) /* Card interface I/O Space Socket s Timing Configuration */ + +/* MECR register defines */ +#define MECR_NOS (1 << 0) /* Number Of Sockets: 0 -> 1 sock, 1 -> 2 sock */ +#define MECR_CIT (1 << 1) /* Card Is There: 0 -> no card, 1 -> card inserted */ + +#define MDCNFG_DE0 (1 << 0) /* SDRAM Bank 0 Enable */ +#define MDCNFG_DE1 (1 << 1) /* SDRAM Bank 1 Enable */ +#define MDCNFG_DE2 (1 << 16) /* SDRAM Bank 2 Enable */ +#define MDCNFG_DE3 (1 << 17) /* SDRAM Bank 3 Enable */ + +#define MDREFR_K0DB4 (1 << 29) /* SDCLK0 Divide by 4 Control/Status */ +#define MDREFR_K2FREE (1 << 25) /* SDRAM Free-Running Control */ +#define MDREFR_K1FREE (1 << 24) /* SDRAM Free-Running Control */ +#define MDREFR_K0FREE (1 << 23) /* SDRAM Free-Running Control */ +#define MDREFR_SLFRSH (1 << 22) /* SDRAM Self-Refresh Control/Status */ +#define MDREFR_APD (1 << 20) /* SDRAM/SSRAM Auto-Power-Down Enable */ +#define MDREFR_K2DB2 (1 << 19) /* SDCLK2 Divide by 2 Control/Status */ +#define MDREFR_K2RUN (1 << 18) /* SDCLK2 Run Control/Status */ +#define MDREFR_K1DB2 (1 << 17) /* SDCLK1 Divide by 2 Control/Status */ +#define MDREFR_K1RUN (1 << 16) /* SDCLK1 Run Control/Status */ +#define MDREFR_E1PIN (1 << 15) /* SDCKE1 Level Control/Status */ +#define MDREFR_K0DB2 (1 << 14) /* SDCLK0 Divide by 2 Control/Status */ +#define MDREFR_K0RUN (1 << 13) /* SDCLK0 Run Control/Status */ +#define MDREFR_E0PIN (1 << 12) /* SDCKE0 Level Control/Status */ + +#endif diff --git a/arch/arm/mach-pxa/lpd270.c b/arch/arm/mach-pxa/lpd270.c index db615d578c0..8ab62a67780 100644 --- a/arch/arm/mach-pxa/lpd270.c +++ b/arch/arm/mach-pxa/lpd270.c @@ -46,6 +46,7 @@ #include #include #include +#include #include "generic.h" #include "devices.h" @@ -463,7 +464,7 @@ static void __init lpd270_init(void) pxa_set_btuart_info(NULL); pxa_set_stuart_info(NULL); - lpd270_flash_data[0].width = (BOOT_DEF & 1) ? 2 : 4; + lpd270_flash_data[0].width = (__raw_readl(BOOT_DEF) & 1) ? 2 : 4; lpd270_flash_data[1].width = 4; /* diff --git a/arch/arm/mach-pxa/lubbock.c b/arch/arm/mach-pxa/lubbock.c index 5b1cc1c01c6..d3375486c8c 100644 --- a/arch/arm/mach-pxa/lubbock.c +++ b/arch/arm/mach-pxa/lubbock.c @@ -50,6 +50,7 @@ #include #include #include +#include #include "generic.h" #include "clock.h" @@ -525,7 +526,7 @@ static void __init lubbock_init(void) pxa_set_ac97_info(NULL); lubbock_flash_data[0].width = lubbock_flash_data[1].width = - (BOOT_DEF & 1) ? 2 : 4; + (__raw_readl(BOOT_DEF) & 1) ? 2 : 4; /* Compensate for the nROMBT switch which swaps the flash banks */ printk(KERN_NOTICE "Lubbock configured to boot from %s (bank %d)\n", flashboot?"Flash":"ROM", flashboot); diff --git a/arch/arm/mach-pxa/mainstone.c b/arch/arm/mach-pxa/mainstone.c index 8bef7c8bcbb..740c03590e3 100644 --- a/arch/arm/mach-pxa/mainstone.c +++ b/arch/arm/mach-pxa/mainstone.c @@ -51,6 +51,7 @@ #include #include #include +#include #include "generic.h" #include "devices.h" @@ -565,7 +566,7 @@ static void __init mainstone_init(void) pxa_set_btuart_info(NULL); pxa_set_stuart_info(NULL); - mst_flash_data[0].width = (BOOT_DEF & 1) ? 2 : 4; + mst_flash_data[0].width = (__raw_readl(BOOT_DEF) & 1) ? 2 : 4; mst_flash_data[1].width = 4; /* Compensate for SW7 which swaps the flash banks */ diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c index 67200ce25e1..23136b6afa8 100644 --- a/arch/arm/mach-pxa/pxa25x.c +++ b/arch/arm/mach-pxa/pxa25x.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "generic.h" #include "devices.h" @@ -323,8 +324,8 @@ void __init pxa26x_init_irq(void) static struct map_desc pxa25x_io_desc[] __initdata = { { /* Mem Ctl */ - .virtual = 0xf6000000, - .pfn = __phys_to_pfn(0x48000000), + .virtual = SMEMC_VIRT, + .pfn = __phys_to_pfn(PXA2XX_SMEMC_BASE), .length = 0x00200000, .type = MT_DEVICE }, diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c index 71dbee42edf..839548d9418 100644 --- a/arch/arm/mach-pxa/pxa27x.c +++ b/arch/arm/mach-pxa/pxa27x.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,8 @@ #include #include #include +#include + #include #include "generic.h" @@ -255,7 +258,7 @@ enum { void pxa27x_cpu_pm_save(unsigned long *sleep_save) { - SAVE(MDREFR); + sleep_save[SLEEP_SAVE_MDREFR] = __raw_readl(MDREFR); SAVE(PCFR); SAVE(CKEN); @@ -264,7 +267,7 @@ void pxa27x_cpu_pm_save(unsigned long *sleep_save) void pxa27x_cpu_pm_restore(unsigned long *sleep_save) { - RESTORE(MDREFR); + __raw_writel(sleep_save[SLEEP_SAVE_MDREFR], MDREFR); RESTORE(PCFR); PSSR = PSSR_RDH | PSSR_PH; @@ -373,8 +376,8 @@ void __init pxa27x_init_irq(void) static struct map_desc pxa27x_io_desc[] __initdata = { { /* Mem Ctl */ - .virtual = 0xf6000000, - .pfn = __phys_to_pfn(0x48000000), + .virtual = SMEMC_VIRT, + .pfn = __phys_to_pfn(PXA2XX_SMEMC_BASE), .length = 0x00200000, .type = MT_DEVICE }, { /* IMem ctl */ diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c index 5480a94e646..dc658ad234e 100644 --- a/arch/arm/mach-pxa/pxa3xx.c +++ b/arch/arm/mach-pxa/pxa3xx.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "generic.h" @@ -583,8 +584,8 @@ void __init pxa3xx_init_irq(void) static struct map_desc pxa3xx_io_desc[] __initdata = { { /* Mem Ctl */ - .virtual = 0xf6000000, - .pfn = __phys_to_pfn(0x4a000000), + .virtual = SMEMC_VIRT, + .pfn = __phys_to_pfn(PXA3XX_SMEMC_BASE), .length = 0x00200000, .type = MT_DEVICE } diff --git a/arch/arm/mach-pxa/sleep.S b/arch/arm/mach-pxa/sleep.S index 52c30b01a67..2f5b08aeb52 100644 --- a/arch/arm/mach-pxa/sleep.S +++ b/arch/arm/mach-pxa/sleep.S @@ -14,7 +14,7 @@ #include #include #include - +#include #include #define MDREFR_KDIV 0x200a4000 // all banks diff --git a/arch/arm/mach-pxa/smemc.c b/arch/arm/mach-pxa/smemc.c index d6f6904132a..232b7316ec0 100644 --- a/arch/arm/mach-pxa/smemc.c +++ b/arch/arm/mach-pxa/smemc.c @@ -9,50 +9,37 @@ #include #include - -#define SMEMC_PHYS_BASE (0x4A000000) -#define SMEMC_PHYS_SIZE (0x90) - -#define MSC0 (0x08) /* Static Memory Controller Register 0 */ -#define MSC1 (0x0C) /* Static Memory Controller Register 1 */ -#define SXCNFG (0x1C) /* Synchronous Static Memory Control Register */ -#define MEMCLKCFG (0x68) /* Clock Configuration */ -#define CSADRCFG0 (0x80) /* Address Configuration Register for CS0 */ -#define CSADRCFG1 (0x84) /* Address Configuration Register for CS1 */ -#define CSADRCFG2 (0x88) /* Address Configuration Register for CS2 */ -#define CSADRCFG3 (0x8C) /* Address Configuration Register for CS3 */ +#include #ifdef CONFIG_PM -static void __iomem *smemc_mmio_base; - static unsigned long msc[2]; static unsigned long sxcnfg, memclkcfg; static unsigned long csadrcfg[4]; static int pxa3xx_smemc_suspend(struct sys_device *dev, pm_message_t state) { - msc[0] = __raw_readl(smemc_mmio_base + MSC0); - msc[1] = __raw_readl(smemc_mmio_base + MSC1); - sxcnfg = __raw_readl(smemc_mmio_base + SXCNFG); - memclkcfg = __raw_readl(smemc_mmio_base + MEMCLKCFG); - csadrcfg[0] = __raw_readl(smemc_mmio_base + CSADRCFG0); - csadrcfg[1] = __raw_readl(smemc_mmio_base + CSADRCFG1); - csadrcfg[2] = __raw_readl(smemc_mmio_base + CSADRCFG2); - csadrcfg[3] = __raw_readl(smemc_mmio_base + CSADRCFG3); + msc[0] = __raw_readl(MSC0); + msc[1] = __raw_readl(MSC1); + sxcnfg = __raw_readl(SXCNFG); + memclkcfg = __raw_readl(MEMCLKCFG); + csadrcfg[0] = __raw_readl(CSADRCFG0); + csadrcfg[1] = __raw_readl(CSADRCFG1); + csadrcfg[2] = __raw_readl(CSADRCFG2); + csadrcfg[3] = __raw_readl(CSADRCFG3); return 0; } static int pxa3xx_smemc_resume(struct sys_device *dev) { - __raw_writel(msc[0], smemc_mmio_base + MSC0); - __raw_writel(msc[1], smemc_mmio_base + MSC1); - __raw_writel(sxcnfg, smemc_mmio_base + SXCNFG); - __raw_writel(memclkcfg, smemc_mmio_base + MEMCLKCFG); - __raw_writel(csadrcfg[0], smemc_mmio_base + CSADRCFG0); - __raw_writel(csadrcfg[1], smemc_mmio_base + CSADRCFG1); - __raw_writel(csadrcfg[2], smemc_mmio_base + CSADRCFG2); - __raw_writel(csadrcfg[3], smemc_mmio_base + CSADRCFG3); + __raw_writel(msc[0], MSC0); + __raw_writel(msc[1], MSC1); + __raw_writel(sxcnfg, SXCNFG); + __raw_writel(memclkcfg, MEMCLKCFG); + __raw_writel(csadrcfg[0], CSADRCFG0); + __raw_writel(csadrcfg[1], CSADRCFG1); + __raw_writel(csadrcfg[2], CSADRCFG2); + __raw_writel(csadrcfg[3], CSADRCFG3); return 0; } @@ -73,10 +60,6 @@ static int __init smemc_init(void) int ret = 0; if (cpu_is_pxa3xx()) { - smemc_mmio_base = ioremap(SMEMC_PHYS_BASE, SMEMC_PHYS_SIZE); - if (smemc_mmio_base == NULL) - return -ENODEV; - ret = sysdev_class_register(&smemc_sysclass); if (ret) return ret; diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c index 38bcc2a0c51..0499a69e767 100644 --- a/arch/arm/mach-pxa/spitz.c +++ b/arch/arm/mach-pxa/spitz.c @@ -45,6 +45,7 @@ #include #include #include +#include #include @@ -930,9 +931,10 @@ static void spitz_poweroff(void) static void spitz_restart(char mode, const char *cmd) { + uint32_t msc0 = __raw_readl(MSC0); /* Bootloader magic for a reboot */ - if ((MSC0 & 0xffff0000) == 0x7ff00000) - MSC0 = (MSC0 & 0xffff) | 0x7ee00000; + if ((msc0 & 0xffff0000) == 0x7ff00000) + __raw_writel((msc0 & 0xffff) | 0x7ee00000, MSC0); spitz_poweroff(); } diff --git a/arch/arm/mach-pxa/stargate2.c b/arch/arm/mach-pxa/stargate2.c index 19f5378e766..3498a142394 100644 --- a/arch/arm/mach-pxa/stargate2.c +++ b/arch/arm/mach-pxa/stargate2.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -976,7 +977,7 @@ static void __init stargate2_init(void) { /* This is probably a board specific hack as this must be set prior to connecting the MFP stuff up. */ - MECR &= ~MECR_NOS; + __raw_writel(__raw_readl(MECR) & ~MECR_NOS, MECR); pxa2xx_mfp_config(ARRAY_AND_SIZE(stargate2_pin_config)); diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c index 38833858cf8..57d61ee9b22 100644 --- a/arch/arm/mach-pxa/tosa.c +++ b/arch/arm/mach-pxa/tosa.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -893,9 +894,11 @@ static void tosa_poweroff(void) static void tosa_restart(char mode, const char *cmd) { + uint32_t msc0 = __raw_readl(MSC0); + /* Bootloader magic for a reboot */ - if((MSC0 & 0xffff0000) == 0x7ff00000) - MSC0 = (MSC0 & 0xffff) | 0x7ee00000; + if((msc0 & 0xffff0000) == 0x7ff00000) + __raw_writel((msc0 & 0xffff) | 0x7ee00000, MSC0); tosa_poweroff(); } diff --git a/arch/arm/mach-pxa/trizeps4.c b/arch/arm/mach-pxa/trizeps4.c index ab3b933e346..43fc9ca1459 100644 --- a/arch/arm/mach-pxa/trizeps4.c +++ b/arch/arm/mach-pxa/trizeps4.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include "generic.h" @@ -542,7 +543,7 @@ static void __init trizeps4_map_io(void) pxa27x_map_io(); iotable_init(trizeps4_io_desc, ARRAY_SIZE(trizeps4_io_desc)); - if ((MSC0 & 0x8) && (BOOT_DEF & 0x1)) { + if ((__raw_readl(MSC0) & 0x8) && (__raw_readl(BOOT_DEF) & 0x1)) { /* if flash is 16 bit wide its a Trizeps4 WL */ __machine_arch_type = MACH_TYPE_TRIZEPS4WL; trizeps4_flash_data[0].width = 2; diff --git a/arch/arm/mach-pxa/xcep.c b/arch/arm/mach-pxa/xcep.c index 99744f4d05a..51c0281c6e0 100644 --- a/arch/arm/mach-pxa/xcep.c +++ b/arch/arm/mach-pxa/xcep.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "generic.h" @@ -172,9 +173,9 @@ static void __init xcep_init(void) /* See Intel XScale Developer's Guide for details */ /* Set RDF and RDN to appropriate values (chip select 3 (smc91x)) */ - MSC1 = (MSC1 & 0xffff) | 0xD5540000; + __raw_writel((__raw_readl(MSC1) & 0xffff) | 0xD5540000, MSC1); /* Set RDF and RDN to appropriate values (chip select 5 (fpga)) */ - MSC2 = (MSC2 & 0xffff) | 0x72A00000; + __raw_writel((__raw_readl(MSC2) & 0xffff) | 0x72A00000, MSC2); platform_add_devices(ARRAY_AND_SIZE(devices)); pxa_set_i2c_info(&xcep_i2c_platform_data); diff --git a/arch/arm/mach-pxa/zeus.c b/arch/arm/mach-pxa/zeus.c index 0909df2151e..c87f2b35ee0 100644 --- a/arch/arm/mach-pxa/zeus.c +++ b/arch/arm/mach-pxa/zeus.c @@ -47,6 +47,7 @@ #include #include #include +#include #include "generic.h" @@ -823,13 +824,16 @@ static mfp_cfg_t zeus_pin_config[] __initdata = { static void __init zeus_init(void) { u16 dm9000_msc = DM9K_MSC_VALUE; + u32 msc0, msc1; system_rev = __raw_readw(ZEUS_CPLD_VERSION); pr_info("Zeus CPLD V%dI%d\n", (system_rev & 0xf0) >> 4, (system_rev & 0x0f)); /* Fix timings for dm9000s (CS1/CS2)*/ - MSC0 = (MSC0 & 0xffff) | (dm9000_msc << 16); - MSC1 = (MSC1 & 0xffff0000) | dm9000_msc; + msc0 = __raw_readl(MSC0) & 0x0000ffff | (dm9000_msc << 16); + msc1 = __raw_readl(MSC1) & 0xffff0000 | dm9000_msc; + __raw_writel(msc0, MSC0); + __raw_writel(msc1, MSC1); pm_power_off = zeus_power_off; zeus_setup_apm(); diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c index ae07b4db8a6..55a7d0b045b 100644 --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -116,37 +117,49 @@ static inline u_int pxa2xx_pcmcia_cmd_time(u_int mem_clk_10khz, static int pxa2xx_pcmcia_set_mcmem( int sock, int speed, int clock ) { - MCMEM(sock) = ((pxa2xx_mcxx_setup(speed, clock) + uint32_t val; + + val = ((pxa2xx_mcxx_setup(speed, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa2xx_mcxx_asst(speed, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa2xx_mcxx_hold(speed, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); + __raw_writel(val, MCMEM(sock)); + return 0; } static int pxa2xx_pcmcia_set_mcio( int sock, int speed, int clock ) { - MCIO(sock) = ((pxa2xx_mcxx_setup(speed, clock) + uint32_t val; + + val = ((pxa2xx_mcxx_setup(speed, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa2xx_mcxx_asst(speed, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa2xx_mcxx_hold(speed, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); + __raw_writel(val, MCIO(sock)); + return 0; } static int pxa2xx_pcmcia_set_mcatt( int sock, int speed, int clock ) { - MCATT(sock) = ((pxa2xx_mcxx_setup(speed, clock) + uint32_t val; + + val = ((pxa2xx_mcxx_setup(speed, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa2xx_mcxx_asst(speed, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa2xx_mcxx_hold(speed, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); + __raw_writel(val, MCATT(sock)); + return 0; } @@ -205,19 +218,18 @@ pxa2xx_pcmcia_frequency_change(struct soc_pcmcia_socket *skt, static void pxa2xx_configure_sockets(struct device *dev) { struct pcmcia_low_level *ops = dev->platform_data; - /* * We have at least one socket, so set MECR:CIT * (Card Is There) */ - MECR |= MECR_CIT; + uint32_t mecr = MECR_CIT; /* Set MECR:NOS (Number Of Sockets) */ if ((ops->first + ops->nr) > 1 || machine_is_viper() || machine_is_arcom_zeus()) - MECR |= MECR_NOS; - else - MECR &= ~MECR_NOS; + mecr |= MECR_NOS; + + __raw_writel(mecr, MECR); } static const char *skt_names[] = { -- cgit v1.2.3-70-g09d2 From 364dbdf3b6c31a4a5fb7a6d479e7aafb4a7a10b6 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 4 Nov 2010 14:44:00 -0400 Subject: video: add driver for PXA3xx 2D graphics accelerator This adds a driver for the the 2D graphics accelerator found on PXA3xx processors. Only resource mapping, interrupt handling and a simple ioctl handler is done by the kernel part, the rest of the logic is implemented in DirectFB userspace. Graphic applications greatly benefit for line drawing, blend, and rectangle and triangle filling operations. Benchmarks done on a PXA303 using the df_dok benchmarking tool follow, where the value in square brackets show the CPU usage during that test. Without accelerator (benchmarking 256x252 on 480x262 RGB16 (16bit)): Anti-aliased Text 3.016 secs ( 65.649 KChars/sec) [ 99.6%] Fill Rectangle 3.021 secs ( 175.107 MPixel/sec) [ 98.0%] Fill Rectangle (blend) 3.582 secs ( 3.602 MPixel/sec) [ 99.7%] Fill Rectangles [10] 3.177 secs ( 182.753 MPixel/sec) [ 98.1%] Fill Rectangles [10] (blend) 18.020 secs ( 3.580 MPixel/sec) [ 98.7%] Fill Spans 3.019 secs ( 145.306 MPixel/sec) [ 98.0%] Fill Spans (blend) 3.616 secs ( 3.568 MPixel/sec) [ 99.4%] Blit 3.074 secs ( 39.874 MPixel/sec) [ 98.0%] Blit 180 3.020 secs ( 32.042 MPixel/sec) [ 98.0%] Blit with format conversion 3.005 secs ( 19.321 MPixel/sec) [ 99.6%] Blit from 32bit (blend) 4.792 secs ( 2.692 MPixel/sec) [ 98.7%] With accelerator: Anti-aliased Text 3.056 secs (* 36.518 KChars/sec) [ 21.3%] Fill Rectangle 3.015 secs (* 115.543 MPixel/sec) [ 8.9%] Fill Rectangle (blend) 3.180 secs (* 20.286 MPixel/sec) [ 1.8%] Fill Rectangles [10] 3.251 secs (* 119.062 MPixel/sec) [ 1.2%] Fill Rectangles [10] (blend) 6.293 secs (* 20.502 MPixel/sec) [ 0.3%] Fill Spans 3.051 secs (* 97.264 MPixel/sec) [ 35.7%] Fill Spans (blend) 3.377 secs (* 15.282 MPixel/sec) [ 17.8%] Blit 3.046 secs (* 27.533 MPixel/sec) [ 2.6%] Blit 180 3.098 secs (* 27.070 MPixel/sec) [ 2.2%] Blit with format conversion 3.131 secs (* 39.148 MPixel/sec) [ 2.8%] Blit from 32bit (blend) 3.346 secs (* 11.568 MPixel/sec) [ 0.8%] Signed-off-by: Daniel Mack Tested-by: Sven Neumann Cc: Eric Miao Cc: Denis Oliver Kropp Cc: Sven Neumann Cc: Haojian Zhuang Signed-off-by: Eric Miao --- drivers/video/Kconfig | 10 + drivers/video/Makefile | 1 + drivers/video/pxa3xx-gcu.c | 772 +++++++++++++++++++++++++++++++++++++++++++++ drivers/video/pxa3xx-gcu.h | 38 +++ 4 files changed, 821 insertions(+) create mode 100644 drivers/video/pxa3xx-gcu.c create mode 100644 drivers/video/pxa3xx-gcu.h (limited to 'drivers') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 27c1fb4b1e0..ab77297fbed 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1850,6 +1850,16 @@ config FB_PXA_PARAMETERS describes the available parameters. +config PXA3XX_GCU + tristate "PXA3xx 2D graphics accelerator driver" + depends on FB_PXA + help + Kernelspace driver for the 2D graphics controller unit (GCU) + found on PXA3xx processors. There is a counterpart driver in the + DirectFB suite, see http://www.directfb.org/ + + If you compile this as a module, it will be called pxa3xx_gcu. + config FB_MBX tristate "2700G LCD framebuffer support" depends on FB && ARCH_PXA diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 485e8ed1318..9260a898f34 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -100,6 +100,7 @@ obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o obj-$(CONFIG_FB_PXA) += pxafb.o obj-$(CONFIG_FB_PXA168) += pxa168fb.o +obj-$(CONFIG_PXA3XX_GCU) += pxa3xx-gcu.o obj-$(CONFIG_FB_W100) += w100fb.o obj-$(CONFIG_FB_TMIO) += tmiofb.o obj-$(CONFIG_FB_AU1100) += au1100fb.o diff --git a/drivers/video/pxa3xx-gcu.c b/drivers/video/pxa3xx-gcu.c new file mode 100644 index 00000000000..b81168df253 --- /dev/null +++ b/drivers/video/pxa3xx-gcu.c @@ -0,0 +1,772 @@ +/* + * pxa3xx-gc.c - Linux kernel module for PXA3xx graphics controllers + * + * This driver needs a DirectFB counterpart in user space, communication + * is handled via mmap()ed memory areas and an ioctl. + * + * Copyright (c) 2009 Daniel Mack + * Copyright (c) 2009 Janine Kropp + * Copyright (c) 2009 Denis Oliver Kropp + * + * 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. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * WARNING: This controller is attached to System Bus 2 of the PXA which + * needs its arbiter to be enabled explictly (CKENB & 1<<9). + * There is currently no way to do this from Linux, so you need to teach + * your bootloader for now. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pxa3xx-gcu.h" + +#define DRV_NAME "pxa3xx-gcu" +#define MISCDEV_MINOR 197 + +#define REG_GCCR 0x00 +#define GCCR_SYNC_CLR (1 << 9) +#define GCCR_BP_RST (1 << 8) +#define GCCR_ABORT (1 << 6) +#define GCCR_STOP (1 << 4) + +#define REG_GCISCR 0x04 +#define REG_GCIECR 0x08 +#define REG_GCRBBR 0x20 +#define REG_GCRBLR 0x24 +#define REG_GCRBHR 0x28 +#define REG_GCRBTR 0x2C +#define REG_GCRBEXHR 0x30 + +#define IE_EOB (1 << 0) +#define IE_EEOB (1 << 5) +#define IE_ALL 0xff + +#define SHARED_SIZE PAGE_ALIGN(sizeof(struct pxa3xx_gcu_shared)) + +/* #define PXA3XX_GCU_DEBUG */ +/* #define PXA3XX_GCU_DEBUG_TIMER */ + +#ifdef PXA3XX_GCU_DEBUG +#define QDUMP(msg) \ + do { \ + QPRINT(priv, KERN_DEBUG, msg); \ + } while (0) +#else +#define QDUMP(msg) do {} while (0) +#endif + +#define QERROR(msg) \ + do { \ + QPRINT(priv, KERN_ERR, msg); \ + } while (0) + +struct pxa3xx_gcu_batch { + struct pxa3xx_gcu_batch *next; + u32 *ptr; + dma_addr_t phys; + unsigned long length; +}; + +struct pxa3xx_gcu_priv { + void __iomem *mmio_base; + struct clk *clk; + struct pxa3xx_gcu_shared *shared; + dma_addr_t shared_phys; + struct resource *resource_mem; + struct miscdevice misc_dev; + struct file_operations misc_fops; + wait_queue_head_t wait_idle; + wait_queue_head_t wait_free; + spinlock_t spinlock; + struct timeval base_time; + + struct pxa3xx_gcu_batch *free; + + struct pxa3xx_gcu_batch *ready; + struct pxa3xx_gcu_batch *ready_last; + struct pxa3xx_gcu_batch *running; +}; + +static inline unsigned long +gc_readl(struct pxa3xx_gcu_priv *priv, unsigned int off) +{ + return __raw_readl(priv->mmio_base + off); +} + +static inline void +gc_writel(struct pxa3xx_gcu_priv *priv, unsigned int off, unsigned long val) +{ + __raw_writel(val, priv->mmio_base + off); +} + +#define QPRINT(priv, level, msg) \ + do { \ + struct timeval tv; \ + struct pxa3xx_gcu_shared *shared = priv->shared; \ + u32 base = gc_readl(priv, REG_GCRBBR); \ + \ + do_gettimeofday(&tv); \ + \ + printk(level "%ld.%03ld.%03ld - %-17s: %-21s (%s, " \ + "STATUS " \ + "0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, " \ + "T %5ld)\n", \ + tv.tv_sec - priv->base_time.tv_sec, \ + tv.tv_usec / 1000, tv.tv_usec % 1000, \ + __func__, msg, \ + shared->hw_running ? "running" : " idle", \ + gc_readl(priv, REG_GCISCR), \ + gc_readl(priv, REG_GCRBBR), \ + gc_readl(priv, REG_GCRBLR), \ + (gc_readl(priv, REG_GCRBEXHR) - base) / 4, \ + (gc_readl(priv, REG_GCRBHR) - base) / 4, \ + (gc_readl(priv, REG_GCRBTR) - base) / 4); \ + } while (0) + +static void +pxa3xx_gcu_reset(struct pxa3xx_gcu_priv *priv) +{ + QDUMP("RESET"); + + /* disable interrupts */ + gc_writel(priv, REG_GCIECR, 0); + + /* reset hardware */ + gc_writel(priv, REG_GCCR, GCCR_ABORT); + gc_writel(priv, REG_GCCR, 0); + + memset(priv->shared, 0, SHARED_SIZE); + priv->shared->buffer_phys = priv->shared_phys; + priv->shared->magic = PXA3XX_GCU_SHARED_MAGIC; + + do_gettimeofday(&priv->base_time); + + /* set up the ring buffer pointers */ + gc_writel(priv, REG_GCRBLR, 0); + gc_writel(priv, REG_GCRBBR, priv->shared_phys); + gc_writel(priv, REG_GCRBTR, priv->shared_phys); + + /* enable all IRQs except EOB */ + gc_writel(priv, REG_GCIECR, IE_ALL & ~IE_EOB); +} + +static void +dump_whole_state(struct pxa3xx_gcu_priv *priv) +{ + struct pxa3xx_gcu_shared *sh = priv->shared; + u32 base = gc_readl(priv, REG_GCRBBR); + + QDUMP("DUMP"); + + printk(KERN_DEBUG "== PXA3XX-GCU DUMP ==\n" + "%s, STATUS 0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, T %5ld\n", + sh->hw_running ? "running" : "idle ", + gc_readl(priv, REG_GCISCR), + gc_readl(priv, REG_GCRBBR), + gc_readl(priv, REG_GCRBLR), + (gc_readl(priv, REG_GCRBEXHR) - base) / 4, + (gc_readl(priv, REG_GCRBHR) - base) / 4, + (gc_readl(priv, REG_GCRBTR) - base) / 4); +} + +static void +flush_running(struct pxa3xx_gcu_priv *priv) +{ + struct pxa3xx_gcu_batch *running = priv->running; + struct pxa3xx_gcu_batch *next; + + while (running) { + next = running->next; + running->next = priv->free; + priv->free = running; + running = next; + } + + priv->running = NULL; +} + +static void +run_ready(struct pxa3xx_gcu_priv *priv) +{ + unsigned int num = 0; + struct pxa3xx_gcu_shared *shared = priv->shared; + struct pxa3xx_gcu_batch *ready = priv->ready; + + QDUMP("Start"); + + BUG_ON(!ready); + + shared->buffer[num++] = 0x05000000; + + while (ready) { + shared->buffer[num++] = 0x00000001; + shared->buffer[num++] = ready->phys; + ready = ready->next; + } + + shared->buffer[num++] = 0x05000000; + priv->running = priv->ready; + priv->ready = priv->ready_last = NULL; + gc_writel(priv, REG_GCRBLR, 0); + shared->hw_running = 1; + + /* ring base address */ + gc_writel(priv, REG_GCRBBR, shared->buffer_phys); + + /* ring tail address */ + gc_writel(priv, REG_GCRBTR, shared->buffer_phys + num * 4); + + /* ring length */ + gc_writel(priv, REG_GCRBLR, ((num + 63) & ~63) * 4); +} + +static irqreturn_t +pxa3xx_gcu_handle_irq(int irq, void *ctx) +{ + struct pxa3xx_gcu_priv *priv = ctx; + struct pxa3xx_gcu_shared *shared = priv->shared; + u32 status = gc_readl(priv, REG_GCISCR) & IE_ALL; + + QDUMP("-Interrupt"); + + if (!status) + return IRQ_NONE; + + spin_lock(&priv->spinlock); + shared->num_interrupts++; + + if (status & IE_EEOB) { + QDUMP(" [EEOB]"); + + flush_running(priv); + wake_up_all(&priv->wait_free); + + if (priv->ready) { + run_ready(priv); + } else { + /* There is no more data prepared by the userspace. + * Set hw_running = 0 and wait for the next userspace + * kick-off */ + shared->num_idle++; + shared->hw_running = 0; + + QDUMP(" '-> Idle."); + + /* set ring buffer length to zero */ + gc_writel(priv, REG_GCRBLR, 0); + + wake_up_all(&priv->wait_idle); + } + + shared->num_done++; + } else { + QERROR(" [???]"); + dump_whole_state(priv); + } + + /* Clear the interrupt */ + gc_writel(priv, REG_GCISCR, status); + spin_unlock(&priv->spinlock); + + return IRQ_HANDLED; +} + +static int +pxa3xx_gcu_wait_idle(struct pxa3xx_gcu_priv *priv) +{ + int ret = 0; + + QDUMP("Waiting for idle..."); + + /* Does not need to be atomic. There's a lock in user space, + * but anyhow, this is just for statistics. */ + priv->shared->num_wait_idle++; + + while (priv->shared->hw_running) { + int num = priv->shared->num_interrupts; + u32 rbexhr = gc_readl(priv, REG_GCRBEXHR); + + ret = wait_event_interruptible_timeout(priv->wait_idle, + !priv->shared->hw_running, HZ*4); + + if (ret < 0) + break; + + if (ret > 0) + continue; + + if (gc_readl(priv, REG_GCRBEXHR) == rbexhr && + priv->shared->num_interrupts == num) { + QERROR("TIMEOUT"); + ret = -ETIMEDOUT; + break; + } + } + + QDUMP("done"); + + return ret; +} + +static int +pxa3xx_gcu_wait_free(struct pxa3xx_gcu_priv *priv) +{ + int ret = 0; + + QDUMP("Waiting for free..."); + + /* Does not need to be atomic. There's a lock in user space, + * but anyhow, this is just for statistics. */ + priv->shared->num_wait_free++; + + while (!priv->free) { + u32 rbexhr = gc_readl(priv, REG_GCRBEXHR); + + ret = wait_event_interruptible_timeout(priv->wait_free, + priv->free, HZ*4); + + if (ret < 0) + break; + + if (ret > 0) + continue; + + if (gc_readl(priv, REG_GCRBEXHR) == rbexhr) { + QERROR("TIMEOUT"); + ret = -ETIMEDOUT; + break; + } + } + + QDUMP("done"); + + return ret; +} + +/* Misc device layer */ + +static ssize_t +pxa3xx_gcu_misc_write(struct file *filp, const char *buff, + size_t count, loff_t *offp) +{ + int ret; + unsigned long flags; + struct pxa3xx_gcu_batch *buffer; + struct pxa3xx_gcu_priv *priv = + container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops); + + int words = count / 4; + + /* Does not need to be atomic. There's a lock in user space, + * but anyhow, this is just for statistics. */ + priv->shared->num_writes++; + + priv->shared->num_words += words; + + /* Last word reserved for batch buffer end command */ + if (words >= PXA3XX_GCU_BATCH_WORDS) + return -E2BIG; + + /* Wait for a free buffer */ + if (!priv->free) { + ret = pxa3xx_gcu_wait_free(priv); + if (ret < 0) + return ret; + } + + /* + * Get buffer from free list + */ + spin_lock_irqsave(&priv->spinlock, flags); + + buffer = priv->free; + priv->free = buffer->next; + + spin_unlock_irqrestore(&priv->spinlock, flags); + + + /* Copy data from user into buffer */ + ret = copy_from_user(buffer->ptr, buff, words * 4); + if (ret) { + spin_lock_irqsave(&priv->spinlock, flags); + buffer->next = priv->free; + priv->free = buffer; + spin_unlock_irqrestore(&priv->spinlock, flags); + return ret; + } + + buffer->length = words; + + /* Append batch buffer end command */ + buffer->ptr[words] = 0x01000000; + + /* + * Add buffer to ready list + */ + spin_lock_irqsave(&priv->spinlock, flags); + + buffer->next = NULL; + + if (priv->ready) { + BUG_ON(priv->ready_last == NULL); + + priv->ready_last->next = buffer; + } else + priv->ready = buffer; + + priv->ready_last = buffer; + + if (!priv->shared->hw_running) + run_ready(priv); + + spin_unlock_irqrestore(&priv->spinlock, flags); + + return words * 4; +} + + +static long +pxa3xx_gcu_misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + struct pxa3xx_gcu_priv *priv = + container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops); + + switch (cmd) { + case PXA3XX_GCU_IOCTL_RESET: + spin_lock_irqsave(&priv->spinlock, flags); + pxa3xx_gcu_reset(priv); + spin_unlock_irqrestore(&priv->spinlock, flags); + return 0; + + case PXA3XX_GCU_IOCTL_WAIT_IDLE: + return pxa3xx_gcu_wait_idle(priv); + } + + return -ENOSYS; +} + +static int +pxa3xx_gcu_misc_mmap(struct file *filp, struct vm_area_struct *vma) +{ + unsigned int size = vma->vm_end - vma->vm_start; + struct pxa3xx_gcu_priv *priv = + container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops); + + switch (vma->vm_pgoff) { + case 0: + /* hand out the shared data area */ + if (size != SHARED_SIZE) + return -EINVAL; + + return dma_mmap_coherent(NULL, vma, + priv->shared, priv->shared_phys, size); + + case SHARED_SIZE >> PAGE_SHIFT: + /* hand out the MMIO base for direct register access + * from userspace */ + if (size != resource_size(priv->resource_mem)) + return -EINVAL; + + vma->vm_flags |= VM_IO; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + return io_remap_pfn_range(vma, vma->vm_start, + priv->resource_mem->start >> PAGE_SHIFT, + size, vma->vm_page_prot); + } + + return -EINVAL; +} + + +#ifdef PXA3XX_GCU_DEBUG_TIMER +static struct timer_list pxa3xx_gcu_debug_timer; + +static void pxa3xx_gcu_debug_timedout(unsigned long ptr) +{ + struct pxa3xx_gcu_priv *priv = (struct pxa3xx_gcu_priv *) ptr; + + QERROR("Timer DUMP"); + + /* init the timer structure */ + init_timer(&pxa3xx_gcu_debug_timer); + pxa3xx_gcu_debug_timer.function = pxa3xx_gcu_debug_timedout; + pxa3xx_gcu_debug_timer.data = ptr; + pxa3xx_gcu_debug_timer.expires = jiffies + 5*HZ; /* one second */ + + add_timer(&pxa3xx_gcu_debug_timer); +} + +static void pxa3xx_gcu_init_debug_timer(void) +{ + pxa3xx_gcu_debug_timedout((unsigned long) &pxa3xx_gcu_debug_timer); +} +#else +static inline void pxa3xx_gcu_init_debug_timer(void) {} +#endif + +static int +add_buffer(struct platform_device *dev, + struct pxa3xx_gcu_priv *priv) +{ + struct pxa3xx_gcu_batch *buffer; + + buffer = kzalloc(sizeof(struct pxa3xx_gcu_batch), GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer->ptr = dma_alloc_coherent(&dev->dev, PXA3XX_GCU_BATCH_WORDS * 4, + &buffer->phys, GFP_KERNEL); + if (!buffer->ptr) { + kfree(buffer); + return -ENOMEM; + } + + buffer->next = priv->free; + + priv->free = buffer; + + return 0; +} + +static void +free_buffers(struct platform_device *dev, + struct pxa3xx_gcu_priv *priv) +{ + struct pxa3xx_gcu_batch *next, *buffer = priv->free; + + while (buffer) { + next = buffer->next; + + dma_free_coherent(&dev->dev, PXA3XX_GCU_BATCH_WORDS * 4, + buffer->ptr, buffer->phys); + + kfree(buffer); + + buffer = next; + } + + priv->free = NULL; +} + +static int __devinit +pxa3xx_gcu_probe(struct platform_device *dev) +{ + int i, ret, irq; + struct resource *r; + struct pxa3xx_gcu_priv *priv; + + priv = kzalloc(sizeof(struct pxa3xx_gcu_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + for (i = 0; i < 8; i++) { + ret = add_buffer(dev, priv); + if (ret) { + dev_err(&dev->dev, "failed to allocate DMA memory\n"); + goto err_free_priv; + } + } + + init_waitqueue_head(&priv->wait_idle); + init_waitqueue_head(&priv->wait_free); + spin_lock_init(&priv->spinlock); + + /* we allocate the misc device structure as part of our own allocation, + * so we can get a pointer to our priv structure later on with + * container_of(). This isn't really necessary as we have a fixed minor + * number anyway, but this is to avoid statics. */ + + priv->misc_fops.owner = THIS_MODULE; + priv->misc_fops.write = pxa3xx_gcu_misc_write; + priv->misc_fops.unlocked_ioctl = pxa3xx_gcu_misc_ioctl; + priv->misc_fops.mmap = pxa3xx_gcu_misc_mmap; + + priv->misc_dev.minor = MISCDEV_MINOR, + priv->misc_dev.name = DRV_NAME, + priv->misc_dev.fops = &priv->misc_fops, + + /* register misc device */ + ret = misc_register(&priv->misc_dev); + if (ret < 0) { + dev_err(&dev->dev, "misc_register() for minor %d failed\n", + MISCDEV_MINOR); + goto err_free_priv; + } + + /* handle IO resources */ + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(&dev->dev, "no I/O memory resource defined\n"); + ret = -ENODEV; + goto err_misc_deregister; + } + + if (!request_mem_region(r->start, resource_size(r), dev->name)) { + dev_err(&dev->dev, "failed to request I/O memory\n"); + ret = -EBUSY; + goto err_misc_deregister; + } + + priv->mmio_base = ioremap_nocache(r->start, resource_size(r)); + if (!priv->mmio_base) { + dev_err(&dev->dev, "failed to map I/O memory\n"); + ret = -EBUSY; + goto err_free_mem_region; + } + + /* allocate dma memory */ + priv->shared = dma_alloc_coherent(&dev->dev, SHARED_SIZE, + &priv->shared_phys, GFP_KERNEL); + + if (!priv->shared) { + dev_err(&dev->dev, "failed to allocate DMA memory\n"); + ret = -ENOMEM; + goto err_free_io; + } + + /* enable the clock */ + priv->clk = clk_get(&dev->dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(&dev->dev, "failed to get clock\n"); + ret = -ENODEV; + goto err_free_dma; + } + + ret = clk_enable(priv->clk); + if (ret < 0) { + dev_err(&dev->dev, "failed to enable clock\n"); + goto err_put_clk; + } + + /* request the IRQ */ + irq = platform_get_irq(dev, 0); + if (irq < 0) { + dev_err(&dev->dev, "no IRQ defined\n"); + ret = -ENODEV; + goto err_put_clk; + } + + ret = request_irq(irq, pxa3xx_gcu_handle_irq, + IRQF_DISABLED, DRV_NAME, priv); + if (ret) { + dev_err(&dev->dev, "request_irq failed\n"); + ret = -EBUSY; + goto err_put_clk; + } + + platform_set_drvdata(dev, priv); + priv->resource_mem = r; + pxa3xx_gcu_reset(priv); + pxa3xx_gcu_init_debug_timer(); + + dev_info(&dev->dev, "registered @0x%p, DMA 0x%p (%d bytes), IRQ %d\n", + (void *) r->start, (void *) priv->shared_phys, + SHARED_SIZE, irq); + return 0; + +err_put_clk: + clk_disable(priv->clk); + clk_put(priv->clk); + +err_free_dma: + dma_free_coherent(&dev->dev, SHARED_SIZE, + priv->shared, priv->shared_phys); + +err_free_io: + iounmap(priv->mmio_base); + +err_free_mem_region: + release_mem_region(r->start, resource_size(r)); + +err_misc_deregister: + misc_deregister(&priv->misc_dev); + +err_free_priv: + platform_set_drvdata(dev, NULL); + free_buffers(dev, priv); + kfree(priv); + return ret; +} + +static int __devexit +pxa3xx_gcu_remove(struct platform_device *dev) +{ + struct pxa3xx_gcu_priv *priv = platform_get_drvdata(dev); + struct resource *r = priv->resource_mem; + + pxa3xx_gcu_wait_idle(priv); + + misc_deregister(&priv->misc_dev); + dma_free_coherent(&dev->dev, SHARED_SIZE, + priv->shared, priv->shared_phys); + iounmap(priv->mmio_base); + release_mem_region(r->start, resource_size(r)); + platform_set_drvdata(dev, NULL); + clk_disable(priv->clk); + free_buffers(dev, priv); + kfree(priv); + + return 0; +} + +static struct platform_driver pxa3xx_gcu_driver = { + .probe = pxa3xx_gcu_probe, + .remove = __devexit_p(pxa3xx_gcu_remove), + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + }, +}; + +static int __init +pxa3xx_gcu_init(void) +{ + return platform_driver_register(&pxa3xx_gcu_driver); +} + +static void __exit +pxa3xx_gcu_exit(void) +{ + platform_driver_unregister(&pxa3xx_gcu_driver); +} + +module_init(pxa3xx_gcu_init); +module_exit(pxa3xx_gcu_exit); + +MODULE_DESCRIPTION("PXA3xx graphics controller unit driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(MISCDEV_MINOR); +MODULE_AUTHOR("Janine Kropp , " + "Denis Oliver Kropp , " + "Daniel Mack "); diff --git a/drivers/video/pxa3xx-gcu.h b/drivers/video/pxa3xx-gcu.h new file mode 100644 index 00000000000..0428ed03dc4 --- /dev/null +++ b/drivers/video/pxa3xx-gcu.h @@ -0,0 +1,38 @@ +#ifndef __PXA3XX_GCU_H__ +#define __PXA3XX_GCU_H__ + +#include + +/* Number of 32bit words in display list (ring buffer). */ +#define PXA3XX_GCU_BUFFER_WORDS ((256 * 1024 - 256) / 4) + +/* To be increased when breaking the ABI */ +#define PXA3XX_GCU_SHARED_MAGIC 0x30000001 + +#define PXA3XX_GCU_BATCH_WORDS 8192 + +struct pxa3xx_gcu_shared { + u32 buffer[PXA3XX_GCU_BUFFER_WORDS]; + + bool hw_running; + + unsigned long buffer_phys; + + unsigned int num_words; + unsigned int num_writes; + unsigned int num_done; + unsigned int num_interrupts; + unsigned int num_wait_idle; + unsigned int num_wait_free; + unsigned int num_idle; + + u32 magic; +}; + +/* Initialization and synchronization. + * Hardware is started upon write(). */ +#define PXA3XX_GCU_IOCTL_RESET _IO('G', 0) +#define PXA3XX_GCU_IOCTL_WAIT_IDLE _IO('G', 2) + +#endif /* __PXA3XX_GCU_H__ */ + -- cgit v1.2.3-70-g09d2 From 2a125dd56b3a853701063fe8a678ad7603e385fd Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Mon, 22 Nov 2010 22:48:49 +0800 Subject: ARM: pxa: remove get_memclk_frequency_10khz() Introduce 'struct clk' for memory and remove get_memclk_frequency_10khz(). Signed-off-by: Eric Miao --- arch/arm/mach-pxa/generic.c | 13 ------------- arch/arm/mach-pxa/generic.h | 4 ---- arch/arm/mach-pxa/pxa25x.c | 20 ++++++++++---------- arch/arm/mach-pxa/pxa27x.c | 15 +++++++++++---- drivers/pcmcia/pxa2xx_base.c | 17 ++++++++++++++--- drivers/pcmcia/soc_common.h | 3 +++ 6 files changed, 38 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-pxa/generic.c b/arch/arm/mach-pxa/generic.c index d4ce8f9233d..d2bb071b0af 100644 --- a/arch/arm/mach-pxa/generic.c +++ b/arch/arm/mach-pxa/generic.c @@ -71,19 +71,6 @@ unsigned int get_clk_frequency_khz(int info) } EXPORT_SYMBOL(get_clk_frequency_khz); -/* - * Return the current memory clock frequency in units of 10kHz - */ -unsigned int get_memclk_frequency_10khz(void) -{ - if (cpu_is_pxa25x()) - return pxa25x_get_memclk_frequency_10khz(); - else if (cpu_is_pxa27x()) - return pxa27x_get_memclk_frequency_10khz(); - return 0; -} -EXPORT_SYMBOL(get_memclk_frequency_10khz); - /* * Intel PXA2xx internal register mapping. * diff --git a/arch/arm/mach-pxa/generic.h b/arch/arm/mach-pxa/generic.h index d2e8bc3aa52..0569d82d259 100644 --- a/arch/arm/mach-pxa/generic.h +++ b/arch/arm/mach-pxa/generic.h @@ -36,18 +36,14 @@ extern unsigned int get_clk_frequency_khz(int info); #ifdef CONFIG_PXA25x extern unsigned pxa25x_get_clk_frequency_khz(int); -extern unsigned pxa25x_get_memclk_frequency_10khz(void); #else #define pxa25x_get_clk_frequency_khz(x) (0) -#define pxa25x_get_memclk_frequency_10khz() (0) #endif #ifdef CONFIG_PXA27x extern unsigned pxa27x_get_clk_frequency_khz(int); -extern unsigned pxa27x_get_memclk_frequency_10khz(void); #else #define pxa27x_get_clk_frequency_khz(x) (0) -#define pxa27x_get_memclk_frequency_10khz() (0) #endif #if defined(CONFIG_PXA25x) || defined(CONFIG_PXA27x) diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c index f29775e3e18..65051bb6d62 100644 --- a/arch/arm/mach-pxa/pxa25x.c +++ b/arch/arm/mach-pxa/pxa25x.c @@ -92,23 +92,21 @@ unsigned int pxa25x_get_clk_frequency_khz(int info) return (turbo & 1) ? (N/1000) : (M/1000); } -/* - * Return the current memory clock frequency in units of 10kHz - */ -unsigned int pxa25x_get_memclk_frequency_10khz(void) +static unsigned long clk_pxa25x_mem_getrate(struct clk *clk) { - return L_clk_mult[(CCCR >> 0) & 0x1f] * BASE_CLK / 10000; + return L_clk_mult[(CCCR >> 0) & 0x1f] * BASE_CLK; } -static unsigned long clk_pxa25x_lcd_getrate(struct clk *clk) -{ - return pxa25x_get_memclk_frequency_10khz() * 10000; -} +static const struct clkops clk_pxa25x_mem_ops = { + .enable = clk_dummy_enable, + .disable = clk_dummy_disable, + .getrate = clk_pxa25x_mem_getrate, +}; static const struct clkops clk_pxa25x_lcd_ops = { .enable = clk_pxa2xx_cken_enable, .disable = clk_pxa2xx_cken_disable, - .getrate = clk_pxa25x_lcd_getrate, + .getrate = clk_pxa25x_mem_getrate, }; static unsigned long gpio12_config_32k[] = { @@ -185,6 +183,7 @@ static DEFINE_PXA2_CKEN(pxa25x_ficp, FICP, 47923000, 0); static DEFINE_CK(pxa25x_lcd, LCD, &clk_pxa25x_lcd_ops); static DEFINE_CLK(pxa25x_gpio11, &clk_pxa25x_gpio11_ops, 3686400, 0); static DEFINE_CLK(pxa25x_gpio12, &clk_pxa25x_gpio12_ops, 32768, 0); +static DEFINE_CLK(pxa25x_mem, &clk_pxa25x_mem_ops, 0, 0); static struct clk_lookup pxa25x_clkregs[] = { INIT_CLKREG(&clk_pxa25x_lcd, "pxa2xx-fb", NULL), @@ -205,6 +204,7 @@ static struct clk_lookup pxa25x_clkregs[] = { INIT_CLKREG(&clk_pxa25x_ac97, NULL, "AC97CLK"), INIT_CLKREG(&clk_pxa25x_gpio11, NULL, "GPIO11_CLK"), INIT_CLKREG(&clk_pxa25x_gpio12, NULL, "GPIO12_CLK"), + INIT_CLKREG(&clk_pxa25x_mem, "pxa2xx-pcmcia", NULL), }; static struct clk_lookup pxa25x_hwuart_clkreg = diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c index 13242f2a339..6d2254b92ad 100644 --- a/arch/arm/mach-pxa/pxa27x.c +++ b/arch/arm/mach-pxa/pxa27x.c @@ -111,10 +111,9 @@ unsigned int pxa27x_get_clk_frequency_khz(int info) } /* - * Return the current mem clock frequency in units of 10kHz as - * reflected by CCCR[A], B, and L + * Return the current mem clock frequency as reflected by CCCR[A], B, and L */ -unsigned int pxa27x_get_memclk_frequency_10khz(void) +static unsigned long clk_pxa27x_mem_getrate(struct clk *clk) { unsigned long ccsr, clkcfg; unsigned int l, L, m, M; @@ -133,9 +132,15 @@ unsigned int pxa27x_get_memclk_frequency_10khz(void) L = l * BASE_CLK; M = (!cccr_a) ? (L/m) : ((b) ? L : (L/2)); - return (M / 10000); + return M; } +static const struct clkops clk_pxa27x_mem_ops = { + .enable = clk_dummy_enable, + .disable = clk_dummy_disable, + .getrate = clk_pxa27x_mem_getrate, +}; + /* * Return the current LCD clock frequency in units of 10kHz as */ @@ -192,6 +197,7 @@ static DEFINE_PXA2_CKEN(pxa27x_memc, MEMC, 0, 0); static DEFINE_CK(pxa27x_lcd, LCD, &clk_pxa27x_lcd_ops); static DEFINE_CK(pxa27x_camera, CAMERA, &clk_pxa27x_lcd_ops); +static DEFINE_CLK(pxa27x_mem, &clk_pxa27x_mem_ops, 0, 0); static struct clk_lookup pxa27x_clkregs[] = { INIT_CLKREG(&clk_pxa27x_lcd, "pxa2xx-fb", NULL), @@ -220,6 +226,7 @@ static struct clk_lookup pxa27x_clkregs[] = { INIT_CLKREG(&clk_pxa27x_memstk, NULL, "MSTKCLK"), INIT_CLKREG(&clk_pxa27x_im, NULL, "IMCLK"), INIT_CLKREG(&clk_pxa27x_memc, NULL, "MEMCLK"), + INIT_CLKREG(&clk_pxa27x_mem, "pxa2xx-pcmcia", NULL), }; #ifdef CONFIG_PM diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c index 55a7d0b045b..3c01774eb39 100644 --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -179,8 +179,8 @@ static int pxa2xx_pcmcia_set_mcxx(struct soc_pcmcia_socket *skt, unsigned int cl static int pxa2xx_pcmcia_set_timing(struct soc_pcmcia_socket *skt) { - unsigned int clk = get_memclk_frequency_10khz(); - return pxa2xx_pcmcia_set_mcxx(skt, clk); + unsigned long clk = clk_get_rate(skt->clk); + return pxa2xx_pcmcia_set_mcxx(skt, clk / 10000); } #ifdef CONFIG_CPU_FREQ @@ -282,24 +282,33 @@ static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev) struct pcmcia_low_level *ops; struct skt_dev_info *sinfo; struct soc_pcmcia_socket *skt; + struct clk *clk; ops = (struct pcmcia_low_level *)dev->dev.platform_data; if (!ops) return -ENODEV; + clk = clk_get(&dev->dev, NULL); + if (!clk) + return -ENODEV; + pxa2xx_drv_pcmcia_ops(ops); sinfo = kzalloc(SKT_DEV_INFO_SIZE(ops->nr), GFP_KERNEL); - if (!sinfo) + if (!sinfo) { + clk_put(clk); return -ENOMEM; + } sinfo->nskt = ops->nr; + sinfo->clk = clk; /* Initialize processor specific parameters */ for (i = 0; i < ops->nr; i++) { skt = &sinfo->skt[i]; skt->nr = ops->first + i; + skt->clk = clk; skt->ops = ops; skt->socket.owner = ops->owner; skt->socket.dev.parent = &dev->dev; @@ -314,6 +323,7 @@ static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev) while (--i >= 0) soc_pcmcia_remove_one(&sinfo->skt[i]); kfree(sinfo); + clk_put(clk); } else { pxa2xx_configure_sockets(&dev->dev); dev_set_drvdata(&dev->dev, sinfo); @@ -332,6 +342,7 @@ static int pxa2xx_drv_pcmcia_remove(struct platform_device *dev) for (i = 0; i < sinfo->nskt; i++) soc_pcmcia_remove_one(&sinfo->skt[i]); + clk_put(sinfo->clk); kfree(sinfo); return 0; } diff --git a/drivers/pcmcia/soc_common.h b/drivers/pcmcia/soc_common.h index bbcd5385a22..9daa73615c8 100644 --- a/drivers/pcmcia/soc_common.h +++ b/drivers/pcmcia/soc_common.h @@ -10,6 +10,7 @@ #define _ASM_ARCH_PCMCIA /* include the world */ +#include #include #include #include @@ -29,6 +30,7 @@ struct soc_pcmcia_socket { * Info from low level handler */ unsigned int nr; + struct clk *clk; /* * Core PCMCIA state @@ -56,6 +58,7 @@ struct soc_pcmcia_socket { struct skt_dev_info { int nskt; + struct clk *clk; struct soc_pcmcia_socket skt[0]; }; -- cgit v1.2.3-70-g09d2 From c6eda6c5eeb357ff231121619fb49d2bc0605faf Mon Sep 17 00:00:00 2001 From: Sundar Iyer Date: Mon, 13 Dec 2010 09:33:12 +0530 Subject: mfd/tc35892: rename tc35892 header to tc3589x Rename the header file to include further variants within the same mfd core driver Acked-by: Samuel Ortiz Signed-off-by: Sundar Iyer Signed-off-by: Linus Walleij --- drivers/gpio/tc35892-gpio.c | 2 +- drivers/mfd/tc35892.c | 2 +- include/linux/mfd/tc35892.h | 136 -------------------------------------------- include/linux/mfd/tc3589x.h | 136 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+), 138 deletions(-) delete mode 100644 include/linux/mfd/tc35892.h create mode 100644 include/linux/mfd/tc3589x.h (limited to 'drivers') diff --git a/drivers/gpio/tc35892-gpio.c b/drivers/gpio/tc35892-gpio.c index 7e10c935a04..027b857c18f 100644 --- a/drivers/gpio/tc35892-gpio.c +++ b/drivers/gpio/tc35892-gpio.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include /* * These registers are modified under the irq bus lock and cached to avoid diff --git a/drivers/mfd/tc35892.c b/drivers/mfd/tc35892.c index e619e2a5599..f230235c376 100644 --- a/drivers/mfd/tc35892.c +++ b/drivers/mfd/tc35892.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include /** * tc35892_reg_read() - read a single TC35892 register diff --git a/include/linux/mfd/tc35892.h b/include/linux/mfd/tc35892.h deleted file mode 100644 index eff3094ca84..00000000000 --- a/include/linux/mfd/tc35892.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * License Terms: GNU General Public License, version 2 - */ - -#ifndef __LINUX_MFD_TC35892_H -#define __LINUX_MFD_TC35892_H - -#include - -#define TC35892_RSTCTRL_IRQRST (1 << 4) -#define TC35892_RSTCTRL_TIMRST (1 << 3) -#define TC35892_RSTCTRL_ROTRST (1 << 2) -#define TC35892_RSTCTRL_KBDRST (1 << 1) -#define TC35892_RSTCTRL_GPIRST (1 << 0) - -#define TC35892_IRQST 0x91 - -#define TC35892_MANFCODE_MAGIC 0x03 -#define TC35892_MANFCODE 0x80 -#define TC35892_VERSION 0x81 -#define TC35892_IOCFG 0xA7 - -#define TC35892_CLKMODE 0x88 -#define TC35892_CLKCFG 0x89 -#define TC35892_CLKEN 0x8A - -#define TC35892_RSTCTRL 0x82 -#define TC35892_EXTRSTN 0x83 -#define TC35892_RSTINTCLR 0x84 - -#define TC35892_GPIOIS0 0xC9 -#define TC35892_GPIOIS1 0xCA -#define TC35892_GPIOIS2 0xCB -#define TC35892_GPIOIBE0 0xCC -#define TC35892_GPIOIBE1 0xCD -#define TC35892_GPIOIBE2 0xCE -#define TC35892_GPIOIEV0 0xCF -#define TC35892_GPIOIEV1 0xD0 -#define TC35892_GPIOIEV2 0xD1 -#define TC35892_GPIOIE0 0xD2 -#define TC35892_GPIOIE1 0xD3 -#define TC35892_GPIOIE2 0xD4 -#define TC35892_GPIORIS0 0xD6 -#define TC35892_GPIORIS1 0xD7 -#define TC35892_GPIORIS2 0xD8 -#define TC35892_GPIOMIS0 0xD9 -#define TC35892_GPIOMIS1 0xDA -#define TC35892_GPIOMIS2 0xDB -#define TC35892_GPIOIC0 0xDC -#define TC35892_GPIOIC1 0xDD -#define TC35892_GPIOIC2 0xDE - -#define TC35892_GPIODATA0 0xC0 -#define TC35892_GPIOMASK0 0xc1 -#define TC35892_GPIODATA1 0xC2 -#define TC35892_GPIOMASK1 0xc3 -#define TC35892_GPIODATA2 0xC4 -#define TC35892_GPIOMASK2 0xC5 - -#define TC35892_GPIODIR0 0xC6 -#define TC35892_GPIODIR1 0xC7 -#define TC35892_GPIODIR2 0xC8 - -#define TC35892_GPIOSYNC0 0xE6 -#define TC35892_GPIOSYNC1 0xE7 -#define TC35892_GPIOSYNC2 0xE8 - -#define TC35892_GPIOWAKE0 0xE9 -#define TC35892_GPIOWAKE1 0xEA -#define TC35892_GPIOWAKE2 0xEB - -#define TC35892_GPIOODM0 0xE0 -#define TC35892_GPIOODE0 0xE1 -#define TC35892_GPIOODM1 0xE2 -#define TC35892_GPIOODE1 0xE3 -#define TC35892_GPIOODM2 0xE4 -#define TC35892_GPIOODE2 0xE5 - -#define TC35892_INT_GPIIRQ 0 -#define TC35892_INT_TI0IRQ 1 -#define TC35892_INT_TI1IRQ 2 -#define TC35892_INT_TI2IRQ 3 -#define TC35892_INT_ROTIRQ 5 -#define TC35892_INT_KBDIRQ 6 -#define TC35892_INT_PORIRQ 7 - -#define TC35892_NR_INTERNAL_IRQS 8 -#define TC35892_INT_GPIO(x) (TC35892_NR_INTERNAL_IRQS + (x)) - -struct tc35892 { - struct mutex lock; - struct device *dev; - struct i2c_client *i2c; - - int irq_base; - int num_gpio; - struct tc35892_platform_data *pdata; -}; - -extern int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data); -extern int tc35892_reg_read(struct tc35892 *tc35892, u8 reg); -extern int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length, - u8 *values); -extern int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length, - const u8 *values); -extern int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val); - -/** - * struct tc35892_gpio_platform_data - TC35892 GPIO platform data - * @gpio_base: first gpio number assigned to TC35892. A maximum of - * %TC35892_NR_GPIOS GPIOs will be allocated. - * @setup: callback for board-specific initialization - * @remove: callback for board-specific teardown - */ -struct tc35892_gpio_platform_data { - int gpio_base; - void (*setup)(struct tc35892 *tc35892, unsigned gpio_base); - void (*remove)(struct tc35892 *tc35892, unsigned gpio_base); -}; - -/** - * struct tc35892_platform_data - TC35892 platform data - * @irq_base: base IRQ number. %TC35892_NR_IRQS irqs will be used. - * @gpio: GPIO-specific platform data - */ -struct tc35892_platform_data { - int irq_base; - struct tc35892_gpio_platform_data *gpio; -}; - -#define TC35892_NR_GPIOS 24 -#define TC35892_NR_IRQS TC35892_INT_GPIO(TC35892_NR_GPIOS) - -#endif diff --git a/include/linux/mfd/tc3589x.h b/include/linux/mfd/tc3589x.h new file mode 100644 index 00000000000..eff3094ca84 --- /dev/null +++ b/include/linux/mfd/tc3589x.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License, version 2 + */ + +#ifndef __LINUX_MFD_TC35892_H +#define __LINUX_MFD_TC35892_H + +#include + +#define TC35892_RSTCTRL_IRQRST (1 << 4) +#define TC35892_RSTCTRL_TIMRST (1 << 3) +#define TC35892_RSTCTRL_ROTRST (1 << 2) +#define TC35892_RSTCTRL_KBDRST (1 << 1) +#define TC35892_RSTCTRL_GPIRST (1 << 0) + +#define TC35892_IRQST 0x91 + +#define TC35892_MANFCODE_MAGIC 0x03 +#define TC35892_MANFCODE 0x80 +#define TC35892_VERSION 0x81 +#define TC35892_IOCFG 0xA7 + +#define TC35892_CLKMODE 0x88 +#define TC35892_CLKCFG 0x89 +#define TC35892_CLKEN 0x8A + +#define TC35892_RSTCTRL 0x82 +#define TC35892_EXTRSTN 0x83 +#define TC35892_RSTINTCLR 0x84 + +#define TC35892_GPIOIS0 0xC9 +#define TC35892_GPIOIS1 0xCA +#define TC35892_GPIOIS2 0xCB +#define TC35892_GPIOIBE0 0xCC +#define TC35892_GPIOIBE1 0xCD +#define TC35892_GPIOIBE2 0xCE +#define TC35892_GPIOIEV0 0xCF +#define TC35892_GPIOIEV1 0xD0 +#define TC35892_GPIOIEV2 0xD1 +#define TC35892_GPIOIE0 0xD2 +#define TC35892_GPIOIE1 0xD3 +#define TC35892_GPIOIE2 0xD4 +#define TC35892_GPIORIS0 0xD6 +#define TC35892_GPIORIS1 0xD7 +#define TC35892_GPIORIS2 0xD8 +#define TC35892_GPIOMIS0 0xD9 +#define TC35892_GPIOMIS1 0xDA +#define TC35892_GPIOMIS2 0xDB +#define TC35892_GPIOIC0 0xDC +#define TC35892_GPIOIC1 0xDD +#define TC35892_GPIOIC2 0xDE + +#define TC35892_GPIODATA0 0xC0 +#define TC35892_GPIOMASK0 0xc1 +#define TC35892_GPIODATA1 0xC2 +#define TC35892_GPIOMASK1 0xc3 +#define TC35892_GPIODATA2 0xC4 +#define TC35892_GPIOMASK2 0xC5 + +#define TC35892_GPIODIR0 0xC6 +#define TC35892_GPIODIR1 0xC7 +#define TC35892_GPIODIR2 0xC8 + +#define TC35892_GPIOSYNC0 0xE6 +#define TC35892_GPIOSYNC1 0xE7 +#define TC35892_GPIOSYNC2 0xE8 + +#define TC35892_GPIOWAKE0 0xE9 +#define TC35892_GPIOWAKE1 0xEA +#define TC35892_GPIOWAKE2 0xEB + +#define TC35892_GPIOODM0 0xE0 +#define TC35892_GPIOODE0 0xE1 +#define TC35892_GPIOODM1 0xE2 +#define TC35892_GPIOODE1 0xE3 +#define TC35892_GPIOODM2 0xE4 +#define TC35892_GPIOODE2 0xE5 + +#define TC35892_INT_GPIIRQ 0 +#define TC35892_INT_TI0IRQ 1 +#define TC35892_INT_TI1IRQ 2 +#define TC35892_INT_TI2IRQ 3 +#define TC35892_INT_ROTIRQ 5 +#define TC35892_INT_KBDIRQ 6 +#define TC35892_INT_PORIRQ 7 + +#define TC35892_NR_INTERNAL_IRQS 8 +#define TC35892_INT_GPIO(x) (TC35892_NR_INTERNAL_IRQS + (x)) + +struct tc35892 { + struct mutex lock; + struct device *dev; + struct i2c_client *i2c; + + int irq_base; + int num_gpio; + struct tc35892_platform_data *pdata; +}; + +extern int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data); +extern int tc35892_reg_read(struct tc35892 *tc35892, u8 reg); +extern int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length, + u8 *values); +extern int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length, + const u8 *values); +extern int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val); + +/** + * struct tc35892_gpio_platform_data - TC35892 GPIO platform data + * @gpio_base: first gpio number assigned to TC35892. A maximum of + * %TC35892_NR_GPIOS GPIOs will be allocated. + * @setup: callback for board-specific initialization + * @remove: callback for board-specific teardown + */ +struct tc35892_gpio_platform_data { + int gpio_base; + void (*setup)(struct tc35892 *tc35892, unsigned gpio_base); + void (*remove)(struct tc35892 *tc35892, unsigned gpio_base); +}; + +/** + * struct tc35892_platform_data - TC35892 platform data + * @irq_base: base IRQ number. %TC35892_NR_IRQS irqs will be used. + * @gpio: GPIO-specific platform data + */ +struct tc35892_platform_data { + int irq_base; + struct tc35892_gpio_platform_data *gpio; +}; + +#define TC35892_NR_GPIOS 24 +#define TC35892_NR_IRQS TC35892_INT_GPIO(TC35892_NR_GPIOS) + +#endif -- cgit v1.2.3-70-g09d2 From f4e8afdc7ab1b5a0962be02a9dd15d29a81f4c53 Mon Sep 17 00:00:00 2001 From: Sundar Iyer Date: Mon, 13 Dec 2010 09:33:13 +0530 Subject: mfd/tc35892: rename tc35892 core driver to tc3589x Rename the tc35892 core/gpio drivers to tc3589x to include new variants in the same mfd core Acked-by: Samuel Ortiz Signed-off-by: Sundar Iyer Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 8 +- drivers/gpio/Makefile | 2 +- drivers/gpio/tc35892-gpio.c | 389 -------------------------------------------- drivers/gpio/tc3589x-gpio.c | 389 ++++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/Kconfig | 6 +- drivers/mfd/Makefile | 2 +- drivers/mfd/tc35892.c | 345 --------------------------------------- drivers/mfd/tc3589x.c | 345 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 743 insertions(+), 743 deletions(-) delete mode 100644 drivers/gpio/tc35892-gpio.c create mode 100644 drivers/gpio/tc3589x-gpio.c delete mode 100644 drivers/mfd/tc35892.c create mode 100644 drivers/mfd/tc3589x.c (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3143ac795eb..082495bb08a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -230,11 +230,11 @@ config GPIO_STMPE This enables support for the GPIOs found on the STMPE I/O Expanders. -config GPIO_TC35892 - bool "TC35892 GPIOs" - depends on MFD_TC35892 +config GPIO_TC3589X + bool "TC3589X GPIOs" + depends on MFD_TC3589X help - This enables support for the GPIOs found on the TC35892 + This enables support for the GPIOs found on the TC3589X I/O Expander. config GPIO_TWL4030 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index bdf3ddec065..39bfd7a3765 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -24,7 +24,7 @@ obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o obj-$(CONFIG_GPIO_PCH) += pch_gpio.o obj-$(CONFIG_GPIO_PL061) += pl061.o obj-$(CONFIG_GPIO_STMPE) += stmpe-gpio.o -obj-$(CONFIG_GPIO_TC35892) += tc35892-gpio.o +obj-$(CONFIG_GPIO_TC3589X) += tc3589x-gpio.o obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o diff --git a/drivers/gpio/tc35892-gpio.c b/drivers/gpio/tc35892-gpio.c deleted file mode 100644 index 027b857c18f..00000000000 --- a/drivers/gpio/tc35892-gpio.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * License Terms: GNU General Public License, version 2 - * Author: Hanumath Prasad for ST-Ericsson - * Author: Rabin Vincent for ST-Ericsson - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * These registers are modified under the irq bus lock and cached to avoid - * unnecessary writes in bus_sync_unlock. - */ -enum { REG_IBE, REG_IEV, REG_IS, REG_IE }; - -#define CACHE_NR_REGS 4 -#define CACHE_NR_BANKS 3 - -struct tc35892_gpio { - struct gpio_chip chip; - struct tc35892 *tc35892; - struct device *dev; - struct mutex irq_lock; - - int irq_base; - - /* Caches of interrupt control registers for bus_lock */ - u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS]; - u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS]; -}; - -static inline struct tc35892_gpio *to_tc35892_gpio(struct gpio_chip *chip) -{ - return container_of(chip, struct tc35892_gpio, chip); -} - -static int tc35892_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); - struct tc35892 *tc35892 = tc35892_gpio->tc35892; - u8 reg = TC35892_GPIODATA0 + (offset / 8) * 2; - u8 mask = 1 << (offset % 8); - int ret; - - ret = tc35892_reg_read(tc35892, reg); - if (ret < 0) - return ret; - - return ret & mask; -} - -static void tc35892_gpio_set(struct gpio_chip *chip, unsigned offset, int val) -{ - struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); - struct tc35892 *tc35892 = tc35892_gpio->tc35892; - u8 reg = TC35892_GPIODATA0 + (offset / 8) * 2; - unsigned pos = offset % 8; - u8 data[] = {!!val << pos, 1 << pos}; - - tc35892_block_write(tc35892, reg, ARRAY_SIZE(data), data); -} - -static int tc35892_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int val) -{ - struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); - struct tc35892 *tc35892 = tc35892_gpio->tc35892; - u8 reg = TC35892_GPIODIR0 + offset / 8; - unsigned pos = offset % 8; - - tc35892_gpio_set(chip, offset, val); - - return tc35892_set_bits(tc35892, reg, 1 << pos, 1 << pos); -} - -static int tc35892_gpio_direction_input(struct gpio_chip *chip, - unsigned offset) -{ - struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); - struct tc35892 *tc35892 = tc35892_gpio->tc35892; - u8 reg = TC35892_GPIODIR0 + offset / 8; - unsigned pos = offset % 8; - - return tc35892_set_bits(tc35892, reg, 1 << pos, 0); -} - -static int tc35892_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); - - return tc35892_gpio->irq_base + offset; -} - -static struct gpio_chip template_chip = { - .label = "tc35892", - .owner = THIS_MODULE, - .direction_input = tc35892_gpio_direction_input, - .get = tc35892_gpio_get, - .direction_output = tc35892_gpio_direction_output, - .set = tc35892_gpio_set, - .to_irq = tc35892_gpio_to_irq, - .can_sleep = 1, -}; - -static int tc35892_gpio_irq_set_type(unsigned int irq, unsigned int type) -{ - struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); - int offset = irq - tc35892_gpio->irq_base; - int regoffset = offset / 8; - int mask = 1 << (offset % 8); - - if (type == IRQ_TYPE_EDGE_BOTH) { - tc35892_gpio->regs[REG_IBE][regoffset] |= mask; - return 0; - } - - tc35892_gpio->regs[REG_IBE][regoffset] &= ~mask; - - if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH) - tc35892_gpio->regs[REG_IS][regoffset] |= mask; - else - tc35892_gpio->regs[REG_IS][regoffset] &= ~mask; - - if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH) - tc35892_gpio->regs[REG_IEV][regoffset] |= mask; - else - tc35892_gpio->regs[REG_IEV][regoffset] &= ~mask; - - return 0; -} - -static void tc35892_gpio_irq_lock(unsigned int irq) -{ - struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); - - mutex_lock(&tc35892_gpio->irq_lock); -} - -static void tc35892_gpio_irq_sync_unlock(unsigned int irq) -{ - struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); - struct tc35892 *tc35892 = tc35892_gpio->tc35892; - static const u8 regmap[] = { - [REG_IBE] = TC35892_GPIOIBE0, - [REG_IEV] = TC35892_GPIOIEV0, - [REG_IS] = TC35892_GPIOIS0, - [REG_IE] = TC35892_GPIOIE0, - }; - int i, j; - - for (i = 0; i < CACHE_NR_REGS; i++) { - for (j = 0; j < CACHE_NR_BANKS; j++) { - u8 old = tc35892_gpio->oldregs[i][j]; - u8 new = tc35892_gpio->regs[i][j]; - - if (new == old) - continue; - - tc35892_gpio->oldregs[i][j] = new; - tc35892_reg_write(tc35892, regmap[i] + j * 8, new); - } - } - - mutex_unlock(&tc35892_gpio->irq_lock); -} - -static void tc35892_gpio_irq_mask(unsigned int irq) -{ - struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); - int offset = irq - tc35892_gpio->irq_base; - int regoffset = offset / 8; - int mask = 1 << (offset % 8); - - tc35892_gpio->regs[REG_IE][regoffset] &= ~mask; -} - -static void tc35892_gpio_irq_unmask(unsigned int irq) -{ - struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); - int offset = irq - tc35892_gpio->irq_base; - int regoffset = offset / 8; - int mask = 1 << (offset % 8); - - tc35892_gpio->regs[REG_IE][regoffset] |= mask; -} - -static struct irq_chip tc35892_gpio_irq_chip = { - .name = "tc35892-gpio", - .bus_lock = tc35892_gpio_irq_lock, - .bus_sync_unlock = tc35892_gpio_irq_sync_unlock, - .mask = tc35892_gpio_irq_mask, - .unmask = tc35892_gpio_irq_unmask, - .set_type = tc35892_gpio_irq_set_type, -}; - -static irqreturn_t tc35892_gpio_irq(int irq, void *dev) -{ - struct tc35892_gpio *tc35892_gpio = dev; - struct tc35892 *tc35892 = tc35892_gpio->tc35892; - u8 status[CACHE_NR_BANKS]; - int ret; - int i; - - ret = tc35892_block_read(tc35892, TC35892_GPIOMIS0, - ARRAY_SIZE(status), status); - if (ret < 0) - return IRQ_NONE; - - for (i = 0; i < ARRAY_SIZE(status); i++) { - unsigned int stat = status[i]; - if (!stat) - continue; - - while (stat) { - int bit = __ffs(stat); - int line = i * 8 + bit; - - handle_nested_irq(tc35892_gpio->irq_base + line); - stat &= ~(1 << bit); - } - - tc35892_reg_write(tc35892, TC35892_GPIOIC0 + i, status[i]); - } - - return IRQ_HANDLED; -} - -static int tc35892_gpio_irq_init(struct tc35892_gpio *tc35892_gpio) -{ - int base = tc35892_gpio->irq_base; - int irq; - - for (irq = base; irq < base + tc35892_gpio->chip.ngpio; irq++) { - set_irq_chip_data(irq, tc35892_gpio); - set_irq_chip_and_handler(irq, &tc35892_gpio_irq_chip, - handle_simple_irq); - set_irq_nested_thread(irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); -#else - set_irq_noprobe(irq); -#endif - } - - return 0; -} - -static void tc35892_gpio_irq_remove(struct tc35892_gpio *tc35892_gpio) -{ - int base = tc35892_gpio->irq_base; - int irq; - - for (irq = base; irq < base + tc35892_gpio->chip.ngpio; irq++) { -#ifdef CONFIG_ARM - set_irq_flags(irq, 0); -#endif - set_irq_chip_and_handler(irq, NULL, NULL); - set_irq_chip_data(irq, NULL); - } -} - -static int __devinit tc35892_gpio_probe(struct platform_device *pdev) -{ - struct tc35892 *tc35892 = dev_get_drvdata(pdev->dev.parent); - struct tc35892_gpio_platform_data *pdata; - struct tc35892_gpio *tc35892_gpio; - int ret; - int irq; - - pdata = tc35892->pdata->gpio; - if (!pdata) - return -ENODEV; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - tc35892_gpio = kzalloc(sizeof(struct tc35892_gpio), GFP_KERNEL); - if (!tc35892_gpio) - return -ENOMEM; - - mutex_init(&tc35892_gpio->irq_lock); - - tc35892_gpio->dev = &pdev->dev; - tc35892_gpio->tc35892 = tc35892; - - tc35892_gpio->chip = template_chip; - tc35892_gpio->chip.ngpio = tc35892->num_gpio; - tc35892_gpio->chip.dev = &pdev->dev; - tc35892_gpio->chip.base = pdata->gpio_base; - - tc35892_gpio->irq_base = tc35892->irq_base + TC35892_INT_GPIO(0); - - /* Bring the GPIO module out of reset */ - ret = tc35892_set_bits(tc35892, TC35892_RSTCTRL, - TC35892_RSTCTRL_GPIRST, 0); - if (ret < 0) - goto out_free; - - ret = tc35892_gpio_irq_init(tc35892_gpio); - if (ret) - goto out_free; - - ret = request_threaded_irq(irq, NULL, tc35892_gpio_irq, IRQF_ONESHOT, - "tc35892-gpio", tc35892_gpio); - if (ret) { - dev_err(&pdev->dev, "unable to get irq: %d\n", ret); - goto out_removeirq; - } - - ret = gpiochip_add(&tc35892_gpio->chip); - if (ret) { - dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret); - goto out_freeirq; - } - - if (pdata->setup) - pdata->setup(tc35892, tc35892_gpio->chip.base); - - platform_set_drvdata(pdev, tc35892_gpio); - - return 0; - -out_freeirq: - free_irq(irq, tc35892_gpio); -out_removeirq: - tc35892_gpio_irq_remove(tc35892_gpio); -out_free: - kfree(tc35892_gpio); - return ret; -} - -static int __devexit tc35892_gpio_remove(struct platform_device *pdev) -{ - struct tc35892_gpio *tc35892_gpio = platform_get_drvdata(pdev); - struct tc35892 *tc35892 = tc35892_gpio->tc35892; - struct tc35892_gpio_platform_data *pdata = tc35892->pdata->gpio; - int irq = platform_get_irq(pdev, 0); - int ret; - - if (pdata->remove) - pdata->remove(tc35892, tc35892_gpio->chip.base); - - ret = gpiochip_remove(&tc35892_gpio->chip); - if (ret < 0) { - dev_err(tc35892_gpio->dev, - "unable to remove gpiochip: %d\n", ret); - return ret; - } - - free_irq(irq, tc35892_gpio); - tc35892_gpio_irq_remove(tc35892_gpio); - - platform_set_drvdata(pdev, NULL); - kfree(tc35892_gpio); - - return 0; -} - -static struct platform_driver tc35892_gpio_driver = { - .driver.name = "tc35892-gpio", - .driver.owner = THIS_MODULE, - .probe = tc35892_gpio_probe, - .remove = __devexit_p(tc35892_gpio_remove), -}; - -static int __init tc35892_gpio_init(void) -{ - return platform_driver_register(&tc35892_gpio_driver); -} -subsys_initcall(tc35892_gpio_init); - -static void __exit tc35892_gpio_exit(void) -{ - platform_driver_unregister(&tc35892_gpio_driver); -} -module_exit(tc35892_gpio_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("TC35892 GPIO driver"); -MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); diff --git a/drivers/gpio/tc3589x-gpio.c b/drivers/gpio/tc3589x-gpio.c new file mode 100644 index 00000000000..027b857c18f --- /dev/null +++ b/drivers/gpio/tc3589x-gpio.c @@ -0,0 +1,389 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License, version 2 + * Author: Hanumath Prasad for ST-Ericsson + * Author: Rabin Vincent for ST-Ericsson + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * These registers are modified under the irq bus lock and cached to avoid + * unnecessary writes in bus_sync_unlock. + */ +enum { REG_IBE, REG_IEV, REG_IS, REG_IE }; + +#define CACHE_NR_REGS 4 +#define CACHE_NR_BANKS 3 + +struct tc35892_gpio { + struct gpio_chip chip; + struct tc35892 *tc35892; + struct device *dev; + struct mutex irq_lock; + + int irq_base; + + /* Caches of interrupt control registers for bus_lock */ + u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS]; + u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS]; +}; + +static inline struct tc35892_gpio *to_tc35892_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct tc35892_gpio, chip); +} + +static int tc35892_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); + struct tc35892 *tc35892 = tc35892_gpio->tc35892; + u8 reg = TC35892_GPIODATA0 + (offset / 8) * 2; + u8 mask = 1 << (offset % 8); + int ret; + + ret = tc35892_reg_read(tc35892, reg); + if (ret < 0) + return ret; + + return ret & mask; +} + +static void tc35892_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +{ + struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); + struct tc35892 *tc35892 = tc35892_gpio->tc35892; + u8 reg = TC35892_GPIODATA0 + (offset / 8) * 2; + unsigned pos = offset % 8; + u8 data[] = {!!val << pos, 1 << pos}; + + tc35892_block_write(tc35892, reg, ARRAY_SIZE(data), data); +} + +static int tc35892_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int val) +{ + struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); + struct tc35892 *tc35892 = tc35892_gpio->tc35892; + u8 reg = TC35892_GPIODIR0 + offset / 8; + unsigned pos = offset % 8; + + tc35892_gpio_set(chip, offset, val); + + return tc35892_set_bits(tc35892, reg, 1 << pos, 1 << pos); +} + +static int tc35892_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); + struct tc35892 *tc35892 = tc35892_gpio->tc35892; + u8 reg = TC35892_GPIODIR0 + offset / 8; + unsigned pos = offset % 8; + + return tc35892_set_bits(tc35892, reg, 1 << pos, 0); +} + +static int tc35892_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); + + return tc35892_gpio->irq_base + offset; +} + +static struct gpio_chip template_chip = { + .label = "tc35892", + .owner = THIS_MODULE, + .direction_input = tc35892_gpio_direction_input, + .get = tc35892_gpio_get, + .direction_output = tc35892_gpio_direction_output, + .set = tc35892_gpio_set, + .to_irq = tc35892_gpio_to_irq, + .can_sleep = 1, +}; + +static int tc35892_gpio_irq_set_type(unsigned int irq, unsigned int type) +{ + struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); + int offset = irq - tc35892_gpio->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + if (type == IRQ_TYPE_EDGE_BOTH) { + tc35892_gpio->regs[REG_IBE][regoffset] |= mask; + return 0; + } + + tc35892_gpio->regs[REG_IBE][regoffset] &= ~mask; + + if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH) + tc35892_gpio->regs[REG_IS][regoffset] |= mask; + else + tc35892_gpio->regs[REG_IS][regoffset] &= ~mask; + + if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH) + tc35892_gpio->regs[REG_IEV][regoffset] |= mask; + else + tc35892_gpio->regs[REG_IEV][regoffset] &= ~mask; + + return 0; +} + +static void tc35892_gpio_irq_lock(unsigned int irq) +{ + struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); + + mutex_lock(&tc35892_gpio->irq_lock); +} + +static void tc35892_gpio_irq_sync_unlock(unsigned int irq) +{ + struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); + struct tc35892 *tc35892 = tc35892_gpio->tc35892; + static const u8 regmap[] = { + [REG_IBE] = TC35892_GPIOIBE0, + [REG_IEV] = TC35892_GPIOIEV0, + [REG_IS] = TC35892_GPIOIS0, + [REG_IE] = TC35892_GPIOIE0, + }; + int i, j; + + for (i = 0; i < CACHE_NR_REGS; i++) { + for (j = 0; j < CACHE_NR_BANKS; j++) { + u8 old = tc35892_gpio->oldregs[i][j]; + u8 new = tc35892_gpio->regs[i][j]; + + if (new == old) + continue; + + tc35892_gpio->oldregs[i][j] = new; + tc35892_reg_write(tc35892, regmap[i] + j * 8, new); + } + } + + mutex_unlock(&tc35892_gpio->irq_lock); +} + +static void tc35892_gpio_irq_mask(unsigned int irq) +{ + struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); + int offset = irq - tc35892_gpio->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + tc35892_gpio->regs[REG_IE][regoffset] &= ~mask; +} + +static void tc35892_gpio_irq_unmask(unsigned int irq) +{ + struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); + int offset = irq - tc35892_gpio->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + tc35892_gpio->regs[REG_IE][regoffset] |= mask; +} + +static struct irq_chip tc35892_gpio_irq_chip = { + .name = "tc35892-gpio", + .bus_lock = tc35892_gpio_irq_lock, + .bus_sync_unlock = tc35892_gpio_irq_sync_unlock, + .mask = tc35892_gpio_irq_mask, + .unmask = tc35892_gpio_irq_unmask, + .set_type = tc35892_gpio_irq_set_type, +}; + +static irqreturn_t tc35892_gpio_irq(int irq, void *dev) +{ + struct tc35892_gpio *tc35892_gpio = dev; + struct tc35892 *tc35892 = tc35892_gpio->tc35892; + u8 status[CACHE_NR_BANKS]; + int ret; + int i; + + ret = tc35892_block_read(tc35892, TC35892_GPIOMIS0, + ARRAY_SIZE(status), status); + if (ret < 0) + return IRQ_NONE; + + for (i = 0; i < ARRAY_SIZE(status); i++) { + unsigned int stat = status[i]; + if (!stat) + continue; + + while (stat) { + int bit = __ffs(stat); + int line = i * 8 + bit; + + handle_nested_irq(tc35892_gpio->irq_base + line); + stat &= ~(1 << bit); + } + + tc35892_reg_write(tc35892, TC35892_GPIOIC0 + i, status[i]); + } + + return IRQ_HANDLED; +} + +static int tc35892_gpio_irq_init(struct tc35892_gpio *tc35892_gpio) +{ + int base = tc35892_gpio->irq_base; + int irq; + + for (irq = base; irq < base + tc35892_gpio->chip.ngpio; irq++) { + set_irq_chip_data(irq, tc35892_gpio); + set_irq_chip_and_handler(irq, &tc35892_gpio_irq_chip, + handle_simple_irq); + set_irq_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + set_irq_noprobe(irq); +#endif + } + + return 0; +} + +static void tc35892_gpio_irq_remove(struct tc35892_gpio *tc35892_gpio) +{ + int base = tc35892_gpio->irq_base; + int irq; + + for (irq = base; irq < base + tc35892_gpio->chip.ngpio; irq++) { +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + set_irq_chip_and_handler(irq, NULL, NULL); + set_irq_chip_data(irq, NULL); + } +} + +static int __devinit tc35892_gpio_probe(struct platform_device *pdev) +{ + struct tc35892 *tc35892 = dev_get_drvdata(pdev->dev.parent); + struct tc35892_gpio_platform_data *pdata; + struct tc35892_gpio *tc35892_gpio; + int ret; + int irq; + + pdata = tc35892->pdata->gpio; + if (!pdata) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + tc35892_gpio = kzalloc(sizeof(struct tc35892_gpio), GFP_KERNEL); + if (!tc35892_gpio) + return -ENOMEM; + + mutex_init(&tc35892_gpio->irq_lock); + + tc35892_gpio->dev = &pdev->dev; + tc35892_gpio->tc35892 = tc35892; + + tc35892_gpio->chip = template_chip; + tc35892_gpio->chip.ngpio = tc35892->num_gpio; + tc35892_gpio->chip.dev = &pdev->dev; + tc35892_gpio->chip.base = pdata->gpio_base; + + tc35892_gpio->irq_base = tc35892->irq_base + TC35892_INT_GPIO(0); + + /* Bring the GPIO module out of reset */ + ret = tc35892_set_bits(tc35892, TC35892_RSTCTRL, + TC35892_RSTCTRL_GPIRST, 0); + if (ret < 0) + goto out_free; + + ret = tc35892_gpio_irq_init(tc35892_gpio); + if (ret) + goto out_free; + + ret = request_threaded_irq(irq, NULL, tc35892_gpio_irq, IRQF_ONESHOT, + "tc35892-gpio", tc35892_gpio); + if (ret) { + dev_err(&pdev->dev, "unable to get irq: %d\n", ret); + goto out_removeirq; + } + + ret = gpiochip_add(&tc35892_gpio->chip); + if (ret) { + dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret); + goto out_freeirq; + } + + if (pdata->setup) + pdata->setup(tc35892, tc35892_gpio->chip.base); + + platform_set_drvdata(pdev, tc35892_gpio); + + return 0; + +out_freeirq: + free_irq(irq, tc35892_gpio); +out_removeirq: + tc35892_gpio_irq_remove(tc35892_gpio); +out_free: + kfree(tc35892_gpio); + return ret; +} + +static int __devexit tc35892_gpio_remove(struct platform_device *pdev) +{ + struct tc35892_gpio *tc35892_gpio = platform_get_drvdata(pdev); + struct tc35892 *tc35892 = tc35892_gpio->tc35892; + struct tc35892_gpio_platform_data *pdata = tc35892->pdata->gpio; + int irq = platform_get_irq(pdev, 0); + int ret; + + if (pdata->remove) + pdata->remove(tc35892, tc35892_gpio->chip.base); + + ret = gpiochip_remove(&tc35892_gpio->chip); + if (ret < 0) { + dev_err(tc35892_gpio->dev, + "unable to remove gpiochip: %d\n", ret); + return ret; + } + + free_irq(irq, tc35892_gpio); + tc35892_gpio_irq_remove(tc35892_gpio); + + platform_set_drvdata(pdev, NULL); + kfree(tc35892_gpio); + + return 0; +} + +static struct platform_driver tc35892_gpio_driver = { + .driver.name = "tc35892-gpio", + .driver.owner = THIS_MODULE, + .probe = tc35892_gpio_probe, + .remove = __devexit_p(tc35892_gpio_remove), +}; + +static int __init tc35892_gpio_init(void) +{ + return platform_driver_register(&tc35892_gpio_driver); +} +subsys_initcall(tc35892_gpio_init); + +static void __exit tc35892_gpio_exit(void) +{ + platform_driver_unregister(&tc35892_gpio_driver); +} +module_exit(tc35892_gpio_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TC35892 GPIO driver"); +MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3a1493b8b5e..e8e704f5274 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -218,12 +218,12 @@ config MFD_STMPE Keypad: stmpe-keypad Touchscreen: stmpe-ts -config MFD_TC35892 - bool "Support Toshiba TC35892" +config MFD_TC3589X + bool "Support Toshiba TC35892 and variants" depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE help - Support for the Toshiba TC35892 I/O Expander. + Support for the Toshiba TC35892 and variants I/O Expander. This driver provides common support for accessing the device, additional drivers must be enabled in order to use the diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f54b3659abb..e590d1e44cf 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -16,7 +16,7 @@ obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o obj-$(CONFIG_MFD_STMPE) += stmpe.o -obj-$(CONFIG_MFD_TC35892) += tc35892.o +obj-$(CONFIG_MFD_TC3589X) += tc3589x.o obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o diff --git a/drivers/mfd/tc35892.c b/drivers/mfd/tc35892.c deleted file mode 100644 index f230235c376..00000000000 --- a/drivers/mfd/tc35892.c +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * License Terms: GNU General Public License, version 2 - * Author: Hanumath Prasad for ST-Ericsson - * Author: Rabin Vincent for ST-Ericsson - */ - -#include -#include -#include -#include -#include -#include -#include - -/** - * tc35892_reg_read() - read a single TC35892 register - * @tc35892: Device to read from - * @reg: Register to read - */ -int tc35892_reg_read(struct tc35892 *tc35892, u8 reg) -{ - int ret; - - ret = i2c_smbus_read_byte_data(tc35892->i2c, reg); - if (ret < 0) - dev_err(tc35892->dev, "failed to read reg %#x: %d\n", - reg, ret); - - return ret; -} -EXPORT_SYMBOL_GPL(tc35892_reg_read); - -/** - * tc35892_reg_read() - write a single TC35892 register - * @tc35892: Device to write to - * @reg: Register to read - * @data: Value to write - */ -int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data) -{ - int ret; - - ret = i2c_smbus_write_byte_data(tc35892->i2c, reg, data); - if (ret < 0) - dev_err(tc35892->dev, "failed to write reg %#x: %d\n", - reg, ret); - - return ret; -} -EXPORT_SYMBOL_GPL(tc35892_reg_write); - -/** - * tc35892_block_read() - read multiple TC35892 registers - * @tc35892: Device to read from - * @reg: First register - * @length: Number of registers - * @values: Buffer to write to - */ -int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length, u8 *values) -{ - int ret; - - ret = i2c_smbus_read_i2c_block_data(tc35892->i2c, reg, length, values); - if (ret < 0) - dev_err(tc35892->dev, "failed to read regs %#x: %d\n", - reg, ret); - - return ret; -} -EXPORT_SYMBOL_GPL(tc35892_block_read); - -/** - * tc35892_block_write() - write multiple TC35892 registers - * @tc35892: Device to write to - * @reg: First register - * @length: Number of registers - * @values: Values to write - */ -int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length, - const u8 *values) -{ - int ret; - - ret = i2c_smbus_write_i2c_block_data(tc35892->i2c, reg, length, - values); - if (ret < 0) - dev_err(tc35892->dev, "failed to write regs %#x: %d\n", - reg, ret); - - return ret; -} -EXPORT_SYMBOL_GPL(tc35892_block_write); - -/** - * tc35892_set_bits() - set the value of a bitfield in a TC35892 register - * @tc35892: Device to write to - * @reg: Register to write - * @mask: Mask of bits to set - * @values: Value to set - */ -int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val) -{ - int ret; - - mutex_lock(&tc35892->lock); - - ret = tc35892_reg_read(tc35892, reg); - if (ret < 0) - goto out; - - ret &= ~mask; - ret |= val; - - ret = tc35892_reg_write(tc35892, reg, ret); - -out: - mutex_unlock(&tc35892->lock); - return ret; -} -EXPORT_SYMBOL_GPL(tc35892_set_bits); - -static struct resource gpio_resources[] = { - { - .start = TC35892_INT_GPIIRQ, - .end = TC35892_INT_GPIIRQ, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct mfd_cell tc35892_devs[] = { - { - .name = "tc35892-gpio", - .num_resources = ARRAY_SIZE(gpio_resources), - .resources = &gpio_resources[0], - }, -}; - -static irqreturn_t tc35892_irq(int irq, void *data) -{ - struct tc35892 *tc35892 = data; - int status; - - status = tc35892_reg_read(tc35892, TC35892_IRQST); - if (status < 0) - return IRQ_NONE; - - while (status) { - int bit = __ffs(status); - - handle_nested_irq(tc35892->irq_base + bit); - status &= ~(1 << bit); - } - - /* - * A dummy read or write (to any register) appears to be necessary to - * have the last interrupt clear (for example, GPIO IC write) take - * effect. - */ - tc35892_reg_read(tc35892, TC35892_IRQST); - - return IRQ_HANDLED; -} - -static void tc35892_irq_dummy(unsigned int irq) -{ - /* No mask/unmask at this level */ -} - -static struct irq_chip tc35892_irq_chip = { - .name = "tc35892", - .mask = tc35892_irq_dummy, - .unmask = tc35892_irq_dummy, -}; - -static int tc35892_irq_init(struct tc35892 *tc35892) -{ - int base = tc35892->irq_base; - int irq; - - for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) { - set_irq_chip_data(irq, tc35892); - set_irq_chip_and_handler(irq, &tc35892_irq_chip, - handle_edge_irq); - set_irq_nested_thread(irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); -#else - set_irq_noprobe(irq); -#endif - } - - return 0; -} - -static void tc35892_irq_remove(struct tc35892 *tc35892) -{ - int base = tc35892->irq_base; - int irq; - - for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) { -#ifdef CONFIG_ARM - set_irq_flags(irq, 0); -#endif - set_irq_chip_and_handler(irq, NULL, NULL); - set_irq_chip_data(irq, NULL); - } -} - -static int tc35892_chip_init(struct tc35892 *tc35892) -{ - int manf, ver, ret; - - manf = tc35892_reg_read(tc35892, TC35892_MANFCODE); - if (manf < 0) - return manf; - - ver = tc35892_reg_read(tc35892, TC35892_VERSION); - if (ver < 0) - return ver; - - if (manf != TC35892_MANFCODE_MAGIC) { - dev_err(tc35892->dev, "unknown manufacturer: %#x\n", manf); - return -EINVAL; - } - - dev_info(tc35892->dev, "manufacturer: %#x, version: %#x\n", manf, ver); - - /* Put everything except the IRQ module into reset */ - ret = tc35892_reg_write(tc35892, TC35892_RSTCTRL, - TC35892_RSTCTRL_TIMRST - | TC35892_RSTCTRL_ROTRST - | TC35892_RSTCTRL_KBDRST - | TC35892_RSTCTRL_GPIRST); - if (ret < 0) - return ret; - - /* Clear the reset interrupt. */ - return tc35892_reg_write(tc35892, TC35892_RSTINTCLR, 0x1); -} - -static int __devinit tc35892_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) -{ - struct tc35892_platform_data *pdata = i2c->dev.platform_data; - struct tc35892 *tc35892; - int ret; - - if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA - | I2C_FUNC_SMBUS_I2C_BLOCK)) - return -EIO; - - tc35892 = kzalloc(sizeof(struct tc35892), GFP_KERNEL); - if (!tc35892) - return -ENOMEM; - - mutex_init(&tc35892->lock); - - tc35892->dev = &i2c->dev; - tc35892->i2c = i2c; - tc35892->pdata = pdata; - tc35892->irq_base = pdata->irq_base; - tc35892->num_gpio = id->driver_data; - - i2c_set_clientdata(i2c, tc35892); - - ret = tc35892_chip_init(tc35892); - if (ret) - goto out_free; - - ret = tc35892_irq_init(tc35892); - if (ret) - goto out_free; - - ret = request_threaded_irq(tc35892->i2c->irq, NULL, tc35892_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "tc35892", tc35892); - if (ret) { - dev_err(tc35892->dev, "failed to request IRQ: %d\n", ret); - goto out_removeirq; - } - - ret = mfd_add_devices(tc35892->dev, -1, tc35892_devs, - ARRAY_SIZE(tc35892_devs), NULL, - tc35892->irq_base); - if (ret) { - dev_err(tc35892->dev, "failed to add children\n"); - goto out_freeirq; - } - - return 0; - -out_freeirq: - free_irq(tc35892->i2c->irq, tc35892); -out_removeirq: - tc35892_irq_remove(tc35892); -out_free: - kfree(tc35892); - return ret; -} - -static int __devexit tc35892_remove(struct i2c_client *client) -{ - struct tc35892 *tc35892 = i2c_get_clientdata(client); - - mfd_remove_devices(tc35892->dev); - - free_irq(tc35892->i2c->irq, tc35892); - tc35892_irq_remove(tc35892); - - kfree(tc35892); - - return 0; -} - -static const struct i2c_device_id tc35892_id[] = { - { "tc35892", 24 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tc35892_id); - -static struct i2c_driver tc35892_driver = { - .driver.name = "tc35892", - .driver.owner = THIS_MODULE, - .probe = tc35892_probe, - .remove = __devexit_p(tc35892_remove), - .id_table = tc35892_id, -}; - -static int __init tc35892_init(void) -{ - return i2c_add_driver(&tc35892_driver); -} -subsys_initcall(tc35892_init); - -static void __exit tc35892_exit(void) -{ - i2c_del_driver(&tc35892_driver); -} -module_exit(tc35892_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("TC35892 MFD core driver"); -MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c new file mode 100644 index 00000000000..f230235c376 --- /dev/null +++ b/drivers/mfd/tc3589x.c @@ -0,0 +1,345 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License, version 2 + * Author: Hanumath Prasad for ST-Ericsson + * Author: Rabin Vincent for ST-Ericsson + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * tc35892_reg_read() - read a single TC35892 register + * @tc35892: Device to read from + * @reg: Register to read + */ +int tc35892_reg_read(struct tc35892 *tc35892, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(tc35892->i2c, reg); + if (ret < 0) + dev_err(tc35892->dev, "failed to read reg %#x: %d\n", + reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(tc35892_reg_read); + +/** + * tc35892_reg_read() - write a single TC35892 register + * @tc35892: Device to write to + * @reg: Register to read + * @data: Value to write + */ +int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data) +{ + int ret; + + ret = i2c_smbus_write_byte_data(tc35892->i2c, reg, data); + if (ret < 0) + dev_err(tc35892->dev, "failed to write reg %#x: %d\n", + reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(tc35892_reg_write); + +/** + * tc35892_block_read() - read multiple TC35892 registers + * @tc35892: Device to read from + * @reg: First register + * @length: Number of registers + * @values: Buffer to write to + */ +int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length, u8 *values) +{ + int ret; + + ret = i2c_smbus_read_i2c_block_data(tc35892->i2c, reg, length, values); + if (ret < 0) + dev_err(tc35892->dev, "failed to read regs %#x: %d\n", + reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(tc35892_block_read); + +/** + * tc35892_block_write() - write multiple TC35892 registers + * @tc35892: Device to write to + * @reg: First register + * @length: Number of registers + * @values: Values to write + */ +int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length, + const u8 *values) +{ + int ret; + + ret = i2c_smbus_write_i2c_block_data(tc35892->i2c, reg, length, + values); + if (ret < 0) + dev_err(tc35892->dev, "failed to write regs %#x: %d\n", + reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(tc35892_block_write); + +/** + * tc35892_set_bits() - set the value of a bitfield in a TC35892 register + * @tc35892: Device to write to + * @reg: Register to write + * @mask: Mask of bits to set + * @values: Value to set + */ +int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val) +{ + int ret; + + mutex_lock(&tc35892->lock); + + ret = tc35892_reg_read(tc35892, reg); + if (ret < 0) + goto out; + + ret &= ~mask; + ret |= val; + + ret = tc35892_reg_write(tc35892, reg, ret); + +out: + mutex_unlock(&tc35892->lock); + return ret; +} +EXPORT_SYMBOL_GPL(tc35892_set_bits); + +static struct resource gpio_resources[] = { + { + .start = TC35892_INT_GPIIRQ, + .end = TC35892_INT_GPIIRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell tc35892_devs[] = { + { + .name = "tc35892-gpio", + .num_resources = ARRAY_SIZE(gpio_resources), + .resources = &gpio_resources[0], + }, +}; + +static irqreturn_t tc35892_irq(int irq, void *data) +{ + struct tc35892 *tc35892 = data; + int status; + + status = tc35892_reg_read(tc35892, TC35892_IRQST); + if (status < 0) + return IRQ_NONE; + + while (status) { + int bit = __ffs(status); + + handle_nested_irq(tc35892->irq_base + bit); + status &= ~(1 << bit); + } + + /* + * A dummy read or write (to any register) appears to be necessary to + * have the last interrupt clear (for example, GPIO IC write) take + * effect. + */ + tc35892_reg_read(tc35892, TC35892_IRQST); + + return IRQ_HANDLED; +} + +static void tc35892_irq_dummy(unsigned int irq) +{ + /* No mask/unmask at this level */ +} + +static struct irq_chip tc35892_irq_chip = { + .name = "tc35892", + .mask = tc35892_irq_dummy, + .unmask = tc35892_irq_dummy, +}; + +static int tc35892_irq_init(struct tc35892 *tc35892) +{ + int base = tc35892->irq_base; + int irq; + + for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) { + set_irq_chip_data(irq, tc35892); + set_irq_chip_and_handler(irq, &tc35892_irq_chip, + handle_edge_irq); + set_irq_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + set_irq_noprobe(irq); +#endif + } + + return 0; +} + +static void tc35892_irq_remove(struct tc35892 *tc35892) +{ + int base = tc35892->irq_base; + int irq; + + for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) { +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + set_irq_chip_and_handler(irq, NULL, NULL); + set_irq_chip_data(irq, NULL); + } +} + +static int tc35892_chip_init(struct tc35892 *tc35892) +{ + int manf, ver, ret; + + manf = tc35892_reg_read(tc35892, TC35892_MANFCODE); + if (manf < 0) + return manf; + + ver = tc35892_reg_read(tc35892, TC35892_VERSION); + if (ver < 0) + return ver; + + if (manf != TC35892_MANFCODE_MAGIC) { + dev_err(tc35892->dev, "unknown manufacturer: %#x\n", manf); + return -EINVAL; + } + + dev_info(tc35892->dev, "manufacturer: %#x, version: %#x\n", manf, ver); + + /* Put everything except the IRQ module into reset */ + ret = tc35892_reg_write(tc35892, TC35892_RSTCTRL, + TC35892_RSTCTRL_TIMRST + | TC35892_RSTCTRL_ROTRST + | TC35892_RSTCTRL_KBDRST + | TC35892_RSTCTRL_GPIRST); + if (ret < 0) + return ret; + + /* Clear the reset interrupt. */ + return tc35892_reg_write(tc35892, TC35892_RSTINTCLR, 0x1); +} + +static int __devinit tc35892_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct tc35892_platform_data *pdata = i2c->dev.platform_data; + struct tc35892 *tc35892; + int ret; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_I2C_BLOCK)) + return -EIO; + + tc35892 = kzalloc(sizeof(struct tc35892), GFP_KERNEL); + if (!tc35892) + return -ENOMEM; + + mutex_init(&tc35892->lock); + + tc35892->dev = &i2c->dev; + tc35892->i2c = i2c; + tc35892->pdata = pdata; + tc35892->irq_base = pdata->irq_base; + tc35892->num_gpio = id->driver_data; + + i2c_set_clientdata(i2c, tc35892); + + ret = tc35892_chip_init(tc35892); + if (ret) + goto out_free; + + ret = tc35892_irq_init(tc35892); + if (ret) + goto out_free; + + ret = request_threaded_irq(tc35892->i2c->irq, NULL, tc35892_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "tc35892", tc35892); + if (ret) { + dev_err(tc35892->dev, "failed to request IRQ: %d\n", ret); + goto out_removeirq; + } + + ret = mfd_add_devices(tc35892->dev, -1, tc35892_devs, + ARRAY_SIZE(tc35892_devs), NULL, + tc35892->irq_base); + if (ret) { + dev_err(tc35892->dev, "failed to add children\n"); + goto out_freeirq; + } + + return 0; + +out_freeirq: + free_irq(tc35892->i2c->irq, tc35892); +out_removeirq: + tc35892_irq_remove(tc35892); +out_free: + kfree(tc35892); + return ret; +} + +static int __devexit tc35892_remove(struct i2c_client *client) +{ + struct tc35892 *tc35892 = i2c_get_clientdata(client); + + mfd_remove_devices(tc35892->dev); + + free_irq(tc35892->i2c->irq, tc35892); + tc35892_irq_remove(tc35892); + + kfree(tc35892); + + return 0; +} + +static const struct i2c_device_id tc35892_id[] = { + { "tc35892", 24 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tc35892_id); + +static struct i2c_driver tc35892_driver = { + .driver.name = "tc35892", + .driver.owner = THIS_MODULE, + .probe = tc35892_probe, + .remove = __devexit_p(tc35892_remove), + .id_table = tc35892_id, +}; + +static int __init tc35892_init(void) +{ + return i2c_add_driver(&tc35892_driver); +} +subsys_initcall(tc35892_init); + +static void __exit tc35892_exit(void) +{ + i2c_del_driver(&tc35892_driver); +} +module_exit(tc35892_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TC35892 MFD core driver"); +MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); -- cgit v1.2.3-70-g09d2 From 20406ebff4a298e6e3abbc1717a90bb3e55dc820 Mon Sep 17 00:00:00 2001 From: Sundar Iyer Date: Mon, 13 Dec 2010 09:33:14 +0530 Subject: mfd/tc3589x: rename tc35892 structs/registers to tc359x Most of the register layout, client IRQ numbers on the TC35892 is shared also by other variants. Make this generic as tc3589x Acked-by: Samuel Ortiz Signed-off-by: Sundar Iyer Signed-off-by: Linus Walleij --- arch/arm/mach-ux500/board-mop500.c | 12 +- drivers/gpio/tc3589x-gpio.c | 268 ++++++++++++++++++------------------- drivers/mfd/tc3589x.c | 222 +++++++++++++++--------------- include/linux/mfd/tc3589x.h | 202 ++++++++++++++-------------- 4 files changed, 351 insertions(+), 353 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index a8220c5a6a6..5c950261968 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -19,8 +19,7 @@ #include #include #include -#include -#include +#include #include #include @@ -112,24 +111,24 @@ static struct pl022_ssp_controller ssp0_platform_data = { * TC35892 */ -static void mop500_tc35892_init(struct tc35892 *tc35892, unsigned int base) +static void mop500_tc35892_init(struct tc3589x *tc3589x, unsigned int base) { mop500_sdi_tc35892_init(); } -static struct tc35892_gpio_platform_data mop500_tc35892_gpio_data = { +static struct tc3589x_gpio_platform_data mop500_tc35892_gpio_data = { .gpio_base = MOP500_EGPIO(0), .setup = mop500_tc35892_init, }; -static struct tc35892_platform_data mop500_tc35892_data = { +static struct tc3589x_platform_data mop500_tc35892_data = { .gpio = &mop500_tc35892_gpio_data, .irq_base = MOP500_EGPIO_IRQ_BASE, }; static struct i2c_board_info mop500_i2c0_devices[] = { { - I2C_BOARD_INFO("tc35892", 0x42), + I2C_BOARD_INFO("tc3589x", 0x42), .irq = NOMADIK_GPIO_TO_IRQ(217), .platform_data = &mop500_tc35892_data, }, @@ -302,7 +301,6 @@ static void __init u8500_init_machine(void) nmk_config_pins(mop500_pins, ARRAY_SIZE(mop500_pins)); - ux500_ske_keypad_device.dev.platform_data = &ske_keypad_board; platform_add_devices(platform_devs, ARRAY_SIZE(platform_devs)); mop500_i2c_init(); diff --git a/drivers/gpio/tc3589x-gpio.c b/drivers/gpio/tc3589x-gpio.c index 027b857c18f..180d584454f 100644 --- a/drivers/gpio/tc3589x-gpio.c +++ b/drivers/gpio/tc3589x-gpio.c @@ -24,9 +24,9 @@ enum { REG_IBE, REG_IEV, REG_IS, REG_IE }; #define CACHE_NR_REGS 4 #define CACHE_NR_BANKS 3 -struct tc35892_gpio { +struct tc3589x_gpio { struct gpio_chip chip; - struct tc35892 *tc35892; + struct tc3589x *tc3589x; struct device *dev; struct mutex irq_lock; @@ -37,179 +37,179 @@ struct tc35892_gpio { u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS]; }; -static inline struct tc35892_gpio *to_tc35892_gpio(struct gpio_chip *chip) +static inline struct tc3589x_gpio *to_tc3589x_gpio(struct gpio_chip *chip) { - return container_of(chip, struct tc35892_gpio, chip); + return container_of(chip, struct tc3589x_gpio, chip); } -static int tc35892_gpio_get(struct gpio_chip *chip, unsigned offset) +static int tc3589x_gpio_get(struct gpio_chip *chip, unsigned offset) { - struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); - struct tc35892 *tc35892 = tc35892_gpio->tc35892; - u8 reg = TC35892_GPIODATA0 + (offset / 8) * 2; + struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip); + struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; + u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2; u8 mask = 1 << (offset % 8); int ret; - ret = tc35892_reg_read(tc35892, reg); + ret = tc3589x_reg_read(tc3589x, reg); if (ret < 0) return ret; return ret & mask; } -static void tc35892_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +static void tc3589x_gpio_set(struct gpio_chip *chip, unsigned offset, int val) { - struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); - struct tc35892 *tc35892 = tc35892_gpio->tc35892; - u8 reg = TC35892_GPIODATA0 + (offset / 8) * 2; + struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip); + struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; + u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2; unsigned pos = offset % 8; u8 data[] = {!!val << pos, 1 << pos}; - tc35892_block_write(tc35892, reg, ARRAY_SIZE(data), data); + tc3589x_block_write(tc3589x, reg, ARRAY_SIZE(data), data); } -static int tc35892_gpio_direction_output(struct gpio_chip *chip, +static int tc3589x_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int val) { - struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); - struct tc35892 *tc35892 = tc35892_gpio->tc35892; - u8 reg = TC35892_GPIODIR0 + offset / 8; + struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip); + struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; + u8 reg = TC3589x_GPIODIR0 + offset / 8; unsigned pos = offset % 8; - tc35892_gpio_set(chip, offset, val); + tc3589x_gpio_set(chip, offset, val); - return tc35892_set_bits(tc35892, reg, 1 << pos, 1 << pos); + return tc3589x_set_bits(tc3589x, reg, 1 << pos, 1 << pos); } -static int tc35892_gpio_direction_input(struct gpio_chip *chip, +static int tc3589x_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { - struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); - struct tc35892 *tc35892 = tc35892_gpio->tc35892; - u8 reg = TC35892_GPIODIR0 + offset / 8; + struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip); + struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; + u8 reg = TC3589x_GPIODIR0 + offset / 8; unsigned pos = offset % 8; - return tc35892_set_bits(tc35892, reg, 1 << pos, 0); + return tc3589x_set_bits(tc3589x, reg, 1 << pos, 0); } -static int tc35892_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +static int tc3589x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { - struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); + struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip); - return tc35892_gpio->irq_base + offset; + return tc3589x_gpio->irq_base + offset; } static struct gpio_chip template_chip = { - .label = "tc35892", + .label = "tc3589x", .owner = THIS_MODULE, - .direction_input = tc35892_gpio_direction_input, - .get = tc35892_gpio_get, - .direction_output = tc35892_gpio_direction_output, - .set = tc35892_gpio_set, - .to_irq = tc35892_gpio_to_irq, + .direction_input = tc3589x_gpio_direction_input, + .get = tc3589x_gpio_get, + .direction_output = tc3589x_gpio_direction_output, + .set = tc3589x_gpio_set, + .to_irq = tc3589x_gpio_to_irq, .can_sleep = 1, }; -static int tc35892_gpio_irq_set_type(unsigned int irq, unsigned int type) +static int tc3589x_gpio_irq_set_type(unsigned int irq, unsigned int type) { - struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); - int offset = irq - tc35892_gpio->irq_base; + struct tc3589x_gpio *tc3589x_gpio = get_irq_chip_data(irq); + int offset = irq - tc3589x_gpio->irq_base; int regoffset = offset / 8; int mask = 1 << (offset % 8); if (type == IRQ_TYPE_EDGE_BOTH) { - tc35892_gpio->regs[REG_IBE][regoffset] |= mask; + tc3589x_gpio->regs[REG_IBE][regoffset] |= mask; return 0; } - tc35892_gpio->regs[REG_IBE][regoffset] &= ~mask; + tc3589x_gpio->regs[REG_IBE][regoffset] &= ~mask; if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH) - tc35892_gpio->regs[REG_IS][regoffset] |= mask; + tc3589x_gpio->regs[REG_IS][regoffset] |= mask; else - tc35892_gpio->regs[REG_IS][regoffset] &= ~mask; + tc3589x_gpio->regs[REG_IS][regoffset] &= ~mask; if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH) - tc35892_gpio->regs[REG_IEV][regoffset] |= mask; + tc3589x_gpio->regs[REG_IEV][regoffset] |= mask; else - tc35892_gpio->regs[REG_IEV][regoffset] &= ~mask; + tc3589x_gpio->regs[REG_IEV][regoffset] &= ~mask; return 0; } -static void tc35892_gpio_irq_lock(unsigned int irq) +static void tc3589x_gpio_irq_lock(unsigned int irq) { - struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); + struct tc3589x_gpio *tc3589x_gpio = get_irq_chip_data(irq); - mutex_lock(&tc35892_gpio->irq_lock); + mutex_lock(&tc3589x_gpio->irq_lock); } -static void tc35892_gpio_irq_sync_unlock(unsigned int irq) +static void tc3589x_gpio_irq_sync_unlock(unsigned int irq) { - struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); - struct tc35892 *tc35892 = tc35892_gpio->tc35892; + struct tc3589x_gpio *tc3589x_gpio = get_irq_chip_data(irq); + struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; static const u8 regmap[] = { - [REG_IBE] = TC35892_GPIOIBE0, - [REG_IEV] = TC35892_GPIOIEV0, - [REG_IS] = TC35892_GPIOIS0, - [REG_IE] = TC35892_GPIOIE0, + [REG_IBE] = TC3589x_GPIOIBE0, + [REG_IEV] = TC3589x_GPIOIEV0, + [REG_IS] = TC3589x_GPIOIS0, + [REG_IE] = TC3589x_GPIOIE0, }; int i, j; for (i = 0; i < CACHE_NR_REGS; i++) { for (j = 0; j < CACHE_NR_BANKS; j++) { - u8 old = tc35892_gpio->oldregs[i][j]; - u8 new = tc35892_gpio->regs[i][j]; + u8 old = tc3589x_gpio->oldregs[i][j]; + u8 new = tc3589x_gpio->regs[i][j]; if (new == old) continue; - tc35892_gpio->oldregs[i][j] = new; - tc35892_reg_write(tc35892, regmap[i] + j * 8, new); + tc3589x_gpio->oldregs[i][j] = new; + tc3589x_reg_write(tc3589x, regmap[i] + j * 8, new); } } - mutex_unlock(&tc35892_gpio->irq_lock); + mutex_unlock(&tc3589x_gpio->irq_lock); } -static void tc35892_gpio_irq_mask(unsigned int irq) +static void tc3589x_gpio_irq_mask(unsigned int irq) { - struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); - int offset = irq - tc35892_gpio->irq_base; + struct tc3589x_gpio *tc3589x_gpio = get_irq_chip_data(irq); + int offset = irq - tc3589x_gpio->irq_base; int regoffset = offset / 8; int mask = 1 << (offset % 8); - tc35892_gpio->regs[REG_IE][regoffset] &= ~mask; + tc3589x_gpio->regs[REG_IE][regoffset] &= ~mask; } -static void tc35892_gpio_irq_unmask(unsigned int irq) +static void tc3589x_gpio_irq_unmask(unsigned int irq) { - struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); - int offset = irq - tc35892_gpio->irq_base; + struct tc3589x_gpio *tc3589x_gpio = get_irq_chip_data(irq); + int offset = irq - tc3589x_gpio->irq_base; int regoffset = offset / 8; int mask = 1 << (offset % 8); - tc35892_gpio->regs[REG_IE][regoffset] |= mask; + tc3589x_gpio->regs[REG_IE][regoffset] |= mask; } -static struct irq_chip tc35892_gpio_irq_chip = { - .name = "tc35892-gpio", - .bus_lock = tc35892_gpio_irq_lock, - .bus_sync_unlock = tc35892_gpio_irq_sync_unlock, - .mask = tc35892_gpio_irq_mask, - .unmask = tc35892_gpio_irq_unmask, - .set_type = tc35892_gpio_irq_set_type, +static struct irq_chip tc3589x_gpio_irq_chip = { + .name = "tc3589x-gpio", + .bus_lock = tc3589x_gpio_irq_lock, + .bus_sync_unlock = tc3589x_gpio_irq_sync_unlock, + .mask = tc3589x_gpio_irq_mask, + .unmask = tc3589x_gpio_irq_unmask, + .set_type = tc3589x_gpio_irq_set_type, }; -static irqreturn_t tc35892_gpio_irq(int irq, void *dev) +static irqreturn_t tc3589x_gpio_irq(int irq, void *dev) { - struct tc35892_gpio *tc35892_gpio = dev; - struct tc35892 *tc35892 = tc35892_gpio->tc35892; + struct tc3589x_gpio *tc3589x_gpio = dev; + struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; u8 status[CACHE_NR_BANKS]; int ret; int i; - ret = tc35892_block_read(tc35892, TC35892_GPIOMIS0, + ret = tc3589x_block_read(tc3589x, TC3589x_GPIOMIS0, ARRAY_SIZE(status), status); if (ret < 0) return IRQ_NONE; @@ -223,24 +223,24 @@ static irqreturn_t tc35892_gpio_irq(int irq, void *dev) int bit = __ffs(stat); int line = i * 8 + bit; - handle_nested_irq(tc35892_gpio->irq_base + line); + handle_nested_irq(tc3589x_gpio->irq_base + line); stat &= ~(1 << bit); } - tc35892_reg_write(tc35892, TC35892_GPIOIC0 + i, status[i]); + tc3589x_reg_write(tc3589x, TC3589x_GPIOIC0 + i, status[i]); } return IRQ_HANDLED; } -static int tc35892_gpio_irq_init(struct tc35892_gpio *tc35892_gpio) +static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio) { - int base = tc35892_gpio->irq_base; + int base = tc3589x_gpio->irq_base; int irq; - for (irq = base; irq < base + tc35892_gpio->chip.ngpio; irq++) { - set_irq_chip_data(irq, tc35892_gpio); - set_irq_chip_and_handler(irq, &tc35892_gpio_irq_chip, + for (irq = base; irq < base + tc3589x_gpio->chip.ngpio; irq++) { + set_irq_chip_data(irq, tc3589x_gpio); + set_irq_chip_and_handler(irq, &tc3589x_gpio_irq_chip, handle_simple_irq); set_irq_nested_thread(irq, 1); #ifdef CONFIG_ARM @@ -253,12 +253,12 @@ static int tc35892_gpio_irq_init(struct tc35892_gpio *tc35892_gpio) return 0; } -static void tc35892_gpio_irq_remove(struct tc35892_gpio *tc35892_gpio) +static void tc3589x_gpio_irq_remove(struct tc3589x_gpio *tc3589x_gpio) { - int base = tc35892_gpio->irq_base; + int base = tc3589x_gpio->irq_base; int irq; - for (irq = base; irq < base + tc35892_gpio->chip.ngpio; irq++) { + for (irq = base; irq < base + tc3589x_gpio->chip.ngpio; irq++) { #ifdef CONFIG_ARM set_irq_flags(irq, 0); #endif @@ -267,15 +267,15 @@ static void tc35892_gpio_irq_remove(struct tc35892_gpio *tc35892_gpio) } } -static int __devinit tc35892_gpio_probe(struct platform_device *pdev) +static int __devinit tc3589x_gpio_probe(struct platform_device *pdev) { - struct tc35892 *tc35892 = dev_get_drvdata(pdev->dev.parent); - struct tc35892_gpio_platform_data *pdata; - struct tc35892_gpio *tc35892_gpio; + struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent); + struct tc3589x_gpio_platform_data *pdata; + struct tc3589x_gpio *tc3589x_gpio; int ret; int irq; - pdata = tc35892->pdata->gpio; + pdata = tc3589x->pdata->gpio; if (!pdata) return -ENODEV; @@ -283,107 +283,107 @@ static int __devinit tc35892_gpio_probe(struct platform_device *pdev) if (irq < 0) return irq; - tc35892_gpio = kzalloc(sizeof(struct tc35892_gpio), GFP_KERNEL); - if (!tc35892_gpio) + tc3589x_gpio = kzalloc(sizeof(struct tc3589x_gpio), GFP_KERNEL); + if (!tc3589x_gpio) return -ENOMEM; - mutex_init(&tc35892_gpio->irq_lock); + mutex_init(&tc3589x_gpio->irq_lock); - tc35892_gpio->dev = &pdev->dev; - tc35892_gpio->tc35892 = tc35892; + tc3589x_gpio->dev = &pdev->dev; + tc3589x_gpio->tc3589x = tc3589x; - tc35892_gpio->chip = template_chip; - tc35892_gpio->chip.ngpio = tc35892->num_gpio; - tc35892_gpio->chip.dev = &pdev->dev; - tc35892_gpio->chip.base = pdata->gpio_base; + tc3589x_gpio->chip = template_chip; + tc3589x_gpio->chip.ngpio = tc3589x->num_gpio; + tc3589x_gpio->chip.dev = &pdev->dev; + tc3589x_gpio->chip.base = pdata->gpio_base; - tc35892_gpio->irq_base = tc35892->irq_base + TC35892_INT_GPIO(0); + tc3589x_gpio->irq_base = tc3589x->irq_base + TC3589x_INT_GPIO(0); /* Bring the GPIO module out of reset */ - ret = tc35892_set_bits(tc35892, TC35892_RSTCTRL, - TC35892_RSTCTRL_GPIRST, 0); + ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, + TC3589x_RSTCTRL_GPIRST, 0); if (ret < 0) goto out_free; - ret = tc35892_gpio_irq_init(tc35892_gpio); + ret = tc3589x_gpio_irq_init(tc3589x_gpio); if (ret) goto out_free; - ret = request_threaded_irq(irq, NULL, tc35892_gpio_irq, IRQF_ONESHOT, - "tc35892-gpio", tc35892_gpio); + ret = request_threaded_irq(irq, NULL, tc3589x_gpio_irq, IRQF_ONESHOT, + "tc3589x-gpio", tc3589x_gpio); if (ret) { dev_err(&pdev->dev, "unable to get irq: %d\n", ret); goto out_removeirq; } - ret = gpiochip_add(&tc35892_gpio->chip); + ret = gpiochip_add(&tc3589x_gpio->chip); if (ret) { dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret); goto out_freeirq; } if (pdata->setup) - pdata->setup(tc35892, tc35892_gpio->chip.base); + pdata->setup(tc3589x, tc3589x_gpio->chip.base); - platform_set_drvdata(pdev, tc35892_gpio); + platform_set_drvdata(pdev, tc3589x_gpio); return 0; out_freeirq: - free_irq(irq, tc35892_gpio); + free_irq(irq, tc3589x_gpio); out_removeirq: - tc35892_gpio_irq_remove(tc35892_gpio); + tc3589x_gpio_irq_remove(tc3589x_gpio); out_free: - kfree(tc35892_gpio); + kfree(tc3589x_gpio); return ret; } -static int __devexit tc35892_gpio_remove(struct platform_device *pdev) +static int __devexit tc3589x_gpio_remove(struct platform_device *pdev) { - struct tc35892_gpio *tc35892_gpio = platform_get_drvdata(pdev); - struct tc35892 *tc35892 = tc35892_gpio->tc35892; - struct tc35892_gpio_platform_data *pdata = tc35892->pdata->gpio; + struct tc3589x_gpio *tc3589x_gpio = platform_get_drvdata(pdev); + struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; + struct tc3589x_gpio_platform_data *pdata = tc3589x->pdata->gpio; int irq = platform_get_irq(pdev, 0); int ret; if (pdata->remove) - pdata->remove(tc35892, tc35892_gpio->chip.base); + pdata->remove(tc3589x, tc3589x_gpio->chip.base); - ret = gpiochip_remove(&tc35892_gpio->chip); + ret = gpiochip_remove(&tc3589x_gpio->chip); if (ret < 0) { - dev_err(tc35892_gpio->dev, + dev_err(tc3589x_gpio->dev, "unable to remove gpiochip: %d\n", ret); return ret; } - free_irq(irq, tc35892_gpio); - tc35892_gpio_irq_remove(tc35892_gpio); + free_irq(irq, tc3589x_gpio); + tc3589x_gpio_irq_remove(tc3589x_gpio); platform_set_drvdata(pdev, NULL); - kfree(tc35892_gpio); + kfree(tc3589x_gpio); return 0; } -static struct platform_driver tc35892_gpio_driver = { - .driver.name = "tc35892-gpio", +static struct platform_driver tc3589x_gpio_driver = { + .driver.name = "tc3589x-gpio", .driver.owner = THIS_MODULE, - .probe = tc35892_gpio_probe, - .remove = __devexit_p(tc35892_gpio_remove), + .probe = tc3589x_gpio_probe, + .remove = __devexit_p(tc3589x_gpio_remove), }; -static int __init tc35892_gpio_init(void) +static int __init tc3589x_gpio_init(void) { - return platform_driver_register(&tc35892_gpio_driver); + return platform_driver_register(&tc3589x_gpio_driver); } -subsys_initcall(tc35892_gpio_init); +subsys_initcall(tc3589x_gpio_init); -static void __exit tc35892_gpio_exit(void) +static void __exit tc3589x_gpio_exit(void) { - platform_driver_unregister(&tc35892_gpio_driver); + platform_driver_unregister(&tc3589x_gpio_driver); } -module_exit(tc35892_gpio_exit); +module_exit(tc3589x_gpio_exit); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("TC35892 GPIO driver"); +MODULE_DESCRIPTION("TC3589x GPIO driver"); MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index f230235c376..7deff53181d 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -15,141 +15,141 @@ #include /** - * tc35892_reg_read() - read a single TC35892 register - * @tc35892: Device to read from + * tc3589x_reg_read() - read a single TC3589x register + * @tc3589x: Device to read from * @reg: Register to read */ -int tc35892_reg_read(struct tc35892 *tc35892, u8 reg) +int tc3589x_reg_read(struct tc3589x *tc3589x, u8 reg) { int ret; - ret = i2c_smbus_read_byte_data(tc35892->i2c, reg); + ret = i2c_smbus_read_byte_data(tc3589x->i2c, reg); if (ret < 0) - dev_err(tc35892->dev, "failed to read reg %#x: %d\n", + dev_err(tc3589x->dev, "failed to read reg %#x: %d\n", reg, ret); return ret; } -EXPORT_SYMBOL_GPL(tc35892_reg_read); +EXPORT_SYMBOL_GPL(tc3589x_reg_read); /** - * tc35892_reg_read() - write a single TC35892 register - * @tc35892: Device to write to + * tc3589x_reg_read() - write a single TC3589x register + * @tc3589x: Device to write to * @reg: Register to read * @data: Value to write */ -int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data) +int tc3589x_reg_write(struct tc3589x *tc3589x, u8 reg, u8 data) { int ret; - ret = i2c_smbus_write_byte_data(tc35892->i2c, reg, data); + ret = i2c_smbus_write_byte_data(tc3589x->i2c, reg, data); if (ret < 0) - dev_err(tc35892->dev, "failed to write reg %#x: %d\n", + dev_err(tc3589x->dev, "failed to write reg %#x: %d\n", reg, ret); return ret; } -EXPORT_SYMBOL_GPL(tc35892_reg_write); +EXPORT_SYMBOL_GPL(tc3589x_reg_write); /** - * tc35892_block_read() - read multiple TC35892 registers - * @tc35892: Device to read from + * tc3589x_block_read() - read multiple TC3589x registers + * @tc3589x: Device to read from * @reg: First register * @length: Number of registers * @values: Buffer to write to */ -int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length, u8 *values) +int tc3589x_block_read(struct tc3589x *tc3589x, u8 reg, u8 length, u8 *values) { int ret; - ret = i2c_smbus_read_i2c_block_data(tc35892->i2c, reg, length, values); + ret = i2c_smbus_read_i2c_block_data(tc3589x->i2c, reg, length, values); if (ret < 0) - dev_err(tc35892->dev, "failed to read regs %#x: %d\n", + dev_err(tc3589x->dev, "failed to read regs %#x: %d\n", reg, ret); return ret; } -EXPORT_SYMBOL_GPL(tc35892_block_read); +EXPORT_SYMBOL_GPL(tc3589x_block_read); /** - * tc35892_block_write() - write multiple TC35892 registers - * @tc35892: Device to write to + * tc3589x_block_write() - write multiple TC3589x registers + * @tc3589x: Device to write to * @reg: First register * @length: Number of registers * @values: Values to write */ -int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length, +int tc3589x_block_write(struct tc3589x *tc3589x, u8 reg, u8 length, const u8 *values) { int ret; - ret = i2c_smbus_write_i2c_block_data(tc35892->i2c, reg, length, + ret = i2c_smbus_write_i2c_block_data(tc3589x->i2c, reg, length, values); if (ret < 0) - dev_err(tc35892->dev, "failed to write regs %#x: %d\n", + dev_err(tc3589x->dev, "failed to write regs %#x: %d\n", reg, ret); return ret; } -EXPORT_SYMBOL_GPL(tc35892_block_write); +EXPORT_SYMBOL_GPL(tc3589x_block_write); /** - * tc35892_set_bits() - set the value of a bitfield in a TC35892 register - * @tc35892: Device to write to + * tc3589x_set_bits() - set the value of a bitfield in a TC3589x register + * @tc3589x: Device to write to * @reg: Register to write * @mask: Mask of bits to set * @values: Value to set */ -int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val) +int tc3589x_set_bits(struct tc3589x *tc3589x, u8 reg, u8 mask, u8 val) { int ret; - mutex_lock(&tc35892->lock); + mutex_lock(&tc3589x->lock); - ret = tc35892_reg_read(tc35892, reg); + ret = tc3589x_reg_read(tc3589x, reg); if (ret < 0) goto out; ret &= ~mask; ret |= val; - ret = tc35892_reg_write(tc35892, reg, ret); + ret = tc3589x_reg_write(tc3589x, reg, ret); out: - mutex_unlock(&tc35892->lock); + mutex_unlock(&tc3589x->lock); return ret; } -EXPORT_SYMBOL_GPL(tc35892_set_bits); +EXPORT_SYMBOL_GPL(tc3589x_set_bits); static struct resource gpio_resources[] = { { - .start = TC35892_INT_GPIIRQ, - .end = TC35892_INT_GPIIRQ, + .start = TC3589x_INT_GPIIRQ, + .end = TC3589x_INT_GPIIRQ, .flags = IORESOURCE_IRQ, }, }; -static struct mfd_cell tc35892_devs[] = { +static struct mfd_cell tc3589x_devs[] = { { - .name = "tc35892-gpio", + .name = "tc3589x-gpio", .num_resources = ARRAY_SIZE(gpio_resources), .resources = &gpio_resources[0], }, }; -static irqreturn_t tc35892_irq(int irq, void *data) +static irqreturn_t tc3589x_irq(int irq, void *data) { - struct tc35892 *tc35892 = data; + struct tc3589x *tc3589x = data; int status; - status = tc35892_reg_read(tc35892, TC35892_IRQST); + status = tc3589x_reg_read(tc3589x, TC3589x_IRQST); if (status < 0) return IRQ_NONE; while (status) { int bit = __ffs(status); - handle_nested_irq(tc35892->irq_base + bit); + handle_nested_irq(tc3589x->irq_base + bit); status &= ~(1 << bit); } @@ -158,30 +158,30 @@ static irqreturn_t tc35892_irq(int irq, void *data) * have the last interrupt clear (for example, GPIO IC write) take * effect. */ - tc35892_reg_read(tc35892, TC35892_IRQST); + tc3589x_reg_read(tc3589x, TC3589x_IRQST); return IRQ_HANDLED; } -static void tc35892_irq_dummy(unsigned int irq) +static void tc3589x_irq_dummy(unsigned int irq) { /* No mask/unmask at this level */ } -static struct irq_chip tc35892_irq_chip = { - .name = "tc35892", - .mask = tc35892_irq_dummy, - .unmask = tc35892_irq_dummy, +static struct irq_chip tc3589x_irq_chip = { + .name = "tc3589x", + .mask = tc3589x_irq_dummy, + .unmask = tc3589x_irq_dummy, }; -static int tc35892_irq_init(struct tc35892 *tc35892) +static int tc3589x_irq_init(struct tc3589x *tc3589x) { - int base = tc35892->irq_base; + int base = tc3589x->irq_base; int irq; - for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) { - set_irq_chip_data(irq, tc35892); - set_irq_chip_and_handler(irq, &tc35892_irq_chip, + for (irq = base; irq < base + TC3589x_NR_INTERNAL_IRQS; irq++) { + set_irq_chip_data(irq, tc3589x); + set_irq_chip_and_handler(irq, &tc3589x_irq_chip, handle_edge_irq); set_irq_nested_thread(irq, 1); #ifdef CONFIG_ARM @@ -194,12 +194,12 @@ static int tc35892_irq_init(struct tc35892 *tc35892) return 0; } -static void tc35892_irq_remove(struct tc35892 *tc35892) +static void tc3589x_irq_remove(struct tc3589x *tc3589x) { - int base = tc35892->irq_base; + int base = tc3589x->irq_base; int irq; - for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) { + for (irq = base; irq < base + TC3589x_NR_INTERNAL_IRQS; irq++) { #ifdef CONFIG_ARM set_irq_flags(irq, 0); #endif @@ -208,138 +208,138 @@ static void tc35892_irq_remove(struct tc35892 *tc35892) } } -static int tc35892_chip_init(struct tc35892 *tc35892) +static int tc3589x_chip_init(struct tc3589x *tc3589x) { int manf, ver, ret; - manf = tc35892_reg_read(tc35892, TC35892_MANFCODE); + manf = tc3589x_reg_read(tc3589x, TC3589x_MANFCODE); if (manf < 0) return manf; - ver = tc35892_reg_read(tc35892, TC35892_VERSION); + ver = tc3589x_reg_read(tc3589x, TC3589x_VERSION); if (ver < 0) return ver; - if (manf != TC35892_MANFCODE_MAGIC) { - dev_err(tc35892->dev, "unknown manufacturer: %#x\n", manf); + if (manf != TC3589x_MANFCODE_MAGIC) { + dev_err(tc3589x->dev, "unknown manufacturer: %#x\n", manf); return -EINVAL; } - dev_info(tc35892->dev, "manufacturer: %#x, version: %#x\n", manf, ver); + dev_info(tc3589x->dev, "manufacturer: %#x, version: %#x\n", manf, ver); /* Put everything except the IRQ module into reset */ - ret = tc35892_reg_write(tc35892, TC35892_RSTCTRL, - TC35892_RSTCTRL_TIMRST - | TC35892_RSTCTRL_ROTRST - | TC35892_RSTCTRL_KBDRST - | TC35892_RSTCTRL_GPIRST); + ret = tc3589x_reg_write(tc3589x, TC3589x_RSTCTRL, + TC3589x_RSTCTRL_TIMRST + | TC3589x_RSTCTRL_ROTRST + | TC3589x_RSTCTRL_KBDRST + | TC3589x_RSTCTRL_GPIRST); if (ret < 0) return ret; /* Clear the reset interrupt. */ - return tc35892_reg_write(tc35892, TC35892_RSTINTCLR, 0x1); + return tc3589x_reg_write(tc3589x, TC3589x_RSTINTCLR, 0x1); } -static int __devinit tc35892_probe(struct i2c_client *i2c, +static int __devinit tc3589x_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { - struct tc35892_platform_data *pdata = i2c->dev.platform_data; - struct tc35892 *tc35892; + struct tc3589x_platform_data *pdata = i2c->dev.platform_data; + struct tc3589x *tc3589x; int ret; if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK)) return -EIO; - tc35892 = kzalloc(sizeof(struct tc35892), GFP_KERNEL); - if (!tc35892) + tc3589x = kzalloc(sizeof(struct tc3589x), GFP_KERNEL); + if (!tc3589x) return -ENOMEM; - mutex_init(&tc35892->lock); + mutex_init(&tc3589x->lock); - tc35892->dev = &i2c->dev; - tc35892->i2c = i2c; - tc35892->pdata = pdata; - tc35892->irq_base = pdata->irq_base; - tc35892->num_gpio = id->driver_data; + tc3589x->dev = &i2c->dev; + tc3589x->i2c = i2c; + tc3589x->pdata = pdata; + tc3589x->irq_base = pdata->irq_base; + tc3589x->num_gpio = id->driver_data; - i2c_set_clientdata(i2c, tc35892); + i2c_set_clientdata(i2c, tc3589x); - ret = tc35892_chip_init(tc35892); + ret = tc3589x_chip_init(tc3589x); if (ret) goto out_free; - ret = tc35892_irq_init(tc35892); + ret = tc3589x_irq_init(tc3589x); if (ret) goto out_free; - ret = request_threaded_irq(tc35892->i2c->irq, NULL, tc35892_irq, + ret = request_threaded_irq(tc3589x->i2c->irq, NULL, tc3589x_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "tc35892", tc35892); + "tc3589x", tc3589x); if (ret) { - dev_err(tc35892->dev, "failed to request IRQ: %d\n", ret); + dev_err(tc3589x->dev, "failed to request IRQ: %d\n", ret); goto out_removeirq; } - ret = mfd_add_devices(tc35892->dev, -1, tc35892_devs, - ARRAY_SIZE(tc35892_devs), NULL, - tc35892->irq_base); + ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_devs, + ARRAY_SIZE(tc3589x_devs), NULL, + tc3589x->irq_base); if (ret) { - dev_err(tc35892->dev, "failed to add children\n"); + dev_err(tc3589x->dev, "failed to add children\n"); goto out_freeirq; } return 0; out_freeirq: - free_irq(tc35892->i2c->irq, tc35892); + free_irq(tc3589x->i2c->irq, tc3589x); out_removeirq: - tc35892_irq_remove(tc35892); + tc3589x_irq_remove(tc3589x); out_free: - kfree(tc35892); + kfree(tc3589x); return ret; } -static int __devexit tc35892_remove(struct i2c_client *client) +static int __devexit tc3589x_remove(struct i2c_client *client) { - struct tc35892 *tc35892 = i2c_get_clientdata(client); + struct tc3589x *tc3589x = i2c_get_clientdata(client); - mfd_remove_devices(tc35892->dev); + mfd_remove_devices(tc3589x->dev); - free_irq(tc35892->i2c->irq, tc35892); - tc35892_irq_remove(tc35892); + free_irq(tc3589x->i2c->irq, tc3589x); + tc3589x_irq_remove(tc3589x); - kfree(tc35892); + kfree(tc3589x); return 0; } -static const struct i2c_device_id tc35892_id[] = { - { "tc35892", 24 }, +static const struct i2c_device_id tc3589x_id[] = { + { "tc3589x", 24 }, { } }; -MODULE_DEVICE_TABLE(i2c, tc35892_id); +MODULE_DEVICE_TABLE(i2c, tc3589x_id); -static struct i2c_driver tc35892_driver = { - .driver.name = "tc35892", +static struct i2c_driver tc3589x_driver = { + .driver.name = "tc3589x", .driver.owner = THIS_MODULE, - .probe = tc35892_probe, - .remove = __devexit_p(tc35892_remove), - .id_table = tc35892_id, + .probe = tc3589x_probe, + .remove = __devexit_p(tc3589x_remove), + .id_table = tc3589x_id, }; -static int __init tc35892_init(void) +static int __init tc3589x_init(void) { - return i2c_add_driver(&tc35892_driver); + return i2c_add_driver(&tc3589x_driver); } -subsys_initcall(tc35892_init); +subsys_initcall(tc3589x_init); -static void __exit tc35892_exit(void) +static void __exit tc3589x_exit(void) { - i2c_del_driver(&tc35892_driver); + i2c_del_driver(&tc3589x_driver); } -module_exit(tc35892_exit); +module_exit(tc3589x_exit); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("TC35892 MFD core driver"); +MODULE_DESCRIPTION("TC3589x MFD core driver"); MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); diff --git a/include/linux/mfd/tc3589x.h b/include/linux/mfd/tc3589x.h index eff3094ca84..ea1918896f5 100644 --- a/include/linux/mfd/tc3589x.h +++ b/include/linux/mfd/tc3589x.h @@ -4,133 +4,133 @@ * License Terms: GNU General Public License, version 2 */ -#ifndef __LINUX_MFD_TC35892_H -#define __LINUX_MFD_TC35892_H +#ifndef __LINUX_MFD_TC3589x_H +#define __LINUX_MFD_TC3589x_H #include -#define TC35892_RSTCTRL_IRQRST (1 << 4) -#define TC35892_RSTCTRL_TIMRST (1 << 3) -#define TC35892_RSTCTRL_ROTRST (1 << 2) -#define TC35892_RSTCTRL_KBDRST (1 << 1) -#define TC35892_RSTCTRL_GPIRST (1 << 0) - -#define TC35892_IRQST 0x91 - -#define TC35892_MANFCODE_MAGIC 0x03 -#define TC35892_MANFCODE 0x80 -#define TC35892_VERSION 0x81 -#define TC35892_IOCFG 0xA7 - -#define TC35892_CLKMODE 0x88 -#define TC35892_CLKCFG 0x89 -#define TC35892_CLKEN 0x8A - -#define TC35892_RSTCTRL 0x82 -#define TC35892_EXTRSTN 0x83 -#define TC35892_RSTINTCLR 0x84 - -#define TC35892_GPIOIS0 0xC9 -#define TC35892_GPIOIS1 0xCA -#define TC35892_GPIOIS2 0xCB -#define TC35892_GPIOIBE0 0xCC -#define TC35892_GPIOIBE1 0xCD -#define TC35892_GPIOIBE2 0xCE -#define TC35892_GPIOIEV0 0xCF -#define TC35892_GPIOIEV1 0xD0 -#define TC35892_GPIOIEV2 0xD1 -#define TC35892_GPIOIE0 0xD2 -#define TC35892_GPIOIE1 0xD3 -#define TC35892_GPIOIE2 0xD4 -#define TC35892_GPIORIS0 0xD6 -#define TC35892_GPIORIS1 0xD7 -#define TC35892_GPIORIS2 0xD8 -#define TC35892_GPIOMIS0 0xD9 -#define TC35892_GPIOMIS1 0xDA -#define TC35892_GPIOMIS2 0xDB -#define TC35892_GPIOIC0 0xDC -#define TC35892_GPIOIC1 0xDD -#define TC35892_GPIOIC2 0xDE - -#define TC35892_GPIODATA0 0xC0 -#define TC35892_GPIOMASK0 0xc1 -#define TC35892_GPIODATA1 0xC2 -#define TC35892_GPIOMASK1 0xc3 -#define TC35892_GPIODATA2 0xC4 -#define TC35892_GPIOMASK2 0xC5 - -#define TC35892_GPIODIR0 0xC6 -#define TC35892_GPIODIR1 0xC7 -#define TC35892_GPIODIR2 0xC8 - -#define TC35892_GPIOSYNC0 0xE6 -#define TC35892_GPIOSYNC1 0xE7 -#define TC35892_GPIOSYNC2 0xE8 - -#define TC35892_GPIOWAKE0 0xE9 -#define TC35892_GPIOWAKE1 0xEA -#define TC35892_GPIOWAKE2 0xEB - -#define TC35892_GPIOODM0 0xE0 -#define TC35892_GPIOODE0 0xE1 -#define TC35892_GPIOODM1 0xE2 -#define TC35892_GPIOODE1 0xE3 -#define TC35892_GPIOODM2 0xE4 -#define TC35892_GPIOODE2 0xE5 - -#define TC35892_INT_GPIIRQ 0 -#define TC35892_INT_TI0IRQ 1 -#define TC35892_INT_TI1IRQ 2 -#define TC35892_INT_TI2IRQ 3 -#define TC35892_INT_ROTIRQ 5 -#define TC35892_INT_KBDIRQ 6 -#define TC35892_INT_PORIRQ 7 - -#define TC35892_NR_INTERNAL_IRQS 8 -#define TC35892_INT_GPIO(x) (TC35892_NR_INTERNAL_IRQS + (x)) - -struct tc35892 { +#define TC3589x_RSTCTRL_IRQRST (1 << 4) +#define TC3589x_RSTCTRL_TIMRST (1 << 3) +#define TC3589x_RSTCTRL_ROTRST (1 << 2) +#define TC3589x_RSTCTRL_KBDRST (1 << 1) +#define TC3589x_RSTCTRL_GPIRST (1 << 0) + +#define TC3589x_IRQST 0x91 + +#define TC3589x_MANFCODE_MAGIC 0x03 +#define TC3589x_MANFCODE 0x80 +#define TC3589x_VERSION 0x81 +#define TC3589x_IOCFG 0xA7 + +#define TC3589x_CLKMODE 0x88 +#define TC3589x_CLKCFG 0x89 +#define TC3589x_CLKEN 0x8A + +#define TC3589x_RSTCTRL 0x82 +#define TC3589x_EXTRSTN 0x83 +#define TC3589x_RSTINTCLR 0x84 + +#define TC3589x_GPIOIS0 0xC9 +#define TC3589x_GPIOIS1 0xCA +#define TC3589x_GPIOIS2 0xCB +#define TC3589x_GPIOIBE0 0xCC +#define TC3589x_GPIOIBE1 0xCD +#define TC3589x_GPIOIBE2 0xCE +#define TC3589x_GPIOIEV0 0xCF +#define TC3589x_GPIOIEV1 0xD0 +#define TC3589x_GPIOIEV2 0xD1 +#define TC3589x_GPIOIE0 0xD2 +#define TC3589x_GPIOIE1 0xD3 +#define TC3589x_GPIOIE2 0xD4 +#define TC3589x_GPIORIS0 0xD6 +#define TC3589x_GPIORIS1 0xD7 +#define TC3589x_GPIORIS2 0xD8 +#define TC3589x_GPIOMIS0 0xD9 +#define TC3589x_GPIOMIS1 0xDA +#define TC3589x_GPIOMIS2 0xDB +#define TC3589x_GPIOIC0 0xDC +#define TC3589x_GPIOIC1 0xDD +#define TC3589x_GPIOIC2 0xDE + +#define TC3589x_GPIODATA0 0xC0 +#define TC3589x_GPIOMASK0 0xc1 +#define TC3589x_GPIODATA1 0xC2 +#define TC3589x_GPIOMASK1 0xc3 +#define TC3589x_GPIODATA2 0xC4 +#define TC3589x_GPIOMASK2 0xC5 + +#define TC3589x_GPIODIR0 0xC6 +#define TC3589x_GPIODIR1 0xC7 +#define TC3589x_GPIODIR2 0xC8 + +#define TC3589x_GPIOSYNC0 0xE6 +#define TC3589x_GPIOSYNC1 0xE7 +#define TC3589x_GPIOSYNC2 0xE8 + +#define TC3589x_GPIOWAKE0 0xE9 +#define TC3589x_GPIOWAKE1 0xEA +#define TC3589x_GPIOWAKE2 0xEB + +#define TC3589x_GPIOODM0 0xE0 +#define TC3589x_GPIOODE0 0xE1 +#define TC3589x_GPIOODM1 0xE2 +#define TC3589x_GPIOODE1 0xE3 +#define TC3589x_GPIOODM2 0xE4 +#define TC3589x_GPIOODE2 0xE5 + +#define TC3589x_INT_GPIIRQ 0 +#define TC3589x_INT_TI0IRQ 1 +#define TC3589x_INT_TI1IRQ 2 +#define TC3589x_INT_TI2IRQ 3 +#define TC3589x_INT_ROTIRQ 5 +#define TC3589x_INT_KBDIRQ 6 +#define TC3589x_INT_PORIRQ 7 + +#define TC3589x_NR_INTERNAL_IRQS 8 +#define TC3589x_INT_GPIO(x) (TC3589x_NR_INTERNAL_IRQS + (x)) + +struct tc3589x { struct mutex lock; struct device *dev; struct i2c_client *i2c; int irq_base; int num_gpio; - struct tc35892_platform_data *pdata; + struct tc3589x_platform_data *pdata; }; -extern int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data); -extern int tc35892_reg_read(struct tc35892 *tc35892, u8 reg); -extern int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length, +extern int tc3589x_reg_write(struct tc3589x *tc3589x, u8 reg, u8 data); +extern int tc3589x_reg_read(struct tc3589x *tc3589x, u8 reg); +extern int tc3589x_block_read(struct tc3589x *tc3589x, u8 reg, u8 length, u8 *values); -extern int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length, +extern int tc3589x_block_write(struct tc3589x *tc3589x, u8 reg, u8 length, const u8 *values); -extern int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val); +extern int tc3589x_set_bits(struct tc3589x *tc3589x, u8 reg, u8 mask, u8 val); /** - * struct tc35892_gpio_platform_data - TC35892 GPIO platform data - * @gpio_base: first gpio number assigned to TC35892. A maximum of - * %TC35892_NR_GPIOS GPIOs will be allocated. + * struct tc3589x_gpio_platform_data - TC3589x GPIO platform data + * @gpio_base: first gpio number assigned to TC3589x. A maximum of + * %TC3589x_NR_GPIOS GPIOs will be allocated. * @setup: callback for board-specific initialization * @remove: callback for board-specific teardown */ -struct tc35892_gpio_platform_data { +struct tc3589x_gpio_platform_data { int gpio_base; - void (*setup)(struct tc35892 *tc35892, unsigned gpio_base); - void (*remove)(struct tc35892 *tc35892, unsigned gpio_base); + void (*setup)(struct tc3589x *tc3589x, unsigned gpio_base); + void (*remove)(struct tc3589x *tc3589x, unsigned gpio_base); }; /** - * struct tc35892_platform_data - TC35892 platform data - * @irq_base: base IRQ number. %TC35892_NR_IRQS irqs will be used. + * struct tc3589x_platform_data - TC3589x platform data + * @irq_base: base IRQ number. %TC3589x_NR_IRQS irqs will be used. * @gpio: GPIO-specific platform data */ -struct tc35892_platform_data { +struct tc3589x_platform_data { int irq_base; - struct tc35892_gpio_platform_data *gpio; + struct tc3589x_gpio_platform_data *gpio; }; -#define TC35892_NR_GPIOS 24 -#define TC35892_NR_IRQS TC35892_INT_GPIO(TC35892_NR_GPIOS) +#define TC3589x_NR_GPIOS 24 +#define TC3589x_NR_IRQS TC3589x_INT_GPIO(TC3589x_NR_GPIOS) #endif -- cgit v1.2.3-70-g09d2 From 611b7590afa6e6c6b0942b1d3efef17fbb348ef5 Mon Sep 17 00:00:00 2001 From: Sundar Iyer Date: Mon, 13 Dec 2010 09:33:15 +0530 Subject: mfd/tc3589x: add block identifier for multiple child devices Add block identifier to be able to add multiple mfd clients to the mfd core Acked-by: Samuel Ortiz Signed-off-by: Sundar Iyer Signed-off-by: Linus Walleij --- arch/arm/mach-ux500/board-mop500.c | 1 + drivers/mfd/tc3589x.c | 28 +++++++++++++++++++++++----- include/linux/mfd/tc3589x.h | 7 +++++++ 3 files changed, 31 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 5c950261968..060b23aab8e 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -122,6 +122,7 @@ static struct tc3589x_gpio_platform_data mop500_tc35892_gpio_data = { }; static struct tc3589x_platform_data mop500_tc35892_data = { + .block = TC3589x_BLOCK_GPIO, .gpio = &mop500_tc35892_gpio_data, .irq_base = MOP500_EGPIO_IRQ_BASE, }; diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index 7deff53181d..0ed9669d95f 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -129,7 +129,7 @@ static struct resource gpio_resources[] = { }, }; -static struct mfd_cell tc3589x_devs[] = { +static struct mfd_cell tc3589x_dev_gpio[] = { { .name = "tc3589x-gpio", .num_resources = ARRAY_SIZE(gpio_resources), @@ -240,6 +240,26 @@ static int tc3589x_chip_init(struct tc3589x *tc3589x) return tc3589x_reg_write(tc3589x, TC3589x_RSTINTCLR, 0x1); } +static int __devinit tc3589x_device_init(struct tc3589x *tc3589x) +{ + int ret = 0; + unsigned int blocks = tc3589x->pdata->block; + + if (blocks & TC3589x_BLOCK_GPIO) { + ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_gpio, + ARRAY_SIZE(tc3589x_dev_gpio), NULL, + tc3589x->irq_base); + if (ret) { + dev_err(tc3589x->dev, "failed to add gpio child\n"); + return ret; + } + dev_info(tc3589x->dev, "added gpio block\n"); + } + + return ret; + +} + static int __devinit tc3589x_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -281,11 +301,9 @@ static int __devinit tc3589x_probe(struct i2c_client *i2c, goto out_removeirq; } - ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_devs, - ARRAY_SIZE(tc3589x_devs), NULL, - tc3589x->irq_base); + ret = tc3589x_device_init(tc3589x); if (ret) { - dev_err(tc3589x->dev, "failed to add children\n"); + dev_err(tc3589x->dev, "failed to add child devices\n"); goto out_freeirq; } diff --git a/include/linux/mfd/tc3589x.h b/include/linux/mfd/tc3589x.h index ea1918896f5..da00958b12d 100644 --- a/include/linux/mfd/tc3589x.h +++ b/include/linux/mfd/tc3589x.h @@ -9,6 +9,11 @@ #include +enum tx3589x_block { + TC3589x_BLOCK_GPIO = 1 << 0, + TC3589x_BLOCK_KEYPAD = 1 << 1, +}; + #define TC3589x_RSTCTRL_IRQRST (1 << 4) #define TC3589x_RSTCTRL_TIMRST (1 << 3) #define TC3589x_RSTCTRL_ROTRST (1 << 2) @@ -122,10 +127,12 @@ struct tc3589x_gpio_platform_data { /** * struct tc3589x_platform_data - TC3589x platform data + * @block: bitmask of blocks to enable (use TC3589x_BLOCK_*) * @irq_base: base IRQ number. %TC3589x_NR_IRQS irqs will be used. * @gpio: GPIO-specific platform data */ struct tc3589x_platform_data { + unsigned int block; int irq_base; struct tc3589x_gpio_platform_data *gpio; }; -- cgit v1.2.3-70-g09d2 From bd77efd0cea80865d4cfcc1e4b62789c51a74b2d Mon Sep 17 00:00:00 2001 From: Sundar Iyer Date: Mon, 13 Dec 2010 09:33:16 +0530 Subject: mfd/tc3589x: fix random interrupt misses On the TC35892, a random delayed interrupt clear (GPIO IC) write locks up the child interrupts. In such a case, the original interrupt is active and not yet acknowledged. Re-check the IRQST bit for any pending interrupts and handle those. Acked-by: Samuel Ortiz Signed-off-by: Sundar Iyer Signed-off-by: Linus Walleij --- drivers/mfd/tc3589x.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index 0ed9669d95f..708349a5c0e 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -142,6 +142,7 @@ static irqreturn_t tc3589x_irq(int irq, void *data) struct tc3589x *tc3589x = data; int status; +again: status = tc3589x_reg_read(tc3589x, TC3589x_IRQST); if (status < 0) return IRQ_NONE; @@ -156,9 +157,12 @@ static irqreturn_t tc3589x_irq(int irq, void *data) /* * A dummy read or write (to any register) appears to be necessary to * have the last interrupt clear (for example, GPIO IC write) take - * effect. + * effect. In such a case, recheck for any interrupt which is still + * pending. */ - tc3589x_reg_read(tc3589x, TC3589x_IRQST); + status = tc3589x_reg_read(tc3589x, TC3589x_IRQST); + if (status) + goto again; return IRQ_HANDLED; } -- cgit v1.2.3-70-g09d2 From 523bc3820f023169671e9726b8dc075669d14bec Mon Sep 17 00:00:00 2001 From: Sundar Iyer Date: Mon, 13 Dec 2010 09:33:17 +0530 Subject: mfd/tc3589x: undo gpio module reset during chip init Skip putting the GPIO module into a reset during the chip init. This makes sure to preserve any existing GPIO configurations done by pre-kernel boot code. Acked-by: Samuel Ortiz Signed-off-by: Sundar Iyer Signed-off-by: Linus Walleij --- drivers/mfd/tc3589x.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index 708349a5c0e..f000d2ed23e 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -231,12 +231,15 @@ static int tc3589x_chip_init(struct tc3589x *tc3589x) dev_info(tc3589x->dev, "manufacturer: %#x, version: %#x\n", manf, ver); - /* Put everything except the IRQ module into reset */ + /* + * Put everything except the IRQ module into reset; + * also spare the GPIO module for any pin initialization + * done during pre-kernel boot + */ ret = tc3589x_reg_write(tc3589x, TC3589x_RSTCTRL, TC3589x_RSTCTRL_TIMRST | TC3589x_RSTCTRL_ROTRST - | TC3589x_RSTCTRL_KBDRST - | TC3589x_RSTCTRL_GPIRST); + | TC3589x_RSTCTRL_KBDRST); if (ret < 0) return ret; -- cgit v1.2.3-70-g09d2 From 593e9d70fb0f1ece1cf2a61c701dec35d8e41f8d Mon Sep 17 00:00:00 2001 From: Sundar Iyer Date: Mon, 13 Dec 2010 09:33:18 +0530 Subject: mfd/tc3589x: add suspend/resume support Acked-by: Samuel Ortiz Signed-off-by: Sundar Iyer Signed-off-by: Linus Walleij --- drivers/mfd/tc3589x.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index f000d2ed23e..32291fe4151 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -14,6 +14,9 @@ #include #include +#define TC3589x_CLKMODE_MODCTL_SLEEP 0x0 +#define TC3589x_CLKMODE_MODCTL_OPERATION (1 << 0) + /** * tc3589x_reg_read() - read a single TC3589x register * @tc3589x: Device to read from @@ -339,6 +342,37 @@ static int __devexit tc3589x_remove(struct i2c_client *client) return 0; } +static int tc3589x_suspend(struct device *dev) +{ + struct tc3589x *tc3589x = dev_get_drvdata(dev); + struct i2c_client *client = tc3589x->i2c; + int ret = 0; + + /* put the system to sleep mode */ + if (!device_may_wakeup(&client->dev)) + ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE, + TC3589x_CLKMODE_MODCTL_SLEEP); + + return ret; +} + +static int tc3589x_resume(struct device *dev) +{ + struct tc3589x *tc3589x = dev_get_drvdata(dev); + struct i2c_client *client = tc3589x->i2c; + int ret = 0; + + /* enable the system into operation */ + if (!device_may_wakeup(&client->dev)) + ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE, + TC3589x_CLKMODE_MODCTL_OPERATION); + + return ret; +} + +static const SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, + tc3589x_resume); + static const struct i2c_device_id tc3589x_id[] = { { "tc3589x", 24 }, { } @@ -348,6 +382,9 @@ MODULE_DEVICE_TABLE(i2c, tc3589x_id); static struct i2c_driver tc3589x_driver = { .driver.name = "tc3589x", .driver.owner = THIS_MODULE, +#ifdef CONFIG_PM + .driver.pm = &tc3589x_dev_pm_ops, +#endif .probe = tc3589x_probe, .remove = __devexit_p(tc3589x_remove), .id_table = tc3589x_id, -- cgit v1.2.3-70-g09d2 From abda3a24a99998279fe890ea8a789ebe4d605d78 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Mon, 20 Dec 2010 13:01:30 +0100 Subject: mfd: Use dummy_irq_chip for tc3589x This also converts tc3589x to the new irq API. Cc: Rabin Vincent Signed-off-by: Samuel Ortiz Signed-off-by: Linus Walleij --- drivers/mfd/tc3589x.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index 32291fe4151..112efd3c494 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -170,17 +170,6 @@ again: return IRQ_HANDLED; } -static void tc3589x_irq_dummy(unsigned int irq) -{ - /* No mask/unmask at this level */ -} - -static struct irq_chip tc3589x_irq_chip = { - .name = "tc3589x", - .mask = tc3589x_irq_dummy, - .unmask = tc3589x_irq_dummy, -}; - static int tc3589x_irq_init(struct tc3589x *tc3589x) { int base = tc3589x->irq_base; @@ -188,7 +177,7 @@ static int tc3589x_irq_init(struct tc3589x *tc3589x) for (irq = base; irq < base + TC3589x_NR_INTERNAL_IRQS; irq++) { set_irq_chip_data(irq, tc3589x); - set_irq_chip_and_handler(irq, &tc3589x_irq_chip, + set_irq_chip_and_handler(irq, &dummy_irq_chip, handle_edge_irq); set_irq_nested_thread(irq, 1); #ifdef CONFIG_ARM -- cgit v1.2.3-70-g09d2 From a4257af5b0c5479bb81597579841e9daaeccd7f6 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 3 Nov 2010 16:26:42 +0100 Subject: ARM: pxa: Add pxa320 PCMCIA check On PXA320, there's only one PCMCIA slot available. Check for cases where the user would want to register multiple. Also, rework failpath. Signed-off-by: Marek Vasut Signed-off-by: Eric Miao --- drivers/pcmcia/pxa2xx_base.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c index 3c01774eb39..3755e7c8c71 100644 --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -285,8 +285,16 @@ static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev) struct clk *clk; ops = (struct pcmcia_low_level *)dev->dev.platform_data; - if (!ops) - return -ENODEV; + if (!ops) { + ret = -ENODEV; + goto err0; + } + + if (cpu_is_pxa320() && ops->nr > 1) { + dev_err(&dev->dev, "pxa320 supports only one pcmcia slot"); + ret = -EINVAL; + goto err0; + } clk = clk_get(&dev->dev, NULL); if (!clk) @@ -316,7 +324,7 @@ static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev) ret = pxa2xx_drv_pcmcia_add_one(skt); if (ret) - break; + goto err1; } if (ret) { @@ -329,6 +337,13 @@ static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev) dev_set_drvdata(&dev->dev, sinfo); } + return 0; + +err1: + while (--i >= 0) + soc_pcmcia_remove_one(&sinfo->skt[i]); + kfree(sinfo); +err0: return ret; } -- cgit v1.2.3-70-g09d2 From 960c0acaabf603e39b121ae5c0580aaca6f8aa7b Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 11 Aug 2010 03:32:53 +0200 Subject: ARM: pxa: Toradex Colibri PXA270 CF support This driver also contains structures to eventually support PXA320. This is planned to be added in a later patch. Signed-off-by: Marek Vasut Acked-by: Daniel Mack Signed-off-by: Eric Miao --- arch/arm/mach-pxa/colibri-pxa270-evalboard.c | 18 +++ drivers/pcmcia/Kconfig | 2 +- drivers/pcmcia/Makefile | 1 + drivers/pcmcia/pxa2xx_colibri.c | 214 +++++++++++++++++++++++++++ 4 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 drivers/pcmcia/pxa2xx_colibri.c (limited to 'drivers') diff --git a/arch/arm/mach-pxa/colibri-pxa270-evalboard.c b/arch/arm/mach-pxa/colibri-pxa270-evalboard.c index 0f3b632c3b1..6177ff5ecbc 100644 --- a/arch/arm/mach-pxa/colibri-pxa270-evalboard.c +++ b/arch/arm/mach-pxa/colibri-pxa270-evalboard.c @@ -51,6 +51,24 @@ static mfp_cfg_t colibri_pxa270_evalboard_pin_config[] __initdata = { GPIO89_USBH1_PEN, GPIO119_USBH2_PWR, GPIO120_USBH2_PEN, + + /* PCMCIA */ + GPIO85_nPCE_1, + GPIO54_nPCE_2, + GPIO55_nPREG, + GPIO50_nPIOR, + GPIO51_nPIOW, + GPIO49_nPWE, + GPIO48_nPOE, + GPIO57_nIOIS16, + GPIO56_nPWAIT, + GPIO104_PSKTSEL, + GPIO53_GPIO, /* RESET */ + GPIO83_GPIO, /* BVD1 */ + GPIO82_GPIO, /* BVD2 */ + GPIO1_GPIO, /* READY */ + GPIO84_GPIO, /* DETECT */ + GPIO107_GPIO, /* PPEN */ }; /****************************************************************************** diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index c80a7a6e769..e9acf035437 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -215,7 +215,7 @@ config PCMCIA_PXA2XX depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \ || MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \ || ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \ - || MACH_VPAC270 || MACH_BALLOON3) + || MACH_VPAC270 || MACH_BALLOON3 || MACH_COLIBRI) select PCMCIA_SOC_COMMON help Say Y here to include support for the PXA2xx PCMCIA controller diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 8d9386a22eb..2fee7ef0c39 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -70,6 +70,7 @@ pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o pxa2xx-obj-$(CONFIG_MACH_VPAC270) += pxa2xx_vpac270.o pxa2xx-obj-$(CONFIG_MACH_BALLOON3) += pxa2xx_balloon3.o +pxa2xx-obj-$(CONFIG_MACH_COLIBRI) += pxa2xx_colibri.o obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_base.o $(pxa2xx-obj-y) diff --git a/drivers/pcmcia/pxa2xx_colibri.c b/drivers/pcmcia/pxa2xx_colibri.c new file mode 100644 index 00000000000..4ed876c4398 --- /dev/null +++ b/drivers/pcmcia/pxa2xx_colibri.c @@ -0,0 +1,214 @@ +/* + * linux/drivers/pcmcia/pxa2xx_colibri.c + * + * Driver for Toradex Colibri PXA270 CF socket + * + * Copyright (C) 2010 Marek Vasut + * + * 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 "soc_common.h" + +#define COLIBRI270_RESET_GPIO 53 +#define COLIBRI270_PPEN_GPIO 107 +#define COLIBRI270_BVD1_GPIO 83 +#define COLIBRI270_BVD2_GPIO 82 +#define COLIBRI270_DETECT_GPIO 84 +#define COLIBRI270_READY_GPIO 1 + +static struct { + int reset_gpio; + int ppen_gpio; + int bvd1_gpio; + int bvd2_gpio; + int detect_gpio; + int ready_gpio; +} colibri_pcmcia_gpio; + +static struct pcmcia_irqs colibri_irqs[] = { + { + .sock = 0, + .str = "PCMCIA CD" + }, +}; + +static int colibri_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + int ret; + + ret = gpio_request(colibri_pcmcia_gpio.detect_gpio, "DETECT"); + if (ret) + goto err1; + ret = gpio_direction_input(colibri_pcmcia_gpio.detect_gpio); + if (ret) + goto err2; + + ret = gpio_request(colibri_pcmcia_gpio.ready_gpio, "READY"); + if (ret) + goto err2; + ret = gpio_direction_input(colibri_pcmcia_gpio.ready_gpio); + if (ret) + goto err3; + + ret = gpio_request(colibri_pcmcia_gpio.bvd1_gpio, "BVD1"); + if (ret) + goto err3; + ret = gpio_direction_input(colibri_pcmcia_gpio.bvd1_gpio); + if (ret) + goto err4; + + ret = gpio_request(colibri_pcmcia_gpio.bvd2_gpio, "BVD2"); + if (ret) + goto err4; + ret = gpio_direction_input(colibri_pcmcia_gpio.bvd2_gpio); + if (ret) + goto err5; + + ret = gpio_request(colibri_pcmcia_gpio.ppen_gpio, "PPEN"); + if (ret) + goto err5; + ret = gpio_direction_output(colibri_pcmcia_gpio.ppen_gpio, 0); + if (ret) + goto err6; + + ret = gpio_request(colibri_pcmcia_gpio.reset_gpio, "RESET"); + if (ret) + goto err6; + ret = gpio_direction_output(colibri_pcmcia_gpio.reset_gpio, 1); + if (ret) + goto err7; + + colibri_irqs[0].irq = gpio_to_irq(colibri_pcmcia_gpio.detect_gpio); + skt->socket.pci_irq = gpio_to_irq(colibri_pcmcia_gpio.ready_gpio); + + return soc_pcmcia_request_irqs(skt, colibri_irqs, + ARRAY_SIZE(colibri_irqs)); + +err7: + gpio_free(colibri_pcmcia_gpio.detect_gpio); +err6: + gpio_free(colibri_pcmcia_gpio.ready_gpio); +err5: + gpio_free(colibri_pcmcia_gpio.bvd1_gpio); +err4: + gpio_free(colibri_pcmcia_gpio.bvd2_gpio); +err3: + gpio_free(colibri_pcmcia_gpio.reset_gpio); +err2: + gpio_free(colibri_pcmcia_gpio.ppen_gpio); +err1: + return ret; +} + +static void colibri_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + gpio_free(colibri_pcmcia_gpio.detect_gpio); + gpio_free(colibri_pcmcia_gpio.ready_gpio); + gpio_free(colibri_pcmcia_gpio.bvd1_gpio); + gpio_free(colibri_pcmcia_gpio.bvd2_gpio); + gpio_free(colibri_pcmcia_gpio.reset_gpio); + gpio_free(colibri_pcmcia_gpio.ppen_gpio); +} + +static void colibri_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + + state->detect = !!gpio_get_value(colibri_pcmcia_gpio.detect_gpio); + state->ready = !!gpio_get_value(colibri_pcmcia_gpio.ready_gpio); + state->bvd1 = !!gpio_get_value(colibri_pcmcia_gpio.bvd1_gpio); + state->bvd2 = !!gpio_get_value(colibri_pcmcia_gpio.bvd2_gpio); + state->wrprot = 0; + state->vs_3v = 1; + state->vs_Xv = 0; +} + +static int +colibri_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + gpio_set_value(colibri_pcmcia_gpio.ppen_gpio, + !(state->Vcc == 33 && state->Vpp < 50)); + gpio_set_value(colibri_pcmcia_gpio.reset_gpio, state->flags & SS_RESET); + return 0; +} + +static void colibri_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ +} + +static void colibri_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ +} + +static struct pcmcia_low_level colibri_pcmcia_ops = { + .owner = THIS_MODULE, + + .first = 0, + .nr = 1, + + .hw_init = colibri_pcmcia_hw_init, + .hw_shutdown = colibri_pcmcia_hw_shutdown, + + .socket_state = colibri_pcmcia_socket_state, + .configure_socket = colibri_pcmcia_configure_socket, + + .socket_init = colibri_pcmcia_socket_init, + .socket_suspend = colibri_pcmcia_socket_suspend, +}; + +static struct platform_device *colibri_pcmcia_device; + +static int __init colibri_pcmcia_init(void) +{ + int ret; + + colibri_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); + if (!colibri_pcmcia_device) + return -ENOMEM; + + /* Colibri PXA270 */ + if (machine_is_colibri()) { + colibri_pcmcia_gpio.reset_gpio = COLIBRI270_RESET_GPIO; + colibri_pcmcia_gpio.ppen_gpio = COLIBRI270_PPEN_GPIO; + colibri_pcmcia_gpio.bvd1_gpio = COLIBRI270_BVD1_GPIO; + colibri_pcmcia_gpio.bvd2_gpio = COLIBRI270_BVD2_GPIO; + colibri_pcmcia_gpio.detect_gpio = COLIBRI270_DETECT_GPIO; + colibri_pcmcia_gpio.ready_gpio = COLIBRI270_READY_GPIO; + } + + ret = platform_device_add_data(colibri_pcmcia_device, + &colibri_pcmcia_ops, sizeof(colibri_pcmcia_ops)); + + if (!ret) + ret = platform_device_add(colibri_pcmcia_device); + + if (ret) + platform_device_put(colibri_pcmcia_device); + + return ret; +} + +static void __exit colibri_pcmcia_exit(void) +{ + platform_device_unregister(colibri_pcmcia_device); +} + +module_init(colibri_pcmcia_init); +module_exit(colibri_pcmcia_exit); + +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION("PCMCIA support for Toradex Colibri PXA270"); +MODULE_ALIAS("platform:pxa2xx-pcmcia"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From fd62999bad9fc3b176ef6bc9d2a71be940efd908 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 11 Aug 2010 05:04:53 +0200 Subject: ARM: pxa: Colibri PXA320 PCMCIA driver Signed-off-by: Marek Vasut Acked-by: Daniel Mack Signed-off-by: Eric Miao --- arch/arm/mach-pxa/colibri-pxa320.c | 21 +++++++++++++++++++++ drivers/pcmcia/Kconfig | 3 ++- drivers/pcmcia/Makefile | 1 + drivers/pcmcia/pxa2xx_colibri.c | 17 ++++++++++++++++- 4 files changed, 40 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-pxa/colibri-pxa320.c b/arch/arm/mach-pxa/colibri-pxa320.c index 3d2c944b16e..ff9ff5f4fc4 100644 --- a/arch/arm/mach-pxa/colibri-pxa320.c +++ b/arch/arm/mach-pxa/colibri-pxa320.c @@ -73,6 +73,27 @@ static mfp_cfg_t colibri_pxa320_evalboard_pin_config[] __initdata = { /* I2C */ GPIO32_I2C_SCL, GPIO33_I2C_SDA, + + /* PCMCIA */ + MFP_CFG(GPIO59, AF7), /* PRST ; AF7 to tristate */ + MFP_CFG(GPIO61, AF7), /* PCE1 ; AF7 to tristate */ + MFP_CFG(GPIO60, AF7), /* PCE2 ; AF7 to tristate */ + MFP_CFG(GPIO62, AF7), /* PCD ; AF7 to tristate */ + MFP_CFG(GPIO56, AF7), /* PSKTSEL ; AF7 to tristate */ + GPIO27_GPIO, /* RDnWR ; input/tristate */ + GPIO50_GPIO, /* PREG ; input/tristate */ + GPIO2_RDY, + GPIO5_NPIOR, + GPIO6_NPIOW, + GPIO7_NPIOS16, + GPIO8_NPWAIT, + GPIO29_GPIO, /* PRDY (READY GPIO) */ + GPIO57_GPIO, /* PPEN (POWER GPIO) */ + GPIO81_GPIO, /* PCD (DETECT GPIO) */ + GPIO77_GPIO, /* PRST (RESET GPIO) */ + GPIO53_GPIO, /* PBVD1 */ + GPIO79_GPIO, /* PBVD2 */ + GPIO54_GPIO, /* POE */ }; #else static mfp_cfg_t colibri_pxa320_evalboard_pin_config[] __initdata = {}; diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index e9acf035437..de886f3dfd3 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -215,7 +215,8 @@ config PCMCIA_PXA2XX depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \ || MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \ || ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \ - || MACH_VPAC270 || MACH_BALLOON3 || MACH_COLIBRI) + || MACH_VPAC270 || MACH_BALLOON3 || MACH_COLIBRI \ + || MACH_COLIBRI320) select PCMCIA_SOC_COMMON help Say Y here to include support for the PXA2xx PCMCIA controller diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 2fee7ef0c39..9a44a90dcf7 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -71,6 +71,7 @@ pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o pxa2xx-obj-$(CONFIG_MACH_VPAC270) += pxa2xx_vpac270.o pxa2xx-obj-$(CONFIG_MACH_BALLOON3) += pxa2xx_balloon3.o pxa2xx-obj-$(CONFIG_MACH_COLIBRI) += pxa2xx_colibri.o +pxa2xx-obj-$(CONFIG_MACH_COLIBRI320) += pxa2xx_colibri.o obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_base.o $(pxa2xx-obj-y) diff --git a/drivers/pcmcia/pxa2xx_colibri.c b/drivers/pcmcia/pxa2xx_colibri.c index 4ed876c4398..c3f72192af6 100644 --- a/drivers/pcmcia/pxa2xx_colibri.c +++ b/drivers/pcmcia/pxa2xx_colibri.c @@ -27,6 +27,13 @@ #define COLIBRI270_DETECT_GPIO 84 #define COLIBRI270_READY_GPIO 1 +#define COLIBRI320_RESET_GPIO 77 +#define COLIBRI320_PPEN_GPIO 57 +#define COLIBRI320_BVD1_GPIO 53 +#define COLIBRI320_BVD2_GPIO 79 +#define COLIBRI320_DETECT_GPIO 81 +#define COLIBRI320_READY_GPIO 29 + static struct { int reset_gpio; int ppen_gpio; @@ -186,6 +193,14 @@ static int __init colibri_pcmcia_init(void) colibri_pcmcia_gpio.bvd2_gpio = COLIBRI270_BVD2_GPIO; colibri_pcmcia_gpio.detect_gpio = COLIBRI270_DETECT_GPIO; colibri_pcmcia_gpio.ready_gpio = COLIBRI270_READY_GPIO; + /* Colibri PXA320 */ + } else if (machine_is_colibri320()) { + colibri_pcmcia_gpio.reset_gpio = COLIBRI320_RESET_GPIO; + colibri_pcmcia_gpio.ppen_gpio = COLIBRI320_PPEN_GPIO; + colibri_pcmcia_gpio.bvd1_gpio = COLIBRI320_BVD1_GPIO; + colibri_pcmcia_gpio.bvd2_gpio = COLIBRI320_BVD2_GPIO; + colibri_pcmcia_gpio.detect_gpio = COLIBRI320_DETECT_GPIO; + colibri_pcmcia_gpio.ready_gpio = COLIBRI320_READY_GPIO; } ret = platform_device_add_data(colibri_pcmcia_device, @@ -209,6 +224,6 @@ module_init(colibri_pcmcia_init); module_exit(colibri_pcmcia_exit); MODULE_AUTHOR("Marek Vasut "); -MODULE_DESCRIPTION("PCMCIA support for Toradex Colibri PXA270"); +MODULE_DESCRIPTION("PCMCIA support for Toradex Colibri PXA270/PXA320"); MODULE_ALIAS("platform:pxa2xx-pcmcia"); MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From 1b9169d8a0fe2b41fbbb8d152c8108190865f3cf Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 19 Oct 2010 16:19:32 +0200 Subject: ARM: pxa: Update Balloon3 for new FPGA firmware The new FPGA firmware in Balloon3 uses different methods to control it's bus control lines. In the new version, there are separate registers to set/clear bus control lines. This patch updates affected places. Signed-off-by: Marek Vasut Signed-off-by: Eric Miao --- arch/arm/mach-pxa/balloon3.c | 51 ++++++++++++++++++------------- arch/arm/mach-pxa/include/mach/balloon3.h | 2 ++ drivers/pcmcia/pxa2xx_balloon3.c | 11 +++---- 3 files changed, 36 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-pxa/balloon3.c b/arch/arm/mach-pxa/balloon3.c index adb8e9f355d..d4649c0e23f 100644 --- a/arch/arm/mach-pxa/balloon3.c +++ b/arch/arm/mach-pxa/balloon3.c @@ -567,27 +567,29 @@ static inline void balloon3_i2c_init(void) {} * NAND ******************************************************************************/ #if defined(CONFIG_MTD_NAND_PLATFORM)||defined(CONFIG_MTD_NAND_PLATFORM_MODULE) -static uint16_t balloon3_ctl = - BALLOON3_NAND_CONTROL_FLCE0 | BALLOON3_NAND_CONTROL_FLCE1 | - BALLOON3_NAND_CONTROL_FLCE2 | BALLOON3_NAND_CONTROL_FLCE3 | - BALLOON3_NAND_CONTROL_FLWP; - static void balloon3_nand_cmd_ctl(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct nand_chip *this = mtd->priv; + uint8_t balloon3_ctl_set = 0, balloon3_ctl_clr = 0; if (ctrl & NAND_CTRL_CHANGE) { if (ctrl & NAND_CLE) - balloon3_ctl |= BALLOON3_NAND_CONTROL_FLCLE; + balloon3_ctl_set |= BALLOON3_NAND_CONTROL_FLCLE; else - balloon3_ctl &= ~BALLOON3_NAND_CONTROL_FLCLE; + balloon3_ctl_clr |= BALLOON3_NAND_CONTROL_FLCLE; if (ctrl & NAND_ALE) - balloon3_ctl |= BALLOON3_NAND_CONTROL_FLALE; + balloon3_ctl_set |= BALLOON3_NAND_CONTROL_FLALE; else - balloon3_ctl &= ~BALLOON3_NAND_CONTROL_FLALE; - - __raw_writel(balloon3_ctl, BALLOON3_NAND_CONTROL_REG); + balloon3_ctl_clr |= BALLOON3_NAND_CONTROL_FLALE; + + if (balloon3_ctl_clr) + __raw_writel(balloon3_ctl_clr, + BALLOON3_NAND_CONTROL_REG); + if (balloon3_ctl_set) + __raw_writel(balloon3_ctl_set, + BALLOON3_NAND_CONTROL_REG | + BALLOON3_FPGA_SETnCLR); } if (cmd != NAND_CMD_NONE) @@ -599,15 +601,15 @@ static void balloon3_nand_select_chip(struct mtd_info *mtd, int chip) if (chip < 0 || chip > 3) return; - balloon3_ctl |= BALLOON3_NAND_CONTROL_FLCE0 | - BALLOON3_NAND_CONTROL_FLCE1 | - BALLOON3_NAND_CONTROL_FLCE2 | - BALLOON3_NAND_CONTROL_FLCE3; + /* Assert all nCE lines */ + __raw_writew( + BALLOON3_NAND_CONTROL_FLCE0 | BALLOON3_NAND_CONTROL_FLCE1 | + BALLOON3_NAND_CONTROL_FLCE2 | BALLOON3_NAND_CONTROL_FLCE3, + BALLOON3_NAND_CONTROL_REG | BALLOON3_FPGA_SETnCLR); /* Deassert correct nCE line */ - balloon3_ctl &= ~(BALLOON3_NAND_CONTROL_FLCE0 << chip); - - __raw_writew(balloon3_ctl, BALLOON3_NAND_CONTROL_REG); + __raw_writew(BALLOON3_NAND_CONTROL_FLCE0 << chip, + BALLOON3_NAND_CONTROL_REG); } static int balloon3_nand_probe(struct platform_device *pdev) @@ -616,11 +618,12 @@ static int balloon3_nand_probe(struct platform_device *pdev) uint16_t ver; int ret; - __raw_writew(BALLOON3_NAND_CONTROL2_16BIT, BALLOON3_NAND_CONTROL2_REG); + __raw_writew(BALLOON3_NAND_CONTROL2_16BIT, + BALLOON3_NAND_CONTROL2_REG | BALLOON3_FPGA_SETnCLR); ver = __raw_readw(BALLOON3_FPGA_VER); - if (ver > 0x0201) - pr_warn("The FPGA code, version 0x%04x, is newer than rel-0.3. " + if (ver < 0x4f08) + pr_warn("The FPGA code, version 0x%04x, is too old. " "NAND support might be broken in this version!", ver); /* Power up the NAND chips */ @@ -635,7 +638,11 @@ static int balloon3_nand_probe(struct platform_device *pdev) gpio_set_value(BALLOON3_GPIO_RUN_NAND, 1); /* Deassert all nCE lines and write protect line */ - __raw_writel(balloon3_ctl, BALLOON3_NAND_CONTROL_REG); + __raw_writel( + BALLOON3_NAND_CONTROL_FLCE0 | BALLOON3_NAND_CONTROL_FLCE1 | + BALLOON3_NAND_CONTROL_FLCE2 | BALLOON3_NAND_CONTROL_FLCE3 | + BALLOON3_NAND_CONTROL_FLWP, + BALLOON3_NAND_CONTROL_REG | BALLOON3_FPGA_SETnCLR); return 0; err2: diff --git a/arch/arm/mach-pxa/include/mach/balloon3.h b/arch/arm/mach-pxa/include/mach/balloon3.h index 3506da1f27b..f6c81ea5148 100644 --- a/arch/arm/mach-pxa/include/mach/balloon3.h +++ b/arch/arm/mach-pxa/include/mach/balloon3.h @@ -26,6 +26,8 @@ enum balloon3_features { #define BALLOON3_FPGA_VIRT (0xf1000000) /* as per balloon2 */ #define BALLOON3_FPGA_LENGTH 0x01000000 +#define BALLOON3_FPGA_SETnCLR (0x1000) + /* FPGA / CPLD registers for CF socket */ #define BALLOON3_CF_STATUS_REG (BALLOON3_FPGA_VIRT + 0x00e00008) #define BALLOON3_CF_CONTROL_REG (BALLOON3_FPGA_VIRT + 0x00e00008) diff --git a/drivers/pcmcia/pxa2xx_balloon3.c b/drivers/pcmcia/pxa2xx_balloon3.c index dbbdd006320..453c54c9761 100644 --- a/drivers/pcmcia/pxa2xx_balloon3.c +++ b/drivers/pcmcia/pxa2xx_balloon3.c @@ -39,12 +39,10 @@ static struct pcmcia_irqs irqs[] = { static int balloon3_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { uint16_t ver; - int ret; - static void __iomem *fpga_ver; ver = __raw_readw(BALLOON3_FPGA_VER); - if (ver > 0x0201) - pr_warn("The FPGA code, version 0x%04x, is newer than rel-0.3. " + if (ver < 0x4f08) + pr_warn("The FPGA code, version 0x%04x, is too old. " "PCMCIA/CF support might be broken in this version!", ver); @@ -97,8 +95,9 @@ static void balloon3_pcmcia_socket_state(struct soc_pcmcia_socket *skt, static int balloon3_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) { - __raw_writew((state->flags & SS_RESET) ? BALLOON3_CF_RESET : 0, - BALLOON3_CF_CONTROL_REG); + __raw_writew(BALLOON3_CF_RESET, BALLOON3_CF_CONTROL_REG | + ((state->flags & SS_RESET) ? + BALLOON3_FPGA_SETnCLR : 0)); return 0; } -- cgit v1.2.3-70-g09d2 From 11c8ea81cc639c2ea56f94a9cdaa6242ff13a3af Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 17 Dec 2010 21:16:23 +0100 Subject: ux500: rename modem IRQ and MBOX files Suffix the U5500 modem IRQ and MBOX files with *-db5500* so that we clearly know the SoC they belong to, in line with the rest of the files in mach-ux500. Cc: Stefan Nilsson Cc: Martin Persson Signed-off-by: Linus Walleij --- arch/arm/mach-ux500/Makefile | 4 +- arch/arm/mach-ux500/include/mach/mbox-db5500.h | 88 ++++ arch/arm/mach-ux500/include/mach/mbox.h | 88 ---- arch/arm/mach-ux500/mbox-db5500.c | 567 +++++++++++++++++++++++++ arch/arm/mach-ux500/mbox.c | 567 ------------------------- arch/arm/mach-ux500/modem-irq-db5500.c | 139 ++++++ arch/arm/mach-ux500/modem_irq.c | 139 ------ drivers/net/caif/caif_shm_u5500.c | 2 +- 8 files changed, 797 insertions(+), 797 deletions(-) create mode 100644 arch/arm/mach-ux500/include/mach/mbox-db5500.h delete mode 100644 arch/arm/mach-ux500/include/mach/mbox.h create mode 100644 arch/arm/mach-ux500/mbox-db5500.c delete mode 100644 arch/arm/mach-ux500/mbox.c create mode 100644 arch/arm/mach-ux500/modem-irq-db5500.c delete mode 100644 arch/arm/mach-ux500/modem_irq.c (limited to 'drivers') diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile index 3d2c6a510a8..12052e8e064 100644 --- a/arch/arm/mach-ux500/Makefile +++ b/arch/arm/mach-ux500/Makefile @@ -12,6 +12,6 @@ obj-$(CONFIG_SMP) += platsmp.o headsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o obj-$(CONFIG_REGULATOR_AB8500) += board-mop500-regulators.o -obj-$(CONFIG_U5500_MODEM_IRQ) += modem_irq.o -obj-$(CONFIG_U5500_MBOX) += mbox.o +obj-$(CONFIG_U5500_MODEM_IRQ) += modem-irq-db5500.o +obj-$(CONFIG_U5500_MBOX) += mbox-db5500.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o diff --git a/arch/arm/mach-ux500/include/mach/mbox-db5500.h b/arch/arm/mach-ux500/include/mach/mbox-db5500.h new file mode 100644 index 00000000000..7f9da4d2fbd --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/mbox-db5500.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Stefan Nilsson for ST-Ericsson. + * Author: Martin Persson for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +#ifndef __INC_STE_MBOX_H +#define __INC_STE_MBOX_H + +#define MBOX_BUF_SIZE 16 +#define MBOX_NAME_SIZE 8 + +/** + * mbox_recv_cb_t - Definition of the mailbox callback. + * @mbox_msg: The mailbox message. + * @priv: The clients private data as specified in the call to mbox_setup. + * + * This function will be called upon reception of new mailbox messages. + */ +typedef void mbox_recv_cb_t (u32 mbox_msg, void *priv); + +/** + * struct mbox - Mailbox instance struct + * @list: Linked list head. + * @pdev: Pointer to device struct. + * @cb: Callback function. Will be called + * when new data is received. + * @client_data: Clients private data. Will be sent back + * in the callback function. + * @virtbase_peer: Virtual address for outgoing mailbox. + * @virtbase_local: Virtual address for incoming mailbox. + * @buffer: Then internal queue for outgoing messages. + * @name: Name of this mailbox. + * @buffer_available: Completion variable to achieve "blocking send". + * This variable will be signaled when there is + * internal buffer space available. + * @client_blocked: To keep track if any client is currently + * blocked. + * @lock: Spinlock to protect this mailbox instance. + * @write_index: Index in internal buffer to write to. + * @read_index: Index in internal buffer to read from. + * @allocated: Indicates whether this particular mailbox + * id has been allocated by someone. + */ +struct mbox { + struct list_head list; + struct platform_device *pdev; + mbox_recv_cb_t *cb; + void *client_data; + void __iomem *virtbase_peer; + void __iomem *virtbase_local; + u32 buffer[MBOX_BUF_SIZE]; + char name[MBOX_NAME_SIZE]; + struct completion buffer_available; + u8 client_blocked; + spinlock_t lock; + u8 write_index; + u8 read_index; + bool allocated; +}; + +/** + * mbox_setup - Set up a mailbox and return its instance. + * @mbox_id: The ID number of the mailbox. 0 or 1 for modem CPU, + * 2 for modem DSP. + * @mbox_cb: Pointer to the callback function to be called when a new message + * is received. + * @priv: Client user data which will be returned in the callback. + * + * Returns a mailbox instance to be specified in subsequent calls to mbox_send. + */ +struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv); + +/** + * mbox_send - Send a mailbox message. + * @mbox: Mailbox instance (returned by mbox_setup) + * @mbox_msg: The mailbox message to send. + * @block: Specifies whether this call will block until send is possible, + * or return an error if the mailbox buffer is full. + * + * Returns 0 on success or a negative error code on error. -ENOMEM indicates + * that the internal buffer is full and you have to try again later (or + * specify "block" in order to block until send is possible). + */ +int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block); + +#endif /*INC_STE_MBOX_H*/ diff --git a/arch/arm/mach-ux500/include/mach/mbox.h b/arch/arm/mach-ux500/include/mach/mbox.h deleted file mode 100644 index 7f9da4d2fbd..00000000000 --- a/arch/arm/mach-ux500/include/mach/mbox.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * Author: Stefan Nilsson for ST-Ericsson. - * Author: Martin Persson for ST-Ericsson. - * License terms: GNU General Public License (GPL), version 2. - */ - -#ifndef __INC_STE_MBOX_H -#define __INC_STE_MBOX_H - -#define MBOX_BUF_SIZE 16 -#define MBOX_NAME_SIZE 8 - -/** - * mbox_recv_cb_t - Definition of the mailbox callback. - * @mbox_msg: The mailbox message. - * @priv: The clients private data as specified in the call to mbox_setup. - * - * This function will be called upon reception of new mailbox messages. - */ -typedef void mbox_recv_cb_t (u32 mbox_msg, void *priv); - -/** - * struct mbox - Mailbox instance struct - * @list: Linked list head. - * @pdev: Pointer to device struct. - * @cb: Callback function. Will be called - * when new data is received. - * @client_data: Clients private data. Will be sent back - * in the callback function. - * @virtbase_peer: Virtual address for outgoing mailbox. - * @virtbase_local: Virtual address for incoming mailbox. - * @buffer: Then internal queue for outgoing messages. - * @name: Name of this mailbox. - * @buffer_available: Completion variable to achieve "blocking send". - * This variable will be signaled when there is - * internal buffer space available. - * @client_blocked: To keep track if any client is currently - * blocked. - * @lock: Spinlock to protect this mailbox instance. - * @write_index: Index in internal buffer to write to. - * @read_index: Index in internal buffer to read from. - * @allocated: Indicates whether this particular mailbox - * id has been allocated by someone. - */ -struct mbox { - struct list_head list; - struct platform_device *pdev; - mbox_recv_cb_t *cb; - void *client_data; - void __iomem *virtbase_peer; - void __iomem *virtbase_local; - u32 buffer[MBOX_BUF_SIZE]; - char name[MBOX_NAME_SIZE]; - struct completion buffer_available; - u8 client_blocked; - spinlock_t lock; - u8 write_index; - u8 read_index; - bool allocated; -}; - -/** - * mbox_setup - Set up a mailbox and return its instance. - * @mbox_id: The ID number of the mailbox. 0 or 1 for modem CPU, - * 2 for modem DSP. - * @mbox_cb: Pointer to the callback function to be called when a new message - * is received. - * @priv: Client user data which will be returned in the callback. - * - * Returns a mailbox instance to be specified in subsequent calls to mbox_send. - */ -struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv); - -/** - * mbox_send - Send a mailbox message. - * @mbox: Mailbox instance (returned by mbox_setup) - * @mbox_msg: The mailbox message to send. - * @block: Specifies whether this call will block until send is possible, - * or return an error if the mailbox buffer is full. - * - * Returns 0 on success or a negative error code on error. -ENOMEM indicates - * that the internal buffer is full and you have to try again later (or - * specify "block" in order to block until send is possible). - */ -int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block); - -#endif /*INC_STE_MBOX_H*/ diff --git a/arch/arm/mach-ux500/mbox-db5500.c b/arch/arm/mach-ux500/mbox-db5500.c new file mode 100644 index 00000000000..cbf15718fc3 --- /dev/null +++ b/arch/arm/mach-ux500/mbox-db5500.c @@ -0,0 +1,567 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Stefan Nilsson for ST-Ericsson. + * Author: Martin Persson for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +/* + * Mailbox nomenclature: + * + * APE MODEM + * mbox pairX + * .......................... + * . . + * . peer . + * . send ---- . + * . --> | | . + * . | | . + * . ---- . + * . . + * . local . + * . rec ---- . + * . | | <-- . + * . | | . + * . ---- . + * ......................... + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MBOX_NAME "mbox" + +#define MBOX_FIFO_DATA 0x000 +#define MBOX_FIFO_ADD 0x004 +#define MBOX_FIFO_REMOVE 0x008 +#define MBOX_FIFO_THRES_FREE 0x00C +#define MBOX_FIFO_THRES_OCCUP 0x010 +#define MBOX_FIFO_STATUS 0x014 + +#define MBOX_DISABLE_IRQ 0x4 +#define MBOX_ENABLE_IRQ 0x0 +#define MBOX_LATCH 1 + +/* Global list of all mailboxes */ +static struct list_head mboxs = LIST_HEAD_INIT(mboxs); + +static struct mbox *get_mbox_with_id(u8 id) +{ + u8 i; + struct list_head *pos = &mboxs; + for (i = 0; i <= id; i++) + pos = pos->next; + + return (struct mbox *) list_entry(pos, struct mbox, list); +} + +int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block) +{ + int res = 0; + + spin_lock(&mbox->lock); + + dev_dbg(&(mbox->pdev->dev), + "About to buffer 0x%X to mailbox 0x%X." + " ri = %d, wi = %d\n", + mbox_msg, (u32)mbox, mbox->read_index, + mbox->write_index); + + /* Check if write buffer is full */ + while (((mbox->write_index + 1) % MBOX_BUF_SIZE) == mbox->read_index) { + if (!block) { + dev_dbg(&(mbox->pdev->dev), + "Buffer full in non-blocking call! " + "Returning -ENOMEM!\n"); + res = -ENOMEM; + goto exit; + } + spin_unlock(&mbox->lock); + dev_dbg(&(mbox->pdev->dev), + "Buffer full in blocking call! Sleeping...\n"); + mbox->client_blocked = 1; + wait_for_completion(&mbox->buffer_available); + dev_dbg(&(mbox->pdev->dev), + "Blocking send was woken up! Trying again...\n"); + spin_lock(&mbox->lock); + } + + mbox->buffer[mbox->write_index] = mbox_msg; + mbox->write_index = (mbox->write_index + 1) % MBOX_BUF_SIZE; + + /* + * Indicate that we want an IRQ as soon as there is a slot + * in the FIFO + */ + writel(MBOX_ENABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); + +exit: + spin_unlock(&mbox->lock); + return res; +} +EXPORT_SYMBOL(mbox_send); + +#if defined(CONFIG_DEBUG_FS) +/* + * Expected input: + * Example: "echo 0xdeadbeef 4 > mbox-node" sends 0xdeadbeef 4 times + */ +static ssize_t mbox_write_fifo(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + unsigned long mbox_mess; + unsigned long nbr_sends; + unsigned long i; + char int_buf[16]; + char *token; + char *val; + + struct mbox *mbox = (struct mbox *) dev->platform_data; + + strncpy((char *) &int_buf, buf, sizeof(int_buf)); + token = (char *) &int_buf; + + /* Parse message */ + val = strsep(&token, " "); + if ((val == NULL) || (strict_strtoul(val, 16, &mbox_mess) != 0)) + mbox_mess = 0xDEADBEEF; + + val = strsep(&token, " "); + if ((val == NULL) || (strict_strtoul(val, 10, &nbr_sends) != 0)) + nbr_sends = 1; + + dev_dbg(dev, "Will write 0x%lX %ld times using data struct at 0x%X\n", + mbox_mess, nbr_sends, (u32) mbox); + + for (i = 0; i < nbr_sends; i++) + mbox_send(mbox, mbox_mess, true); + + return count; +} + +static ssize_t mbox_read_fifo(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int mbox_value; + struct mbox *mbox = (struct mbox *) dev->platform_data; + + if ((readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7) <= 0) + return sprintf(buf, "Mailbox is empty\n"); + + mbox_value = readl(mbox->virtbase_local + MBOX_FIFO_DATA); + writel(MBOX_LATCH, (mbox->virtbase_local + MBOX_FIFO_REMOVE)); + + return sprintf(buf, "0x%X\n", mbox_value); +} + +static DEVICE_ATTR(fifo, S_IWUGO | S_IRUGO, mbox_read_fifo, mbox_write_fifo); + +static int mbox_show(struct seq_file *s, void *data) +{ + struct list_head *pos; + u8 mbox_index = 0; + + list_for_each(pos, &mboxs) { + struct mbox *m = + (struct mbox *) list_entry(pos, struct mbox, list); + if (m == NULL) { + seq_printf(s, + "Unable to retrieve mailbox %d\n", + mbox_index); + continue; + } + + spin_lock(&m->lock); + if ((m->virtbase_peer == NULL) || (m->virtbase_local == NULL)) { + seq_printf(s, "MAILBOX %d not setup or corrupt\n", + mbox_index); + spin_unlock(&m->lock); + continue; + } + + seq_printf(s, + "===========================\n" + " MAILBOX %d\n" + " PEER MAILBOX DUMP\n" + "---------------------------\n" + "FIFO: 0x%X (%d)\n" + "Free Threshold: 0x%.2X (%d)\n" + "Occupied Threshold: 0x%.2X (%d)\n" + "Status: 0x%.2X (%d)\n" + " Free spaces (ot): %d (%d)\n" + " Occup spaces (ot): %d (%d)\n" + "===========================\n" + " LOCAL MAILBOX DUMP\n" + "---------------------------\n" + "FIFO: 0x%.X (%d)\n" + "Free Threshold: 0x%.2X (%d)\n" + "Occupied Threshold: 0x%.2X (%d)\n" + "Status: 0x%.2X (%d)\n" + " Free spaces (ot): %d (%d)\n" + " Occup spaces (ot): %d (%d)\n" + "===========================\n" + "write_index: %d\n" + "read_index : %d\n" + "===========================\n" + "\n", + mbox_index, + readl(m->virtbase_peer + MBOX_FIFO_DATA), + readl(m->virtbase_peer + MBOX_FIFO_DATA), + readl(m->virtbase_peer + MBOX_FIFO_THRES_FREE), + readl(m->virtbase_peer + MBOX_FIFO_THRES_FREE), + readl(m->virtbase_peer + MBOX_FIFO_THRES_OCCUP), + readl(m->virtbase_peer + MBOX_FIFO_THRES_OCCUP), + readl(m->virtbase_peer + MBOX_FIFO_STATUS), + readl(m->virtbase_peer + MBOX_FIFO_STATUS), + (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 4) & 0x7, + (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 7) & 0x1, + (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 0) & 0x7, + (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 3) & 0x1, + readl(m->virtbase_local + MBOX_FIFO_DATA), + readl(m->virtbase_local + MBOX_FIFO_DATA), + readl(m->virtbase_local + MBOX_FIFO_THRES_FREE), + readl(m->virtbase_local + MBOX_FIFO_THRES_FREE), + readl(m->virtbase_local + MBOX_FIFO_THRES_OCCUP), + readl(m->virtbase_local + MBOX_FIFO_THRES_OCCUP), + readl(m->virtbase_local + MBOX_FIFO_STATUS), + readl(m->virtbase_local + MBOX_FIFO_STATUS), + (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 4) & 0x7, + (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 7) & 0x1, + (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 0) & 0x7, + (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 3) & 0x1, + m->write_index, m->read_index); + mbox_index++; + spin_unlock(&m->lock); + } + + return 0; +} + +static int mbox_open(struct inode *inode, struct file *file) +{ + return single_open(file, mbox_show, NULL); +} + +static const struct file_operations mbox_operations = { + .owner = THIS_MODULE, + .open = mbox_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +static irqreturn_t mbox_irq(int irq, void *arg) +{ + u32 mbox_value; + int nbr_occup; + int nbr_free; + struct mbox *mbox = (struct mbox *) arg; + + spin_lock(&mbox->lock); + + dev_dbg(&(mbox->pdev->dev), + "mbox IRQ [%d] received. ri = %d, wi = %d\n", + irq, mbox->read_index, mbox->write_index); + + /* + * Check if we have any outgoing messages, and if there is space for + * them in the FIFO. + */ + if (mbox->read_index != mbox->write_index) { + /* + * Check by reading FREE for LOCAL since that indicates + * OCCUP for PEER + */ + nbr_free = (readl(mbox->virtbase_local + MBOX_FIFO_STATUS) + >> 4) & 0x7; + dev_dbg(&(mbox->pdev->dev), + "Status indicates %d empty spaces in the FIFO!\n", + nbr_free); + + while ((nbr_free > 0) && + (mbox->read_index != mbox->write_index)) { + /* Write the message and latch it into the FIFO */ + writel(mbox->buffer[mbox->read_index], + (mbox->virtbase_peer + MBOX_FIFO_DATA)); + writel(MBOX_LATCH, + (mbox->virtbase_peer + MBOX_FIFO_ADD)); + dev_dbg(&(mbox->pdev->dev), + "Wrote message 0x%X to addr 0x%X\n", + mbox->buffer[mbox->read_index], + (u32) (mbox->virtbase_peer + MBOX_FIFO_DATA)); + + nbr_free--; + mbox->read_index = + (mbox->read_index + 1) % MBOX_BUF_SIZE; + } + + /* + * Check if we still want IRQ:s when there is free + * space to send + */ + if (mbox->read_index != mbox->write_index) { + dev_dbg(&(mbox->pdev->dev), + "Still have messages to send, but FIFO full. " + "Request IRQ again!\n"); + writel(MBOX_ENABLE_IRQ, + mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); + } else { + dev_dbg(&(mbox->pdev->dev), + "No more messages to send. " + "Do not request IRQ again!\n"); + writel(MBOX_DISABLE_IRQ, + mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); + } + + /* + * Check if we can signal any blocked clients that it is OK to + * start buffering again + */ + if (mbox->client_blocked && + (((mbox->write_index + 1) % MBOX_BUF_SIZE) + != mbox->read_index)) { + dev_dbg(&(mbox->pdev->dev), + "Waking up blocked client\n"); + complete(&mbox->buffer_available); + mbox->client_blocked = 0; + } + } + + /* Check if we have any incoming messages */ + nbr_occup = readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7; + if (nbr_occup == 0) + goto exit; + + if (mbox->cb == NULL) { + dev_dbg(&(mbox->pdev->dev), "No receive callback registered, " + "leaving %d incoming messages in fifo!\n", nbr_occup); + goto exit; + } + + /* Read and acknowledge the message */ + mbox_value = readl(mbox->virtbase_local + MBOX_FIFO_DATA); + writel(MBOX_LATCH, (mbox->virtbase_local + MBOX_FIFO_REMOVE)); + + /* Notify consumer of new mailbox message */ + dev_dbg(&(mbox->pdev->dev), "Calling callback for message 0x%X!\n", + mbox_value); + mbox->cb(mbox_value, mbox->client_data); + +exit: + dev_dbg(&(mbox->pdev->dev), "Exit mbox IRQ. ri = %d, wi = %d\n", + mbox->read_index, mbox->write_index); + spin_unlock(&mbox->lock); + + return IRQ_HANDLED; +} + +/* Setup is executed once for each mbox pair */ +struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv) +{ + struct resource *resource; + int irq; + int res; + struct mbox *mbox; + + mbox = get_mbox_with_id(mbox_id); + if (mbox == NULL) { + dev_err(&(mbox->pdev->dev), "Incorrect mailbox id: %d!\n", + mbox_id); + goto exit; + } + + /* + * Check if mailbox has been allocated to someone else, + * otherwise allocate it + */ + if (mbox->allocated) { + dev_err(&(mbox->pdev->dev), "Mailbox number %d is busy!\n", + mbox_id); + mbox = NULL; + goto exit; + } + mbox->allocated = true; + + dev_dbg(&(mbox->pdev->dev), "Initiating mailbox number %d: 0x%X...\n", + mbox_id, (u32)mbox); + + mbox->client_data = priv; + mbox->cb = mbox_cb; + + /* Get addr for peer mailbox and ioremap it */ + resource = platform_get_resource_byname(mbox->pdev, + IORESOURCE_MEM, + "mbox_peer"); + if (resource == NULL) { + dev_err(&(mbox->pdev->dev), + "Unable to retrieve mbox peer resource\n"); + mbox = NULL; + goto exit; + } + dev_dbg(&(mbox->pdev->dev), + "Resource name: %s start: 0x%X, end: 0x%X\n", + resource->name, resource->start, resource->end); + mbox->virtbase_peer = + ioremap(resource->start, resource->end - resource->start); + if (!mbox->virtbase_peer) { + dev_err(&(mbox->pdev->dev), "Unable to ioremap peer mbox\n"); + mbox = NULL; + goto exit; + } + dev_dbg(&(mbox->pdev->dev), + "ioremapped peer physical: (0x%X-0x%X) to virtual: 0x%X\n", + resource->start, resource->end, (u32) mbox->virtbase_peer); + + /* Get addr for local mailbox and ioremap it */ + resource = platform_get_resource_byname(mbox->pdev, + IORESOURCE_MEM, + "mbox_local"); + if (resource == NULL) { + dev_err(&(mbox->pdev->dev), + "Unable to retrieve mbox local resource\n"); + mbox = NULL; + goto exit; + } + dev_dbg(&(mbox->pdev->dev), + "Resource name: %s start: 0x%X, end: 0x%X\n", + resource->name, resource->start, resource->end); + mbox->virtbase_local = + ioremap(resource->start, resource->end - resource->start); + if (!mbox->virtbase_local) { + dev_err(&(mbox->pdev->dev), "Unable to ioremap local mbox\n"); + mbox = NULL; + goto exit; + } + dev_dbg(&(mbox->pdev->dev), + "ioremapped local physical: (0x%X-0x%X) to virtual: 0x%X\n", + resource->start, resource->end, (u32) mbox->virtbase_peer); + + init_completion(&mbox->buffer_available); + mbox->client_blocked = 0; + + /* Get IRQ for mailbox and allocate it */ + irq = platform_get_irq_byname(mbox->pdev, "mbox_irq"); + if (irq < 0) { + dev_err(&(mbox->pdev->dev), + "Unable to retrieve mbox irq resource\n"); + mbox = NULL; + goto exit; + } + + dev_dbg(&(mbox->pdev->dev), "Allocating irq %d...\n", irq); + res = request_irq(irq, mbox_irq, 0, mbox->name, (void *) mbox); + if (res < 0) { + dev_err(&(mbox->pdev->dev), + "Unable to allocate mbox irq %d\n", irq); + mbox = NULL; + goto exit; + } + + /* Set up mailbox to not launch IRQ on free space in mailbox */ + writel(MBOX_DISABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); + + /* + * Set up mailbox to launch IRQ on new message if we have + * a callback set. If not, do not raise IRQ, but keep message + * in FIFO for manual retrieval + */ + if (mbox_cb != NULL) + writel(MBOX_ENABLE_IRQ, + mbox->virtbase_local + MBOX_FIFO_THRES_OCCUP); + else + writel(MBOX_DISABLE_IRQ, + mbox->virtbase_local + MBOX_FIFO_THRES_OCCUP); + +#if defined(CONFIG_DEBUG_FS) + res = device_create_file(&(mbox->pdev->dev), &dev_attr_fifo); + if (res != 0) + dev_warn(&(mbox->pdev->dev), + "Unable to create mbox sysfs entry"); + + (void) debugfs_create_file("mbox", S_IFREG | S_IRUGO, NULL, + NULL, &mbox_operations); +#endif + + dev_info(&(mbox->pdev->dev), + "Mailbox driver with index %d initated!\n", mbox_id); + +exit: + return mbox; +} +EXPORT_SYMBOL(mbox_setup); + + +int __init mbox_probe(struct platform_device *pdev) +{ + struct mbox local_mbox; + struct mbox *mbox; + int res = 0; + dev_dbg(&(pdev->dev), "Probing mailbox (pdev = 0x%X)...\n", (u32) pdev); + + memset(&local_mbox, 0x0, sizeof(struct mbox)); + + /* Associate our mbox data with the platform device */ + res = platform_device_add_data(pdev, + (void *) &local_mbox, + sizeof(struct mbox)); + if (res != 0) { + dev_err(&(pdev->dev), + "Unable to allocate driver platform data!\n"); + goto exit; + } + + mbox = (struct mbox *) pdev->dev.platform_data; + mbox->pdev = pdev; + mbox->write_index = 0; + mbox->read_index = 0; + + INIT_LIST_HEAD(&(mbox->list)); + list_add_tail(&(mbox->list), &mboxs); + + sprintf(mbox->name, "%s", MBOX_NAME); + spin_lock_init(&mbox->lock); + + dev_info(&(pdev->dev), "Mailbox driver loaded\n"); + +exit: + return res; +} + +static struct platform_driver mbox_driver = { + .driver = { + .name = MBOX_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init mbox_init(void) +{ + return platform_driver_probe(&mbox_driver, mbox_probe); +} + +module_init(mbox_init); + +void __exit mbox_exit(void) +{ + platform_driver_unregister(&mbox_driver); +} + +module_exit(mbox_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MBOX driver"); diff --git a/arch/arm/mach-ux500/mbox.c b/arch/arm/mach-ux500/mbox.c deleted file mode 100644 index 63435389c54..00000000000 --- a/arch/arm/mach-ux500/mbox.c +++ /dev/null @@ -1,567 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * Author: Stefan Nilsson for ST-Ericsson. - * Author: Martin Persson for ST-Ericsson. - * License terms: GNU General Public License (GPL), version 2. - */ - -/* - * Mailbox nomenclature: - * - * APE MODEM - * mbox pairX - * .......................... - * . . - * . peer . - * . send ---- . - * . --> | | . - * . | | . - * . ---- . - * . . - * . local . - * . rec ---- . - * . | | <-- . - * . | | . - * . ---- . - * ......................... - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MBOX_NAME "mbox" - -#define MBOX_FIFO_DATA 0x000 -#define MBOX_FIFO_ADD 0x004 -#define MBOX_FIFO_REMOVE 0x008 -#define MBOX_FIFO_THRES_FREE 0x00C -#define MBOX_FIFO_THRES_OCCUP 0x010 -#define MBOX_FIFO_STATUS 0x014 - -#define MBOX_DISABLE_IRQ 0x4 -#define MBOX_ENABLE_IRQ 0x0 -#define MBOX_LATCH 1 - -/* Global list of all mailboxes */ -static struct list_head mboxs = LIST_HEAD_INIT(mboxs); - -static struct mbox *get_mbox_with_id(u8 id) -{ - u8 i; - struct list_head *pos = &mboxs; - for (i = 0; i <= id; i++) - pos = pos->next; - - return (struct mbox *) list_entry(pos, struct mbox, list); -} - -int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block) -{ - int res = 0; - - spin_lock(&mbox->lock); - - dev_dbg(&(mbox->pdev->dev), - "About to buffer 0x%X to mailbox 0x%X." - " ri = %d, wi = %d\n", - mbox_msg, (u32)mbox, mbox->read_index, - mbox->write_index); - - /* Check if write buffer is full */ - while (((mbox->write_index + 1) % MBOX_BUF_SIZE) == mbox->read_index) { - if (!block) { - dev_dbg(&(mbox->pdev->dev), - "Buffer full in non-blocking call! " - "Returning -ENOMEM!\n"); - res = -ENOMEM; - goto exit; - } - spin_unlock(&mbox->lock); - dev_dbg(&(mbox->pdev->dev), - "Buffer full in blocking call! Sleeping...\n"); - mbox->client_blocked = 1; - wait_for_completion(&mbox->buffer_available); - dev_dbg(&(mbox->pdev->dev), - "Blocking send was woken up! Trying again...\n"); - spin_lock(&mbox->lock); - } - - mbox->buffer[mbox->write_index] = mbox_msg; - mbox->write_index = (mbox->write_index + 1) % MBOX_BUF_SIZE; - - /* - * Indicate that we want an IRQ as soon as there is a slot - * in the FIFO - */ - writel(MBOX_ENABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); - -exit: - spin_unlock(&mbox->lock); - return res; -} -EXPORT_SYMBOL(mbox_send); - -#if defined(CONFIG_DEBUG_FS) -/* - * Expected input: - * Example: "echo 0xdeadbeef 4 > mbox-node" sends 0xdeadbeef 4 times - */ -static ssize_t mbox_write_fifo(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) -{ - unsigned long mbox_mess; - unsigned long nbr_sends; - unsigned long i; - char int_buf[16]; - char *token; - char *val; - - struct mbox *mbox = (struct mbox *) dev->platform_data; - - strncpy((char *) &int_buf, buf, sizeof(int_buf)); - token = (char *) &int_buf; - - /* Parse message */ - val = strsep(&token, " "); - if ((val == NULL) || (strict_strtoul(val, 16, &mbox_mess) != 0)) - mbox_mess = 0xDEADBEEF; - - val = strsep(&token, " "); - if ((val == NULL) || (strict_strtoul(val, 10, &nbr_sends) != 0)) - nbr_sends = 1; - - dev_dbg(dev, "Will write 0x%lX %ld times using data struct at 0x%X\n", - mbox_mess, nbr_sends, (u32) mbox); - - for (i = 0; i < nbr_sends; i++) - mbox_send(mbox, mbox_mess, true); - - return count; -} - -static ssize_t mbox_read_fifo(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - int mbox_value; - struct mbox *mbox = (struct mbox *) dev->platform_data; - - if ((readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7) <= 0) - return sprintf(buf, "Mailbox is empty\n"); - - mbox_value = readl(mbox->virtbase_local + MBOX_FIFO_DATA); - writel(MBOX_LATCH, (mbox->virtbase_local + MBOX_FIFO_REMOVE)); - - return sprintf(buf, "0x%X\n", mbox_value); -} - -static DEVICE_ATTR(fifo, S_IWUGO | S_IRUGO, mbox_read_fifo, mbox_write_fifo); - -static int mbox_show(struct seq_file *s, void *data) -{ - struct list_head *pos; - u8 mbox_index = 0; - - list_for_each(pos, &mboxs) { - struct mbox *m = - (struct mbox *) list_entry(pos, struct mbox, list); - if (m == NULL) { - seq_printf(s, - "Unable to retrieve mailbox %d\n", - mbox_index); - continue; - } - - spin_lock(&m->lock); - if ((m->virtbase_peer == NULL) || (m->virtbase_local == NULL)) { - seq_printf(s, "MAILBOX %d not setup or corrupt\n", - mbox_index); - spin_unlock(&m->lock); - continue; - } - - seq_printf(s, - "===========================\n" - " MAILBOX %d\n" - " PEER MAILBOX DUMP\n" - "---------------------------\n" - "FIFO: 0x%X (%d)\n" - "Free Threshold: 0x%.2X (%d)\n" - "Occupied Threshold: 0x%.2X (%d)\n" - "Status: 0x%.2X (%d)\n" - " Free spaces (ot): %d (%d)\n" - " Occup spaces (ot): %d (%d)\n" - "===========================\n" - " LOCAL MAILBOX DUMP\n" - "---------------------------\n" - "FIFO: 0x%.X (%d)\n" - "Free Threshold: 0x%.2X (%d)\n" - "Occupied Threshold: 0x%.2X (%d)\n" - "Status: 0x%.2X (%d)\n" - " Free spaces (ot): %d (%d)\n" - " Occup spaces (ot): %d (%d)\n" - "===========================\n" - "write_index: %d\n" - "read_index : %d\n" - "===========================\n" - "\n", - mbox_index, - readl(m->virtbase_peer + MBOX_FIFO_DATA), - readl(m->virtbase_peer + MBOX_FIFO_DATA), - readl(m->virtbase_peer + MBOX_FIFO_THRES_FREE), - readl(m->virtbase_peer + MBOX_FIFO_THRES_FREE), - readl(m->virtbase_peer + MBOX_FIFO_THRES_OCCUP), - readl(m->virtbase_peer + MBOX_FIFO_THRES_OCCUP), - readl(m->virtbase_peer + MBOX_FIFO_STATUS), - readl(m->virtbase_peer + MBOX_FIFO_STATUS), - (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 4) & 0x7, - (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 7) & 0x1, - (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 0) & 0x7, - (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 3) & 0x1, - readl(m->virtbase_local + MBOX_FIFO_DATA), - readl(m->virtbase_local + MBOX_FIFO_DATA), - readl(m->virtbase_local + MBOX_FIFO_THRES_FREE), - readl(m->virtbase_local + MBOX_FIFO_THRES_FREE), - readl(m->virtbase_local + MBOX_FIFO_THRES_OCCUP), - readl(m->virtbase_local + MBOX_FIFO_THRES_OCCUP), - readl(m->virtbase_local + MBOX_FIFO_STATUS), - readl(m->virtbase_local + MBOX_FIFO_STATUS), - (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 4) & 0x7, - (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 7) & 0x1, - (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 0) & 0x7, - (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 3) & 0x1, - m->write_index, m->read_index); - mbox_index++; - spin_unlock(&m->lock); - } - - return 0; -} - -static int mbox_open(struct inode *inode, struct file *file) -{ - return single_open(file, mbox_show, NULL); -} - -static const struct file_operations mbox_operations = { - .owner = THIS_MODULE, - .open = mbox_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; -#endif - -static irqreturn_t mbox_irq(int irq, void *arg) -{ - u32 mbox_value; - int nbr_occup; - int nbr_free; - struct mbox *mbox = (struct mbox *) arg; - - spin_lock(&mbox->lock); - - dev_dbg(&(mbox->pdev->dev), - "mbox IRQ [%d] received. ri = %d, wi = %d\n", - irq, mbox->read_index, mbox->write_index); - - /* - * Check if we have any outgoing messages, and if there is space for - * them in the FIFO. - */ - if (mbox->read_index != mbox->write_index) { - /* - * Check by reading FREE for LOCAL since that indicates - * OCCUP for PEER - */ - nbr_free = (readl(mbox->virtbase_local + MBOX_FIFO_STATUS) - >> 4) & 0x7; - dev_dbg(&(mbox->pdev->dev), - "Status indicates %d empty spaces in the FIFO!\n", - nbr_free); - - while ((nbr_free > 0) && - (mbox->read_index != mbox->write_index)) { - /* Write the message and latch it into the FIFO */ - writel(mbox->buffer[mbox->read_index], - (mbox->virtbase_peer + MBOX_FIFO_DATA)); - writel(MBOX_LATCH, - (mbox->virtbase_peer + MBOX_FIFO_ADD)); - dev_dbg(&(mbox->pdev->dev), - "Wrote message 0x%X to addr 0x%X\n", - mbox->buffer[mbox->read_index], - (u32) (mbox->virtbase_peer + MBOX_FIFO_DATA)); - - nbr_free--; - mbox->read_index = - (mbox->read_index + 1) % MBOX_BUF_SIZE; - } - - /* - * Check if we still want IRQ:s when there is free - * space to send - */ - if (mbox->read_index != mbox->write_index) { - dev_dbg(&(mbox->pdev->dev), - "Still have messages to send, but FIFO full. " - "Request IRQ again!\n"); - writel(MBOX_ENABLE_IRQ, - mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); - } else { - dev_dbg(&(mbox->pdev->dev), - "No more messages to send. " - "Do not request IRQ again!\n"); - writel(MBOX_DISABLE_IRQ, - mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); - } - - /* - * Check if we can signal any blocked clients that it is OK to - * start buffering again - */ - if (mbox->client_blocked && - (((mbox->write_index + 1) % MBOX_BUF_SIZE) - != mbox->read_index)) { - dev_dbg(&(mbox->pdev->dev), - "Waking up blocked client\n"); - complete(&mbox->buffer_available); - mbox->client_blocked = 0; - } - } - - /* Check if we have any incoming messages */ - nbr_occup = readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7; - if (nbr_occup == 0) - goto exit; - - if (mbox->cb == NULL) { - dev_dbg(&(mbox->pdev->dev), "No receive callback registered, " - "leaving %d incoming messages in fifo!\n", nbr_occup); - goto exit; - } - - /* Read and acknowledge the message */ - mbox_value = readl(mbox->virtbase_local + MBOX_FIFO_DATA); - writel(MBOX_LATCH, (mbox->virtbase_local + MBOX_FIFO_REMOVE)); - - /* Notify consumer of new mailbox message */ - dev_dbg(&(mbox->pdev->dev), "Calling callback for message 0x%X!\n", - mbox_value); - mbox->cb(mbox_value, mbox->client_data); - -exit: - dev_dbg(&(mbox->pdev->dev), "Exit mbox IRQ. ri = %d, wi = %d\n", - mbox->read_index, mbox->write_index); - spin_unlock(&mbox->lock); - - return IRQ_HANDLED; -} - -/* Setup is executed once for each mbox pair */ -struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv) -{ - struct resource *resource; - int irq; - int res; - struct mbox *mbox; - - mbox = get_mbox_with_id(mbox_id); - if (mbox == NULL) { - dev_err(&(mbox->pdev->dev), "Incorrect mailbox id: %d!\n", - mbox_id); - goto exit; - } - - /* - * Check if mailbox has been allocated to someone else, - * otherwise allocate it - */ - if (mbox->allocated) { - dev_err(&(mbox->pdev->dev), "Mailbox number %d is busy!\n", - mbox_id); - mbox = NULL; - goto exit; - } - mbox->allocated = true; - - dev_dbg(&(mbox->pdev->dev), "Initiating mailbox number %d: 0x%X...\n", - mbox_id, (u32)mbox); - - mbox->client_data = priv; - mbox->cb = mbox_cb; - - /* Get addr for peer mailbox and ioremap it */ - resource = platform_get_resource_byname(mbox->pdev, - IORESOURCE_MEM, - "mbox_peer"); - if (resource == NULL) { - dev_err(&(mbox->pdev->dev), - "Unable to retrieve mbox peer resource\n"); - mbox = NULL; - goto exit; - } - dev_dbg(&(mbox->pdev->dev), - "Resource name: %s start: 0x%X, end: 0x%X\n", - resource->name, resource->start, resource->end); - mbox->virtbase_peer = - ioremap(resource->start, resource->end - resource->start); - if (!mbox->virtbase_peer) { - dev_err(&(mbox->pdev->dev), "Unable to ioremap peer mbox\n"); - mbox = NULL; - goto exit; - } - dev_dbg(&(mbox->pdev->dev), - "ioremapped peer physical: (0x%X-0x%X) to virtual: 0x%X\n", - resource->start, resource->end, (u32) mbox->virtbase_peer); - - /* Get addr for local mailbox and ioremap it */ - resource = platform_get_resource_byname(mbox->pdev, - IORESOURCE_MEM, - "mbox_local"); - if (resource == NULL) { - dev_err(&(mbox->pdev->dev), - "Unable to retrieve mbox local resource\n"); - mbox = NULL; - goto exit; - } - dev_dbg(&(mbox->pdev->dev), - "Resource name: %s start: 0x%X, end: 0x%X\n", - resource->name, resource->start, resource->end); - mbox->virtbase_local = - ioremap(resource->start, resource->end - resource->start); - if (!mbox->virtbase_local) { - dev_err(&(mbox->pdev->dev), "Unable to ioremap local mbox\n"); - mbox = NULL; - goto exit; - } - dev_dbg(&(mbox->pdev->dev), - "ioremapped local physical: (0x%X-0x%X) to virtual: 0x%X\n", - resource->start, resource->end, (u32) mbox->virtbase_peer); - - init_completion(&mbox->buffer_available); - mbox->client_blocked = 0; - - /* Get IRQ for mailbox and allocate it */ - irq = platform_get_irq_byname(mbox->pdev, "mbox_irq"); - if (irq < 0) { - dev_err(&(mbox->pdev->dev), - "Unable to retrieve mbox irq resource\n"); - mbox = NULL; - goto exit; - } - - dev_dbg(&(mbox->pdev->dev), "Allocating irq %d...\n", irq); - res = request_irq(irq, mbox_irq, 0, mbox->name, (void *) mbox); - if (res < 0) { - dev_err(&(mbox->pdev->dev), - "Unable to allocate mbox irq %d\n", irq); - mbox = NULL; - goto exit; - } - - /* Set up mailbox to not launch IRQ on free space in mailbox */ - writel(MBOX_DISABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); - - /* - * Set up mailbox to launch IRQ on new message if we have - * a callback set. If not, do not raise IRQ, but keep message - * in FIFO for manual retrieval - */ - if (mbox_cb != NULL) - writel(MBOX_ENABLE_IRQ, - mbox->virtbase_local + MBOX_FIFO_THRES_OCCUP); - else - writel(MBOX_DISABLE_IRQ, - mbox->virtbase_local + MBOX_FIFO_THRES_OCCUP); - -#if defined(CONFIG_DEBUG_FS) - res = device_create_file(&(mbox->pdev->dev), &dev_attr_fifo); - if (res != 0) - dev_warn(&(mbox->pdev->dev), - "Unable to create mbox sysfs entry"); - - (void) debugfs_create_file("mbox", S_IFREG | S_IRUGO, NULL, - NULL, &mbox_operations); -#endif - - dev_info(&(mbox->pdev->dev), - "Mailbox driver with index %d initated!\n", mbox_id); - -exit: - return mbox; -} -EXPORT_SYMBOL(mbox_setup); - - -int __init mbox_probe(struct platform_device *pdev) -{ - struct mbox local_mbox; - struct mbox *mbox; - int res = 0; - dev_dbg(&(pdev->dev), "Probing mailbox (pdev = 0x%X)...\n", (u32) pdev); - - memset(&local_mbox, 0x0, sizeof(struct mbox)); - - /* Associate our mbox data with the platform device */ - res = platform_device_add_data(pdev, - (void *) &local_mbox, - sizeof(struct mbox)); - if (res != 0) { - dev_err(&(pdev->dev), - "Unable to allocate driver platform data!\n"); - goto exit; - } - - mbox = (struct mbox *) pdev->dev.platform_data; - mbox->pdev = pdev; - mbox->write_index = 0; - mbox->read_index = 0; - - INIT_LIST_HEAD(&(mbox->list)); - list_add_tail(&(mbox->list), &mboxs); - - sprintf(mbox->name, "%s", MBOX_NAME); - spin_lock_init(&mbox->lock); - - dev_info(&(pdev->dev), "Mailbox driver loaded\n"); - -exit: - return res; -} - -static struct platform_driver mbox_driver = { - .driver = { - .name = MBOX_NAME, - .owner = THIS_MODULE, - }, -}; - -static int __init mbox_init(void) -{ - return platform_driver_probe(&mbox_driver, mbox_probe); -} - -module_init(mbox_init); - -void __exit mbox_exit(void) -{ - platform_driver_unregister(&mbox_driver); -} - -module_exit(mbox_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("MBOX driver"); diff --git a/arch/arm/mach-ux500/modem-irq-db5500.c b/arch/arm/mach-ux500/modem-irq-db5500.c new file mode 100644 index 00000000000..3187f887116 --- /dev/null +++ b/arch/arm/mach-ux500/modem-irq-db5500.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Stefan Nilsson for ST-Ericsson. + * Author: Martin Persson for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +#include +#include +#include +#include +#include +#include + +#define MODEM_INTCON_BASE_ADDR 0xBFFD3000 +#define MODEM_INTCON_SIZE 0xFFF + +#define DEST_IRQ41_OFFSET 0x2A4 +#define DEST_IRQ43_OFFSET 0x2AC +#define DEST_IRQ45_OFFSET 0x2B4 + +#define PRIO_IRQ41_OFFSET 0x6A4 +#define PRIO_IRQ43_OFFSET 0x6AC +#define PRIO_IRQ45_OFFSET 0x6B4 + +#define ALLOW_IRQ_OFFSET 0x104 + +#define MODEM_INTCON_CPU_NBR 0x1 +#define MODEM_INTCON_PRIO_HIGH 0x0 + +#define MODEM_INTCON_ALLOW_IRQ41 0x0200 +#define MODEM_INTCON_ALLOW_IRQ43 0x0800 +#define MODEM_INTCON_ALLOW_IRQ45 0x2000 + +#define MODEM_IRQ_REG_OFFSET 0x4 + +struct modem_irq { + void __iomem *modem_intcon_base; +}; + + +static void setup_modem_intcon(void __iomem *modem_intcon_base) +{ + /* IC_DESTINATION_BASE_ARRAY - Which CPU to receive the IRQ */ + writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ41_OFFSET); + writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ43_OFFSET); + writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ45_OFFSET); + + /* IC_PRIORITY_BASE_ARRAY - IRQ priority in modem IRQ controller */ + writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ41_OFFSET); + writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ43_OFFSET); + writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ45_OFFSET); + + /* IC_ALLOW_ARRAY - IRQ enable */ + writel(MODEM_INTCON_ALLOW_IRQ41 | + MODEM_INTCON_ALLOW_IRQ43 | + MODEM_INTCON_ALLOW_IRQ45, + modem_intcon_base + ALLOW_IRQ_OFFSET); +} + +static irqreturn_t modem_cpu_irq_handler(int irq, void *data) +{ + int real_irq; + int virt_irq; + struct modem_irq *mi = (struct modem_irq *)data; + + /* Read modem side IRQ number from modem IRQ controller */ + real_irq = readl(mi->modem_intcon_base + MODEM_IRQ_REG_OFFSET) & 0xFF; + virt_irq = IRQ_MODEM_EVENTS_BASE + real_irq; + + pr_debug("modem_irq: Worker read addr 0x%X and got value 0x%X " + "which will be 0x%X (%d) which translates to " + "virtual IRQ 0x%X (%d)!\n", + (u32)mi->modem_intcon_base + MODEM_IRQ_REG_OFFSET, + real_irq, + real_irq & 0xFF, + real_irq & 0xFF, + virt_irq, + virt_irq); + + if (virt_irq != 0) + generic_handle_irq(virt_irq); + + pr_debug("modem_irq: Done handling virtual IRQ %d!\n", virt_irq); + + return IRQ_HANDLED; +} + +static void create_virtual_irq(int irq, struct irq_chip *modem_irq_chip) +{ + set_irq_chip(irq, modem_irq_chip); + set_irq_handler(irq, handle_simple_irq); + set_irq_flags(irq, IRQF_VALID); + + pr_debug("modem_irq: Created virtual IRQ %d\n", irq); +} + +static int modem_irq_init(void) +{ + int err; + static struct irq_chip modem_irq_chip; + struct modem_irq *mi; + + pr_info("modem_irq: Set up IRQ handler for incoming modem IRQ %d\n", + IRQ_DB5500_MODEM); + + mi = kmalloc(sizeof(struct modem_irq), GFP_KERNEL); + if (!mi) { + pr_err("modem_irq: Could not allocate device\n"); + return -ENOMEM; + } + + mi->modem_intcon_base = + ioremap(MODEM_INTCON_BASE_ADDR, MODEM_INTCON_SIZE); + pr_debug("modem_irq: ioremapped modem_intcon_base from " + "phy 0x%x to virt 0x%x\n", MODEM_INTCON_BASE_ADDR, + (u32)mi->modem_intcon_base); + + setup_modem_intcon(mi->modem_intcon_base); + + modem_irq_chip = dummy_irq_chip; + modem_irq_chip.name = "modem_irq"; + + /* Create the virtual IRQ:s needed */ + create_virtual_irq(MBOX_PAIR0_VIRT_IRQ, &modem_irq_chip); + create_virtual_irq(MBOX_PAIR1_VIRT_IRQ, &modem_irq_chip); + create_virtual_irq(MBOX_PAIR2_VIRT_IRQ, &modem_irq_chip); + + err = request_threaded_irq(IRQ_DB5500_MODEM, NULL, + modem_cpu_irq_handler, IRQF_ONESHOT, + "modem_irq", mi); + if (err) + pr_err("modem_irq: Could not register IRQ %d\n", + IRQ_DB5500_MODEM); + + return 0; +} + +arch_initcall(modem_irq_init); diff --git a/arch/arm/mach-ux500/modem_irq.c b/arch/arm/mach-ux500/modem_irq.c deleted file mode 100644 index 3187f887116..00000000000 --- a/arch/arm/mach-ux500/modem_irq.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * Author: Stefan Nilsson for ST-Ericsson. - * Author: Martin Persson for ST-Ericsson. - * License terms: GNU General Public License (GPL), version 2. - */ - -#include -#include -#include -#include -#include -#include - -#define MODEM_INTCON_BASE_ADDR 0xBFFD3000 -#define MODEM_INTCON_SIZE 0xFFF - -#define DEST_IRQ41_OFFSET 0x2A4 -#define DEST_IRQ43_OFFSET 0x2AC -#define DEST_IRQ45_OFFSET 0x2B4 - -#define PRIO_IRQ41_OFFSET 0x6A4 -#define PRIO_IRQ43_OFFSET 0x6AC -#define PRIO_IRQ45_OFFSET 0x6B4 - -#define ALLOW_IRQ_OFFSET 0x104 - -#define MODEM_INTCON_CPU_NBR 0x1 -#define MODEM_INTCON_PRIO_HIGH 0x0 - -#define MODEM_INTCON_ALLOW_IRQ41 0x0200 -#define MODEM_INTCON_ALLOW_IRQ43 0x0800 -#define MODEM_INTCON_ALLOW_IRQ45 0x2000 - -#define MODEM_IRQ_REG_OFFSET 0x4 - -struct modem_irq { - void __iomem *modem_intcon_base; -}; - - -static void setup_modem_intcon(void __iomem *modem_intcon_base) -{ - /* IC_DESTINATION_BASE_ARRAY - Which CPU to receive the IRQ */ - writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ41_OFFSET); - writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ43_OFFSET); - writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ45_OFFSET); - - /* IC_PRIORITY_BASE_ARRAY - IRQ priority in modem IRQ controller */ - writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ41_OFFSET); - writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ43_OFFSET); - writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ45_OFFSET); - - /* IC_ALLOW_ARRAY - IRQ enable */ - writel(MODEM_INTCON_ALLOW_IRQ41 | - MODEM_INTCON_ALLOW_IRQ43 | - MODEM_INTCON_ALLOW_IRQ45, - modem_intcon_base + ALLOW_IRQ_OFFSET); -} - -static irqreturn_t modem_cpu_irq_handler(int irq, void *data) -{ - int real_irq; - int virt_irq; - struct modem_irq *mi = (struct modem_irq *)data; - - /* Read modem side IRQ number from modem IRQ controller */ - real_irq = readl(mi->modem_intcon_base + MODEM_IRQ_REG_OFFSET) & 0xFF; - virt_irq = IRQ_MODEM_EVENTS_BASE + real_irq; - - pr_debug("modem_irq: Worker read addr 0x%X and got value 0x%X " - "which will be 0x%X (%d) which translates to " - "virtual IRQ 0x%X (%d)!\n", - (u32)mi->modem_intcon_base + MODEM_IRQ_REG_OFFSET, - real_irq, - real_irq & 0xFF, - real_irq & 0xFF, - virt_irq, - virt_irq); - - if (virt_irq != 0) - generic_handle_irq(virt_irq); - - pr_debug("modem_irq: Done handling virtual IRQ %d!\n", virt_irq); - - return IRQ_HANDLED; -} - -static void create_virtual_irq(int irq, struct irq_chip *modem_irq_chip) -{ - set_irq_chip(irq, modem_irq_chip); - set_irq_handler(irq, handle_simple_irq); - set_irq_flags(irq, IRQF_VALID); - - pr_debug("modem_irq: Created virtual IRQ %d\n", irq); -} - -static int modem_irq_init(void) -{ - int err; - static struct irq_chip modem_irq_chip; - struct modem_irq *mi; - - pr_info("modem_irq: Set up IRQ handler for incoming modem IRQ %d\n", - IRQ_DB5500_MODEM); - - mi = kmalloc(sizeof(struct modem_irq), GFP_KERNEL); - if (!mi) { - pr_err("modem_irq: Could not allocate device\n"); - return -ENOMEM; - } - - mi->modem_intcon_base = - ioremap(MODEM_INTCON_BASE_ADDR, MODEM_INTCON_SIZE); - pr_debug("modem_irq: ioremapped modem_intcon_base from " - "phy 0x%x to virt 0x%x\n", MODEM_INTCON_BASE_ADDR, - (u32)mi->modem_intcon_base); - - setup_modem_intcon(mi->modem_intcon_base); - - modem_irq_chip = dummy_irq_chip; - modem_irq_chip.name = "modem_irq"; - - /* Create the virtual IRQ:s needed */ - create_virtual_irq(MBOX_PAIR0_VIRT_IRQ, &modem_irq_chip); - create_virtual_irq(MBOX_PAIR1_VIRT_IRQ, &modem_irq_chip); - create_virtual_irq(MBOX_PAIR2_VIRT_IRQ, &modem_irq_chip); - - err = request_threaded_irq(IRQ_DB5500_MODEM, NULL, - modem_cpu_irq_handler, IRQF_ONESHOT, - "modem_irq", mi); - if (err) - pr_err("modem_irq: Could not register IRQ %d\n", - IRQ_DB5500_MODEM); - - return 0; -} - -arch_initcall(modem_irq_init); diff --git a/drivers/net/caif/caif_shm_u5500.c b/drivers/net/caif/caif_shm_u5500.c index 1cd90da86f1..13fa5353816 100644 --- a/drivers/net/caif/caif_shm_u5500.c +++ b/drivers/net/caif/caif_shm_u5500.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From 09c730a488c32c2cadb31cdb8dcc4df528441197 Mon Sep 17 00:00:00 2001 From: Sundar Iyer Date: Tue, 21 Dec 2010 15:53:31 +0530 Subject: input/tc3589x: add tc3589x keypad support Add support for the keypad controller module found on the TC3589X devices. This driver default adds the support for TC35893 device. Signed-off-by: Sundar Iyer Acked-by: Dmitry Torokhov [Some minor fixups for compilation] Signed-off-by: Linus Walleij --- drivers/input/keyboard/Kconfig | 10 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/tc3589x-keypad.c | 472 ++++++++++++++++++++++++++++++++ drivers/mfd/tc3589x.c | 28 +- include/linux/mfd/tc3589x.h | 52 ++++ 5 files changed, 562 insertions(+), 1 deletion(-) create mode 100644 drivers/input/keyboard/tc3589x-keypad.c (limited to 'drivers') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index b8c51b9781d..85af3c3f7bc 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -443,6 +443,16 @@ config KEYBOARD_OMAP4 To compile this driver as a module, choose M here: the module will be called omap4-keypad. +config KEYBOARD_TC3589X + tristate "TC3589X Keypad support" + depends on MFD_TC3589X + help + Say Y here if you want to use the keypad controller on + TC35892/3 I/O expander. + + To compile this driver as a module, choose M here: the + module will be called tc3589x-keypad. + config KEYBOARD_TNETV107X tristate "TI TNETV107X keypad support" depends on ARCH_DAVINCI_TNETV107X diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index a34452e8ebe..4411c70db3b 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o +obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c new file mode 100644 index 00000000000..69dc0cb20a0 --- /dev/null +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -0,0 +1,472 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Jayeeta Banerjee + * Author: Sundar Iyer + * + * License Terms: GNU General Public License, version 2 + * + * TC35893 MFD Keypad Controller driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Maximum supported keypad matrix row/columns size */ +#define TC3589x_MAX_KPROW 8 +#define TC3589x_MAX_KPCOL 12 + +/* keypad related Constants */ +#define TC3589x_MAX_DEBOUNCE_SETTLE 0xFF +#define DEDICATED_KEY_VAL 0xFF + +/* Pull up/down masks */ +#define TC3589x_NO_PULL_MASK 0x0 +#define TC3589x_PULL_DOWN_MASK 0x1 +#define TC3589x_PULL_UP_MASK 0x2 +#define TC3589x_PULLUP_ALL_MASK 0xAA +#define TC3589x_IO_PULL_VAL(index, mask) ((mask)<<((index)%4)*2)) + +/* Bit masks for IOCFG register */ +#define IOCFG_BALLCFG 0x01 +#define IOCFG_IG 0x08 + +#define KP_EVCODE_COL_MASK 0x0F +#define KP_EVCODE_ROW_MASK 0x70 +#define KP_RELEASE_EVT_MASK 0x80 + +#define KP_ROW_SHIFT 4 + +#define KP_NO_VALID_KEY_MASK 0x7F + +/* bit masks for RESTCTRL register */ +#define TC3589x_KBDRST 0x2 +#define TC3589x_IRQRST 0x10 +#define TC3589x_RESET_ALL 0x1B + +/* KBDMFS register bit mask */ +#define TC3589x_KBDMFS_EN 0x1 + +/* CLKEN register bitmask */ +#define KPD_CLK_EN 0x1 + +/* RSTINTCLR register bit mask */ +#define IRQ_CLEAR 0x1 + +/* bit masks for keyboard interrupts*/ +#define TC3589x_EVT_LOSS_INT 0x8 +#define TC3589x_EVT_INT 0x4 +#define TC3589x_KBD_LOSS_INT 0x2 +#define TC3589x_KBD_INT 0x1 + +/* bit masks for keyboard interrupt clear*/ +#define TC3589x_EVT_INT_CLR 0x2 +#define TC3589x_KBD_INT_CLR 0x1 + +#define TC3589x_KBD_KEYMAP_SIZE 64 + +/** + * struct tc_keypad - data structure used by keypad driver + * @input: pointer to input device object + * @board: keypad platform device + * @krow: number of rows + * @kcol: number of coloumns + * @keymap: matrix scan code table for keycodes + */ +struct tc_keypad { + struct tc3589x *tc3589x; + struct input_dev *input; + const struct tc3589x_keypad_platform_data *board; + unsigned int krow; + unsigned int kcol; + unsigned short keymap[TC3589x_KBD_KEYMAP_SIZE]; + bool keypad_stopped; +}; + +static int __devinit tc3589x_keypad_init_key_hardware(struct tc_keypad *keypad) +{ + int ret; + struct tc3589x *tc3589x = keypad->tc3589x; + u8 settle_time = keypad->board->settle_time; + u8 dbounce_period = keypad->board->debounce_period; + u8 rows = keypad->board->krow & 0xf; /* mask out the nibble */ + u8 column = keypad->board->kcol & 0xf; /* mask out the nibble */ + + /* validate platform configurations */ + if (keypad->board->kcol > TC3589x_MAX_KPCOL || + keypad->board->krow > TC3589x_MAX_KPROW || + keypad->board->debounce_period > TC3589x_MAX_DEBOUNCE_SETTLE || + keypad->board->settle_time > TC3589x_MAX_DEBOUNCE_SETTLE) + return -EINVAL; + + /* configure KBDSIZE 4 LSbits for cols and 4 MSbits for rows */ + ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSIZE, + (rows << KP_ROW_SHIFT) | column); + if (ret < 0) + return ret; + + /* configure dedicated key config, no dedicated key selected */ + ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_LSB, DEDICATED_KEY_VAL); + if (ret < 0) + return ret; + + ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_MSB, DEDICATED_KEY_VAL); + if (ret < 0) + return ret; + + /* Configure settle time */ + ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSETTLE_REG, settle_time); + if (ret < 0) + return ret; + + /* Configure debounce time */ + ret = tc3589x_reg_write(tc3589x, TC3589x_KBDBOUNCE, dbounce_period); + if (ret < 0) + return ret; + + /* Start of initialise keypad GPIOs */ + ret = tc3589x_set_bits(tc3589x, TC3589x_IOCFG, 0x0, IOCFG_IG); + if (ret < 0) + return ret; + + /* Configure pull-up resistors for all row GPIOs */ + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_LSB, + TC3589x_PULLUP_ALL_MASK); + if (ret < 0) + return ret; + + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_MSB, + TC3589x_PULLUP_ALL_MASK); + if (ret < 0) + return ret; + + /* Configure pull-up resistors for all column GPIOs */ + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_LSB, + TC3589x_PULLUP_ALL_MASK); + if (ret < 0) + return ret; + + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_MSB, + TC3589x_PULLUP_ALL_MASK); + if (ret < 0) + return ret; + + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG2_LSB, + TC3589x_PULLUP_ALL_MASK); + + return ret; +} + +#define TC35893_DATA_REGS 4 +#define TC35893_KEYCODE_FIFO_EMPTY 0x7f +#define TC35893_KEYCODE_FIFO_CLEAR 0xff +#define TC35893_KEYPAD_ROW_SHIFT 0x3 + +static irqreturn_t tc3589x_keypad_irq(int irq, void *dev) +{ + struct tc_keypad *keypad = dev; + struct tc3589x *tc3589x = keypad->tc3589x; + u8 i, row_index, col_index, kbd_code, up; + u8 code; + + for (i = 0; i < TC35893_DATA_REGS * 2; i++) { + kbd_code = tc3589x_reg_read(tc3589x, TC3589x_EVTCODE_FIFO); + + /* loop till fifo is empty and no more keys are pressed */ + if (kbd_code == TC35893_KEYCODE_FIFO_EMPTY || + kbd_code == TC35893_KEYCODE_FIFO_CLEAR) + continue; + + /* valid key is found */ + col_index = kbd_code & KP_EVCODE_COL_MASK; + row_index = (kbd_code & KP_EVCODE_ROW_MASK) >> KP_ROW_SHIFT; + code = MATRIX_SCAN_CODE(row_index, col_index, + TC35893_KEYPAD_ROW_SHIFT); + up = kbd_code & KP_RELEASE_EVT_MASK; + + input_event(keypad->input, EV_MSC, MSC_SCAN, code); + input_report_key(keypad->input, keypad->keymap[code], !up); + input_sync(keypad->input); + } + + /* clear IRQ */ + tc3589x_set_bits(tc3589x, TC3589x_KBDIC, + 0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR); + /* enable IRQ */ + tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, + 0x0, TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT); + + return IRQ_HANDLED; +} + +static int tc3589x_keypad_enable(struct tc_keypad *keypad) +{ + struct tc3589x *tc3589x = keypad->tc3589x; + int ret; + + /* pull the keypad module out of reset */ + ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x0); + if (ret < 0) + return ret; + + /* configure KBDMFS */ + ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMFS, 0x0, TC3589x_KBDMFS_EN); + if (ret < 0) + return ret; + + /* enable the keypad clock */ + ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x0, KPD_CLK_EN); + if (ret < 0) + return ret; + + /* clear pending IRQs */ + ret = tc3589x_set_bits(tc3589x, TC3589x_RSTINTCLR, 0x0, 0x1); + if (ret < 0) + return ret; + + /* enable the IRQs */ + ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, 0x0, + TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT); + if (ret < 0) + return ret; + + keypad->keypad_stopped = false; + + return ret; +} + +static int tc3589x_keypad_disable(struct tc_keypad *keypad) +{ + struct tc3589x *tc3589x = keypad->tc3589x; + int ret; + + /* clear IRQ */ + ret = tc3589x_set_bits(tc3589x, TC3589x_KBDIC, + 0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR); + if (ret < 0) + return ret; + + /* disable all interrupts */ + ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, + ~(TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT), 0x0); + if (ret < 0) + return ret; + + /* disable the keypad module */ + ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x1, 0x0); + if (ret < 0) + return ret; + + /* put the keypad module into reset */ + ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x1); + + keypad->keypad_stopped = true; + + return ret; +} + +static int tc3589x_keypad_open(struct input_dev *input) +{ + int error; + struct tc_keypad *keypad = input_get_drvdata(input); + + /* enable the keypad module */ + error = tc3589x_keypad_enable(keypad); + if (error < 0) { + dev_err(&input->dev, "failed to enable keypad module\n"); + return error; + } + + error = tc3589x_keypad_init_key_hardware(keypad); + if (error < 0) { + dev_err(&input->dev, "failed to configure keypad module\n"); + return error; + } + + return 0; +} + +static void tc3589x_keypad_close(struct input_dev *input) +{ + struct tc_keypad *keypad = input_get_drvdata(input); + + /* disable the keypad module */ + tc3589x_keypad_disable(keypad); +} + +static int __devinit tc3589x_keypad_probe(struct platform_device *pdev) +{ + struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent); + struct tc_keypad *keypad; + struct input_dev *input; + const struct tc3589x_keypad_platform_data *plat; + int error, irq; + + plat = tc3589x->pdata->keypad; + if (!plat) { + dev_err(&pdev->dev, "invalid keypad platform data\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + keypad = kzalloc(sizeof(struct tc_keypad), GFP_KERNEL); + input = input_allocate_device(); + if (!keypad || !input) { + dev_err(&pdev->dev, "failed to allocate keypad memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + keypad->board = plat; + keypad->input = input; + keypad->tc3589x = tc3589x; + + input->id.bustype = BUS_I2C; + input->name = pdev->name; + input->dev.parent = &pdev->dev; + + input->keycode = keypad->keymap; + input->keycodesize = sizeof(keypad->keymap[0]); + input->keycodemax = ARRAY_SIZE(keypad->keymap); + + input->open = tc3589x_keypad_open; + input->close = tc3589x_keypad_close; + + input_set_drvdata(input, keypad); + + input_set_capability(input, EV_MSC, MSC_SCAN); + + __set_bit(EV_KEY, input->evbit); + if (!plat->no_autorepeat) + __set_bit(EV_REP, input->evbit); + + matrix_keypad_build_keymap(plat->keymap_data, 0x3, + input->keycode, input->keybit); + + error = request_threaded_irq(irq, NULL, + tc3589x_keypad_irq, plat->irqtype, + "tc3589x-keypad", keypad); + if (error < 0) { + dev_err(&pdev->dev, + "Could not allocate irq %d,error %d\n", + irq, error); + goto err_free_mem; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "Could not register input device\n"); + goto err_free_irq; + } + + /* let platform decide if keypad is a wakeup source or not */ + device_init_wakeup(&pdev->dev, plat->enable_wakeup); + device_set_wakeup_capable(&pdev->dev, plat->enable_wakeup); + + platform_set_drvdata(pdev, keypad); + + return 0; + +err_free_irq: + free_irq(irq, keypad); +err_free_mem: + input_free_device(input); + kfree(keypad); + return error; +} + +static int __devexit tc3589x_keypad_remove(struct platform_device *pdev) +{ + struct tc_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (!keypad->keypad_stopped) + tc3589x_keypad_disable(keypad); + + free_irq(irq, keypad); + + input_unregister_device(keypad->input); + + kfree(keypad); + + return 0; +} + +#ifdef CONFIG_PM +static int tc3589x_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tc_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + /* keypad is already off; we do nothing */ + if (keypad->keypad_stopped) + return 0; + + /* if device is not a wakeup source, disable it for powersave */ + if (!device_may_wakeup(&pdev->dev)) + tc3589x_keypad_disable(keypad); + else + enable_irq_wake(irq); + + return 0; +} + +static int tc3589x_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tc_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (!keypad->keypad_stopped) + return 0; + + /* enable the device to resume normal operations */ + if (!device_may_wakeup(&pdev->dev)) + tc3589x_keypad_enable(keypad); + else + disable_irq_wake(irq); + + return 0; +} + +static const SIMPLE_DEV_PM_OPS(tc3589x_keypad_dev_pm_ops, + tc3589x_keypad_suspend, tc3589x_keypad_resume); +#endif + +static struct platform_driver tc3589x_keypad_driver = { + .driver.name = "tc3589x-keypad", + .driver.owner = THIS_MODULE, +#ifdef CONFIG_PM + .driver.pm = &tc3589x_keypad_dev_pm_ops, +#endif + .probe = tc3589x_keypad_probe, + .remove = __devexit_p(tc3589x_keypad_remove), +}; + +static int __init tc3589x_keypad_init(void) +{ + return platform_driver_register(&tc3589x_keypad_driver); +} +module_init(tc3589x_keypad_init); + +static void __exit tc3589x_keypad_exit(void) +{ + return platform_driver_unregister(&tc3589x_keypad_driver); +} +module_exit(tc3589x_keypad_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Jayeeta Banerjee/Sundar Iyer"); +MODULE_DESCRIPTION("TC35893 Keypad Driver"); +MODULE_ALIAS("platform:tc3589x-keypad") diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index 112efd3c494..729dbeed2ce 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -132,6 +132,14 @@ static struct resource gpio_resources[] = { }, }; +static struct resource keypad_resources[] = { + { + .start = TC3589x_INT_KBDIRQ, + .end = TC3589x_INT_KBDIRQ, + .flags = IORESOURCE_IRQ, + }, +}; + static struct mfd_cell tc3589x_dev_gpio[] = { { .name = "tc3589x-gpio", @@ -140,6 +148,14 @@ static struct mfd_cell tc3589x_dev_gpio[] = { }, }; +static struct mfd_cell tc3589x_dev_keypad[] = { + { + .name = "tc3589x-keypad", + .num_resources = ARRAY_SIZE(keypad_resources), + .resources = &keypad_resources[0], + }, +}; + static irqreturn_t tc3589x_irq(int irq, void *data) { struct tc3589x *tc3589x = data; @@ -255,8 +271,18 @@ static int __devinit tc3589x_device_init(struct tc3589x *tc3589x) dev_info(tc3589x->dev, "added gpio block\n"); } - return ret; + if (blocks & TC3589x_BLOCK_KEYPAD) { + ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_keypad, + ARRAY_SIZE(tc3589x_dev_keypad), NULL, + tc3589x->irq_base); + if (ret) { + dev_err(tc3589x->dev, "failed to keypad child\n"); + return ret; + } + dev_info(tc3589x->dev, "added keypad block\n"); + } + return ret; } static int __devinit tc3589x_probe(struct i2c_client *i2c, diff --git a/include/linux/mfd/tc3589x.h b/include/linux/mfd/tc3589x.h index da00958b12d..16c76e124f9 100644 --- a/include/linux/mfd/tc3589x.h +++ b/include/linux/mfd/tc3589x.h @@ -20,6 +20,17 @@ enum tx3589x_block { #define TC3589x_RSTCTRL_KBDRST (1 << 1) #define TC3589x_RSTCTRL_GPIRST (1 << 0) +/* Keyboard Configuration Registers */ +#define TC3589x_KBDSETTLE_REG 0x01 +#define TC3589x_KBDBOUNCE 0x02 +#define TC3589x_KBDSIZE 0x03 +#define TC3589x_KBCFG_LSB 0x04 +#define TC3589x_KBCFG_MSB 0x05 +#define TC3589x_KBDIC 0x08 +#define TC3589x_KBDMSK 0x09 +#define TC3589x_EVTCODE_FIFO 0x10 +#define TC3589x_KBDMFS 0x8F + #define TC3589x_IRQST 0x91 #define TC3589x_MANFCODE_MAGIC 0x03 @@ -35,6 +46,14 @@ enum tx3589x_block { #define TC3589x_EXTRSTN 0x83 #define TC3589x_RSTINTCLR 0x84 +/* Pull up/down configuration registers */ +#define TC3589x_IOCFG 0xA7 +#define TC3589x_IOPULLCFG0_LSB 0xAA +#define TC3589x_IOPULLCFG0_MSB 0xAB +#define TC3589x_IOPULLCFG1_LSB 0xAC +#define TC3589x_IOPULLCFG1_MSB 0xAD +#define TC3589x_IOPULLCFG2_LSB 0xAE + #define TC3589x_GPIOIS0 0xC9 #define TC3589x_GPIOIS1 0xCA #define TC3589x_GPIOIS2 0xCB @@ -112,6 +131,37 @@ extern int tc3589x_block_write(struct tc3589x *tc3589x, u8 reg, u8 length, const u8 *values); extern int tc3589x_set_bits(struct tc3589x *tc3589x, u8 reg, u8 mask, u8 val); +/* + * Keypad related platform specific constants + * These values may be modified for fine tuning + */ +#define TC_KPD_ROWS 0x8 +#define TC_KPD_COLUMNS 0x8 +#define TC_KPD_DEBOUNCE_PERIOD 0xA3 +#define TC_KPD_SETTLE_TIME 0xA3 + +/** + * struct tc35893_platform_data - data structure for platform specific data + * @keymap_data: matrix scan code table for keycodes + * @krow: mask for available rows, value is 0xFF + * @kcol: mask for available columns, value is 0xFF + * @debounce_period: platform specific debounce time + * @settle_time: platform specific settle down time + * @irqtype: type of interrupt, falling or rising edge + * @enable_wakeup: specifies if keypad event can wake up system from sleep + * @no_autorepeat: flag for auto repetition + */ +struct tc3589x_keypad_platform_data { + const struct matrix_keymap_data *keymap_data; + u8 krow; + u8 kcol; + u8 debounce_period; + u8 settle_time; + unsigned long irqtype; + bool enable_wakeup; + bool no_autorepeat; +}; + /** * struct tc3589x_gpio_platform_data - TC3589x GPIO platform data * @gpio_base: first gpio number assigned to TC3589x. A maximum of @@ -130,11 +180,13 @@ struct tc3589x_gpio_platform_data { * @block: bitmask of blocks to enable (use TC3589x_BLOCK_*) * @irq_base: base IRQ number. %TC3589x_NR_IRQS irqs will be used. * @gpio: GPIO-specific platform data + * @keypad: keypad-specific platform data */ struct tc3589x_platform_data { unsigned int block; int irq_base; struct tc3589x_gpio_platform_data *gpio; + const struct tc3589x_keypad_platform_data *keypad; }; #define TC3589x_NR_GPIOS 24 -- cgit v1.2.3-70-g09d2 From 711669e5b80b6f2d88f61ed8a9681f83d8cbd201 Mon Sep 17 00:00:00 2001 From: "Arnaud Patard (Rtp)" Date: Mon, 20 Dec 2010 16:48:58 +0100 Subject: mx51: fix usb clock support Current code doesn't really enable the usb clocks so if they're disabled when booting linux, the kernel/machine will hang as soon as someone is trying to read a usb register Signed-off-by: Arnaud Patard Signed-off-by: Sascha Hauer --- arch/arm/mach-mx5/clock-mx51-mx53.c | 45 +++++++++++++++++++++++++++++++++++-- drivers/usb/host/ehci-mxc.c | 44 ++++++++++++++++++++++++++---------- 2 files changed, 75 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-mx5/clock-mx51-mx53.c b/arch/arm/mach-mx5/clock-mx51-mx53.c index 9fc65bbc9d7..2f9eae21309 100644 --- a/arch/arm/mach-mx5/clock-mx51-mx53.c +++ b/arch/arm/mach-mx5/clock-mx51-mx53.c @@ -954,6 +954,41 @@ static struct clk usboh3_clk = { .parent = &pll2_sw_clk, .get_rate = clk_usboh3_get_rate, .set_parent = clk_usboh3_set_parent, + .enable = _clk_ccgr_enable, + .disable = _clk_ccgr_disable, + .enable_reg = MXC_CCM_CCGR2, + .enable_shift = MXC_CCM_CCGRx_CG14_OFFSET, +}; + +static struct clk usb_ahb_clk = { + .parent = &ipg_clk, + .enable = _clk_ccgr_enable, + .disable = _clk_ccgr_disable, + .enable_reg = MXC_CCM_CCGR2, + .enable_shift = MXC_CCM_CCGRx_CG13_OFFSET, +}; + +static int clk_usb_phy1_set_parent(struct clk *clk, struct clk *parent) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_USB_PHY_CLK_SEL; + + if (parent == &pll3_sw_clk) + reg |= 1 << MXC_CCM_CSCMR1_USB_PHY_CLK_SEL_OFFSET; + + __raw_writel(reg, MXC_CCM_CSCMR1); + + return 0; +} + +static struct clk usb_phy1_clk = { + .parent = &pll3_sw_clk, + .set_parent = clk_usb_phy1_set_parent, + .enable = _clk_ccgr_enable, + .enable_reg = MXC_CCM_CCGR2, + .enable_shift = MXC_CCM_CCGRx_CG0_OFFSET, + .disable = _clk_ccgr_disable, }; /* eCSPI */ @@ -1094,9 +1129,12 @@ static struct clk_lookup mx51_lookups[] = { _REGISTER_CLOCK("imx-i2c.1", NULL, i2c2_clk) _REGISTER_CLOCK("imx-i2c.2", NULL, hsi2c_clk) _REGISTER_CLOCK("mxc-ehci.0", "usb", usboh3_clk) - _REGISTER_CLOCK("mxc-ehci.0", "usb_ahb", ahb_clk) + _REGISTER_CLOCK("mxc-ehci.0", "usb_ahb", usb_ahb_clk) + _REGISTER_CLOCK("mxc-ehci.0", "usb_phy1", usb_phy1_clk) _REGISTER_CLOCK("mxc-ehci.1", "usb", usboh3_clk) - _REGISTER_CLOCK("mxc-ehci.1", "usb_ahb", ahb_clk) + _REGISTER_CLOCK("mxc-ehci.1", "usb_ahb", usb_ahb_clk) + _REGISTER_CLOCK("mxc-ehci.2", "usb", usboh3_clk) + _REGISTER_CLOCK("mxc-ehci.2", "usb_ahb", usb_ahb_clk) _REGISTER_CLOCK("fsl-usb2-udc", "usb", usboh3_clk) _REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", ahb_clk) _REGISTER_CLOCK("imx-keypad.0", NULL, kpp_clk) @@ -1170,6 +1208,9 @@ int __init mx51_clocks_init(unsigned long ckil, unsigned long osc, mx51_revision(); clk_disable(&iim_clk); + /* move usb_phy_clk to 24MHz */ + clk_set_parent(&usb_phy1_clk, &osc_clk); + /* set the usboh3_clk parent to pll2_sw_clk */ clk_set_parent(&usboh3_clk, &pll2_sw_clk); diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index bce85055019..a22d2df769a 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -28,7 +28,7 @@ #define ULPI_VIEWPORT_OFFSET 0x170 struct ehci_mxc_priv { - struct clk *usbclk, *ahbclk; + struct clk *usbclk, *ahbclk, *phy1clk; struct usb_hcd *hcd; }; @@ -168,17 +168,6 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) goto err_ioremap; } - /* call platform specific init function */ - if (pdata->init) { - ret = pdata->init(pdev); - if (ret) { - dev_err(dev, "platform init failed\n"); - goto err_init; - } - /* platforms need some time to settle changed IO settings */ - mdelay(10); - } - /* enable clocks */ priv->usbclk = clk_get(dev, "usb"); if (IS_ERR(priv->usbclk)) { @@ -196,6 +185,28 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) clk_enable(priv->ahbclk); } + /* "dr" device has its own clock */ + if (pdev->id == 0) { + priv->phy1clk = clk_get(dev, "usb_phy1"); + if (IS_ERR(priv->phy1clk)) { + ret = PTR_ERR(priv->phy1clk); + goto err_clk_phy; + } + clk_enable(priv->phy1clk); + } + + + /* call platform specific init function */ + if (pdata->init) { + ret = pdata->init(pdev); + if (ret) { + dev_err(dev, "platform init failed\n"); + goto err_init; + } + /* platforms need some time to settle changed IO settings */ + mdelay(10); + } + /* setup specific usb hw */ ret = mxc_initialize_usb_hw(pdev->id, pdata->flags); if (ret < 0) @@ -230,6 +241,11 @@ err_add: if (pdata && pdata->exit) pdata->exit(pdev); err_init: + if (priv->phy1clk) { + clk_disable(priv->phy1clk); + clk_put(priv->phy1clk); + } +err_clk_phy: if (priv->ahbclk) { clk_disable(priv->ahbclk); clk_put(priv->ahbclk); @@ -273,6 +289,10 @@ static int __exit ehci_mxc_drv_remove(struct platform_device *pdev) clk_disable(priv->ahbclk); clk_put(priv->ahbclk); } + if (priv->phy1clk) { + clk_disable(priv->phy1clk); + clk_put(priv->phy1clk); + } kfree(priv); -- cgit v1.2.3-70-g09d2