From 379749c46320c82df95c7909ec888da8ed1fb22c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 14 Jan 2013 13:26:15 +0000 Subject: mfd: ab8500-sysctrl: Provide a platform specific pm_power_off() call-back The kernel allows us to specify a function call-back which will be invoked when a system power-off request has been received. Here we provide one which is to be used when shutting down AB8500 based platforms. Signed-off-by: Lee Jones --- include/linux/mfd/abx500/ab8500.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 1cb5698b4d76..6119b2fbad97 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -274,6 +274,7 @@ struct ab8500_codec_platform_data; /** * struct ab8500_platform_data - AB8500 platform data * @irq_base: start of AB8500 IRQs, AB8500_NR_IRQS will be used + * @pm_power_off: Should machine pm power off hook be registered or not * @init: board-specific initialization after detection of ab8500 * @num_regulator_reg_init: number of regulator init registers * @regulator_reg_init: regulator init registers @@ -282,6 +283,7 @@ struct ab8500_codec_platform_data; */ struct ab8500_platform_data { int irq_base; + bool pm_power_off; void (*init) (struct ab8500 *); int num_regulator_reg_init; struct ab8500_regulator_reg_init *regulator_reg_init; -- cgit v1.2.3-58-ga151 From 1abf063ffd18e6934bb81a776b3d71278eeff4ab Mon Sep 17 00:00:00 2001 From: Kennet Wallden Date: Tue, 27 Sep 2011 09:23:56 +0200 Subject: mfd: ab8500-sysctrl: Provide configuration for SysClkReqRfClkBuf registers Add the possibility to pass configuration settings for SysCl1kReqRfClkBuf to SysClk8ReqRfClkBuf via platform data. Signed-off-by: Lee Jones Signed-off-by: Kennet Wallden Reviewed-by: Karl-Johan PERNTZ Reviewed-by: Bengt JONSSON --- drivers/mfd/ab8500-sysctrl.c | 24 ++++++++++++++++++++++++ include/linux/mfd/abx500/ab8500-sysctrl.h | 5 +++++ include/linux/mfd/abx500/ab8500.h | 2 ++ 3 files changed, 31 insertions(+) (limited to 'include') diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index 3aaff6043e18..236324e1136d 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -113,11 +113,35 @@ int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value) static int ab8500_sysctrl_probe(struct platform_device *pdev) { struct ab8500_platform_data *plat; + struct ab8500_sysctrl_platform_data *pdata; sysctrl_dev = &pdev->dev; plat = dev_get_platdata(pdev->dev.parent); if (plat->pm_power_off) pm_power_off = ab8500_power_off; + + pdata = plat->sysctrl; + + if (pdata) { + int ret, i, j; + + for (i = AB8500_SYSCLKREQ1RFCLKBUF; + i <= AB8500_SYSCLKREQ8RFCLKBUF; i++) { + j = i - AB8500_SYSCLKREQ1RFCLKBUF; + ret = ab8500_sysctrl_write(i, 0xff, + pdata->initial_req_buf_config[j]); + dev_dbg(&pdev->dev, + "Setting SysClkReq%dRfClkBuf 0x%X\n", + j + 1, + pdata->initial_req_buf_config[j]); + if (ret < 0) { + dev_err(&pdev->dev, + "unable to set sysClkReq%dRfClkBuf: " + "%d\n", j + 1, ret); + } + } + } + return 0; } diff --git a/include/linux/mfd/abx500/ab8500-sysctrl.h b/include/linux/mfd/abx500/ab8500-sysctrl.h index 10eb50973c39..ebf12e793db9 100644 --- a/include/linux/mfd/abx500/ab8500-sysctrl.h +++ b/include/linux/mfd/abx500/ab8500-sysctrl.h @@ -37,6 +37,11 @@ static inline int ab8500_sysctrl_clear(u16 reg, u8 bits) return ab8500_sysctrl_write(reg, bits, 0); } +/* Configuration data for SysClkReq1RfClkBuf - SysClkReq8RfClkBuf */ +struct ab8500_sysctrl_platform_data { + u8 initial_req_buf_config[8]; +}; + /* Registers */ #define AB8500_TURNONSTATUS 0x100 #define AB8500_RESETSTATUS 0x101 diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 6119b2fbad97..5251bca56326 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -270,6 +270,7 @@ struct regulator_reg_init; struct regulator_init_data; struct ab8500_gpio_platform_data; struct ab8500_codec_platform_data; +struct ab8500_sysctrl_platform_data; /** * struct ab8500_platform_data - AB8500 platform data @@ -291,6 +292,7 @@ struct ab8500_platform_data { struct regulator_init_data *regulator; struct ab8500_gpio_platform_data *gpio; struct ab8500_codec_platform_data *codec; + struct ab8500_sysctrl_platform_data *sysctrl; }; extern int ab8500_init(struct ab8500 *ab8500, -- cgit v1.2.3-58-ga151 From e0f4fec030ce412666cc127702adbf0a6cfa0855 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 27 Jan 2012 11:22:16 +0100 Subject: mfd: abx500-core: Provide an API to dump all ABx500 registers Some drivers can detect subsystem failures e.g. shared memory driver can detect modem sub system failures. It would be helpful in analyzing these failures if AB register dump is available at that point. This patch adds the API for the drivers to dump AB registers in the kernel log. Signed-off-by: Lee Jones Signed-off-by: Mian Yousaf Kaukab Reviewed-by: Linus WALLEIJ Reviewed-by: Jonas ABERG --- drivers/mfd/abx500-core.c | 16 ++++++++++++++++ include/linux/mfd/abx500.h | 2 ++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/drivers/mfd/abx500-core.c b/drivers/mfd/abx500-core.c index 7ce65f49480f..9818afba2515 100644 --- a/drivers/mfd/abx500-core.c +++ b/drivers/mfd/abx500-core.c @@ -153,6 +153,22 @@ int abx500_startup_irq_enabled(struct device *dev, unsigned int irq) } EXPORT_SYMBOL(abx500_startup_irq_enabled); +void abx500_dump_all_banks(void) +{ + struct abx500_ops *ops; + struct device dummy_child = {0}; + struct abx500_device_entry *dev_entry; + + list_for_each_entry(dev_entry, &abx500_list, list) { + dummy_child.parent = dev_entry->dev; + ops = &dev_entry->ops; + + if ((ops != NULL) && (ops->dump_all_banks != NULL)) + ops->dump_all_banks(&dummy_child); + } +} +EXPORT_SYMBOL(abx500_dump_all_banks); + MODULE_AUTHOR("Mattias Wallin "); MODULE_DESCRIPTION("ABX500 core driver"); MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 2138bd33021a..bd480b248c62 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -308,6 +308,7 @@ int abx500_mask_and_set_register_interruptible(struct device *dev, u8 bank, int abx500_get_chip_id(struct device *dev); int abx500_event_registers_startup_state_get(struct device *dev, u8 *event); int abx500_startup_irq_enabled(struct device *dev, unsigned int irq); +void abx500_dump_all_banks(void); struct abx500_ops { int (*get_chip_id) (struct device *); @@ -318,6 +319,7 @@ struct abx500_ops { int (*mask_and_set_register) (struct device *, u8, u8, u8, u8); int (*event_registers_startup_state_get) (struct device *, u8 *); int (*startup_irq_enabled) (struct device *, unsigned int); + void (*dump_all_banks) (struct device *); }; int abx500_register_ops(struct device *core_dev, struct abx500_ops *ops); -- cgit v1.2.3-58-ga151 From 1d843a6c8c2067615fea0ff8cb62d4a5c4a6f8ae Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 27 Jan 2012 11:35:41 +0100 Subject: mfd: ab8500-core: Allow the possibility to dump all AB8500 registers Implement an API so that a user may dump all AB8500 registers via debugfs file access. Signed-off-by: Lee Jones Signed-off-by: Mian Yousaf Kaukab Reviewed-by: Linus WALLEIJ Reviewed-by: Jonas ABERG --- drivers/mfd/ab8500-core.c | 1 + drivers/mfd/ab8500-debugfs.c | 13 +++++++++++++ include/linux/mfd/abx500/ab8500.h | 6 ++++++ 3 files changed, 20 insertions(+) (limited to 'include') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index e1650badd106..d228697f5d9b 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -319,6 +319,7 @@ static struct abx500_ops ab8500_ops = { .mask_and_set_register = ab8500_mask_and_set_register, .event_registers_startup_state_get = NULL, .startup_irq_enabled = NULL, + .dump_all_banks = ab8500_dump_all_banks, }; static void ab8500_irq_lock(struct irq_data *data) diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index cd3cee814fb7..af6f774e658a 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -581,6 +581,19 @@ static int ab8500_print_all_banks(struct seq_file *s, void *p) return 0; } +/* Dump registers to kernel log */ +void ab8500_dump_all_banks(struct device *dev) +{ + unsigned int i; + + printk(KERN_INFO"ab8500 register values:\n"); + + for (i = 1; i < AB8500_NUM_BANKS; i++) { + printk(KERN_INFO" bank %u:\n", i); + ab8500_registers_print(dev, i, NULL); + } +} + static int ab8500_all_banks_open(struct inode *inode, struct file *file) { struct seq_file *s; diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 5251bca56326..dbec7b00ea83 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -345,4 +345,10 @@ static inline int is_ab8500_2p0(struct ab8500 *ab) return (is_ab8500(ab) && (ab->chip_id == AB8500_CUT2P0)); } +#ifdef CONFIG_AB8500_DEBUG +void ab8500_dump_all_banks(struct device *dev); +#else +static inline void ab8500_dump_all_banks(struct device *dev) {} +#endif + #endif /* MFD_AB8500_H */ -- cgit v1.2.3-58-ga151 From 8f0eb43be5f461a28341fe724686f265b0719dd3 Mon Sep 17 00:00:00 2001 From: Bengt Jonsson Date: Tue, 14 Feb 2012 13:01:00 +0100 Subject: mfd: ab8500-debugfs: Add interrupt debug This patch adds an entry in debugfs to check number of interrupts from the AB. Signed-off-by: Lee Jones Signed-off-by: Bengt Jonsson Reviewed-by: Rabin VINCENT --- drivers/mfd/ab8500-core.c | 1 + drivers/mfd/ab8500-debugfs.c | 49 +++++++++++++++++++++++++++++++++++++++ include/linux/mfd/abx500/ab8500.h | 2 ++ 3 files changed, 52 insertions(+) (limited to 'include') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index d228697f5d9b..05a7af4b9768 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -521,6 +521,7 @@ static irqreturn_t ab8500_irq(int irq, void *dev) int virq = ab8500_irq_get_virq(ab8500, line); handle_nested_irq(virq); + ab8500_debug_register_interrupt(line); value &= ~(1 << bit); } while (value); diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 8617b132e730..cbebbd7e7b40 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -736,6 +736,35 @@ static ssize_t ab8500_val_write(struct file *file, return count; } +/* + * Interrupt status + */ +static u32 num_interrupts[AB8500_MAX_NR_IRQS]; +static int num_interrupt_lines; + +void ab8500_debug_register_interrupt(int line) +{ + if (line < num_interrupt_lines) + num_interrupts[line]++; +} + +static int ab8500_interrupts_print(struct seq_file *s, void *p) +{ + int line; + + seq_printf(s, "irq: number of\n"); + + for (line = 0; line < num_interrupt_lines; line++) + seq_printf(s, "%3i: %6i\n", line, num_interrupts[line]); + + return 0; +} + +static int ab8500_interrupts_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab8500_interrupts_print, inode->i_private); +} + /* * - HWREG DB8500 formated routines */ @@ -1489,6 +1518,14 @@ static const struct file_operations ab8500_val_fops = { .owner = THIS_MODULE, }; +static const struct file_operations ab8500_interrupts_fops = { + .open = ab8500_interrupts_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + static const struct file_operations ab8500_subscribe_fops = { .open = ab8500_subscribe_unsubscribe_open, .write = ab8500_subscribe_write, @@ -1597,6 +1634,18 @@ static int ab8500_debug_probe(struct platform_device *plf) if (!file) goto err; + if (is_ab8500(ab8500)) + num_interrupt_lines = AB8500_NR_IRQS; + else if (is_ab8505(ab8500)) + num_interrupt_lines = AB8505_NR_IRQS; + else if (is_ab9540(ab8500)) + num_interrupt_lines = AB9540_NR_IRQS; + + file = debugfs_create_file("interrupts", (S_IRUGO), + ab8500_dir, &plf->dev, &ab8500_interrupts_fops); + if (!file) + goto err; + file = debugfs_create_file("irq-unsubscribe", (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_unsubscribe_fops); if (!file) diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index dbec7b00ea83..fa7173dd71f2 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -347,8 +347,10 @@ static inline int is_ab8500_2p0(struct ab8500 *ab) #ifdef CONFIG_AB8500_DEBUG void ab8500_dump_all_banks(struct device *dev); +void ab8500_debug_register_interrupt(int line); #else static inline void ab8500_dump_all_banks(struct device *dev) {} +static inline void ab8500_debug_register_interrupt(int line) {} #endif #endif /* MFD_AB8500_H */ -- cgit v1.2.3-58-ga151 From ccac71a7f063ad31eb99fac37e95b70ff57f1354 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Nov 2012 19:18:08 +0200 Subject: mfd: omap-usb-host: override number of ports from platform data Both OMAP4 and 5 exhibit the same revision ID in the REVISION register but they have different number of ports i.e. 2 and 3 respectively. So we can't rely on REVISION register for number of ports on OMAP5 and depend on platform data (or device tree) instead. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 34 +++++++++++++++++++++------------- include/linux/platform_data/usb-omap.h | 1 + 2 files changed, 22 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 26319ca72e38..779588be8ab2 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -493,19 +493,27 @@ static int usbhs_omap_probe(struct platform_device *pdev) */ pm_runtime_put_sync(dev); - switch (omap->usbhs_rev) { - case OMAP_USBHS_REV1: - omap->nports = 3; - break; - case OMAP_USBHS_REV2: - omap->nports = 2; - break; - default: - omap->nports = OMAP3_HS_USB_PORTS; - dev_dbg(dev, - "USB HOST Rev : 0x%d not recognized, assuming %d ports\n", - omap->usbhs_rev, omap->nports); - break; + /* + * If platform data contains nports then use that + * else make out number of ports from USBHS revision + */ + if (pdata->nports) { + omap->nports = pdata->nports; + } else { + switch (omap->usbhs_rev) { + case OMAP_USBHS_REV1: + omap->nports = 3; + break; + case OMAP_USBHS_REV2: + omap->nports = 2; + break; + default: + omap->nports = OMAP3_HS_USB_PORTS; + dev_dbg(dev, + "USB HOST Rev:0x%d not recognized, assuming %d ports\n", + omap->usbhs_rev, omap->nports); + break; + } } for (i = 0; i < omap->nports; i++) diff --git a/include/linux/platform_data/usb-omap.h b/include/linux/platform_data/usb-omap.h index e697c85ad3bc..fa579b4c666b 100644 --- a/include/linux/platform_data/usb-omap.h +++ b/include/linux/platform_data/usb-omap.h @@ -55,6 +55,7 @@ struct ohci_hcd_omap_platform_data { }; struct usbhs_omap_platform_data { + int nports; enum usbhs_omap_port_mode port_mode[OMAP3_HS_USB_PORTS]; int reset_gpio_port[OMAP3_HS_USB_PORTS]; struct regulator *regulator[OMAP3_HS_USB_PORTS]; -- cgit v1.2.3-58-ga151 From 60c185f059c88ad4b9b170b1f9322e3adcccca07 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 3 Jan 2013 16:16:58 +0530 Subject: mfd: palmas: Add APIs to access the Palmas' registers Palmas register set is divided into different blocks (base and offset) and hence different i2c addresses. The i2c address offsets are derived from base address of block of registers. Add inline APIs to access the Palma's registers which takes the base of register block and register offset. The i2c address offset is derived from the base address of register blocks. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- include/linux/mfd/palmas.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'include') diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 29f6616e12f0..a4d13d7cd001 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -2789,4 +2789,56 @@ enum usb_irq_events { #define PALMAS_GPADC_TRIM15 0xE #define PALMAS_GPADC_TRIM16 0xF +static inline int palmas_read(struct palmas *palmas, unsigned int base, + unsigned int reg, unsigned int *val) +{ + unsigned int addr = PALMAS_BASE_TO_REG(base, reg); + int slave_id = PALMAS_BASE_TO_SLAVE(base); + + return regmap_read(palmas->regmap[slave_id], addr, val); +} + +static inline int palmas_write(struct palmas *palmas, unsigned int base, + unsigned int reg, unsigned int value) +{ + unsigned int addr = PALMAS_BASE_TO_REG(base, reg); + int slave_id = PALMAS_BASE_TO_SLAVE(base); + + return regmap_write(palmas->regmap[slave_id], addr, value); +} + +static inline int palmas_bulk_write(struct palmas *palmas, unsigned int base, + unsigned int reg, const void *val, size_t val_count) +{ + unsigned int addr = PALMAS_BASE_TO_REG(base, reg); + int slave_id = PALMAS_BASE_TO_SLAVE(base); + + return regmap_bulk_write(palmas->regmap[slave_id], addr, + val, val_count); +} + +static inline int palmas_bulk_read(struct palmas *palmas, unsigned int base, + unsigned int reg, void *val, size_t val_count) +{ + unsigned int addr = PALMAS_BASE_TO_REG(base, reg); + int slave_id = PALMAS_BASE_TO_SLAVE(base); + + return regmap_bulk_read(palmas->regmap[slave_id], addr, + val, val_count); +} + +static inline int palmas_update_bits(struct palmas *palmas, unsigned int base, + unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int addr = PALMAS_BASE_TO_REG(base, reg); + int slave_id = PALMAS_BASE_TO_SLAVE(base); + + return regmap_update_bits(palmas->regmap[slave_id], addr, mask, val); +} + +static inline int palmas_irq_get_virq(struct palmas *palmas, int irq) +{ + return regmap_irq_get_virq(palmas->irq_data, irq); +} + #endif /* __LINUX_MFD_PALMAS_H */ -- cgit v1.2.3-58-ga151 From 3d91f8282c66d9edafa3980385324ce6a48edcda Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 29 Jan 2013 00:47:37 +0800 Subject: mfd: arizona: Provide platform data for MICBIAS configuration Allow the MICBIAS voltages and other attributes to be configured by the platform. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/arizona-core.c | 23 +++++++++++++++++++++++ include/linux/mfd/arizona/pdata.h | 12 ++++++++++++ 2 files changed, 35 insertions(+) (limited to 'include') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 317ba0a4ea25..b562c7bf8a46 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -510,6 +510,29 @@ int arizona_dev_init(struct arizona *arizona) goto err_reset; } + for (i = 0; i < ARIZONA_MAX_MICBIAS; i++) { + if (!arizona->pdata.micbias[i].mV) + continue; + + val = (arizona->pdata.micbias[i].mV - 1500) / 100; + val <<= ARIZONA_MICB1_LVL_SHIFT; + + if (arizona->pdata.micbias[i].ext_cap) + val |= ARIZONA_MICB1_EXT_CAP; + + if (arizona->pdata.micbias[i].discharge) + val |= ARIZONA_MICB1_DISCH; + + if (arizona->pdata.micbias[i].fast_start) + val |= ARIZONA_MICB1_RATE; + + regmap_update_bits(arizona->regmap, + ARIZONA_MIC_BIAS_CTRL_1 + i, + ARIZONA_MICB1_LVL_MASK | + ARIZONA_MICB1_DISCH | + ARIZONA_MICB1_RATE, val); + } + for (i = 0; i < ARIZONA_MAX_INPUT; i++) { /* Default for both is 0 so noop with defaults */ val = arizona->pdata.dmic_ref[i] diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 8b1d1daaae16..37894d6a4f6f 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -56,6 +56,8 @@ #define ARIZONA_DMIC_MICBIAS2 2 #define ARIZONA_DMIC_MICBIAS3 3 +#define ARIZONA_MAX_MICBIAS 3 + #define ARIZONA_INMODE_DIFF 0 #define ARIZONA_INMODE_SE 1 #define ARIZONA_INMODE_DMIC 2 @@ -69,6 +71,13 @@ struct regulator_init_data; +struct arizona_micbias { + int mV; /** Regulated voltage */ + unsigned int ext_cap:1; /** External capacitor fitted */ + unsigned int discharge:1; /** Actively discharge */ + unsigned int fast_start:1; /** Enable aggressive startup ramp rate */ +}; + struct arizona_micd_config { unsigned int src; unsigned int bias; @@ -106,6 +115,9 @@ struct arizona_pdata { /** Reference voltage for DMIC inputs */ int dmic_ref[ARIZONA_MAX_INPUT]; + /** MICBIAS configurations */ + struct arizona_micbias micbias[ARIZONA_MAX_MICBIAS]; + /** Mode of input structures */ int inmode[ARIZONA_MAX_INPUT]; -- cgit v1.2.3-58-ga151 From 5d4e9bd79a5ab5bd4695d3becaa71da447a76a94 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:50 +0100 Subject: mfd: twl-core: Clean up module id lookup and definitions Use enums for all module definitions: twl_module_ids for common functionality among twl4030/twl6030 twl4030_module_ids for twl4030 specific ids twl6030_module_ids for twl6030 specific ids In this way the list can be managed easier when new functionality going to be implemented. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 105 +++++++++++++++++++++++------------------------- include/linux/i2c/twl.h | 66 ++++++++++++++++-------------- 2 files changed, 86 insertions(+), 85 deletions(-) (limited to 'include') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 4f3baadd0038..b781cdd0629d 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -74,8 +74,6 @@ #define SUB_CHIP_ID3 3 #define SUB_CHIP_ID_INVAL 0xff -#define TWL_MODULE_LAST TWL4030_MODULE_LAST - /* Base Address defns for twl4030_map[] */ /* subchip/slave 0 - USB ID */ @@ -94,10 +92,7 @@ #define TWL4030_BASEADD_MADC 0x0000 #define TWL4030_BASEADD_MAIN_CHARGE 0x0074 #define TWL4030_BASEADD_PRECHARGE 0x00AA -#define TWL4030_BASEADD_PWM0 0x00F8 -#define TWL4030_BASEADD_PWM1 0x00FB -#define TWL4030_BASEADD_PWMA 0x00EF -#define TWL4030_BASEADD_PWMB 0x00F1 +#define TWL4030_BASEADD_PWM 0x00F8 #define TWL4030_BASEADD_KEYPAD 0x00D2 #define TWL5031_BASEADD_ACCESSORY 0x0074 /* Replaces Main Charge */ @@ -117,7 +112,7 @@ /* subchip/slave 0 0x48 - POWER */ #define TWL6030_BASEADD_RTC 0x0000 -#define TWL6030_BASEADD_MEM 0x0017 +#define TWL6030_BASEADD_SECURED_REG 0x0017 #define TWL6030_BASEADD_PM_MASTER 0x001F #define TWL6030_BASEADD_PM_SLAVE_MISC 0x0030 /* PM_RECEIVER */ #define TWL6030_BASEADD_PM_MISC 0x00E2 @@ -132,6 +127,7 @@ #define TWL6030_BASEADD_PIH 0x00D0 #define TWL6030_BASEADD_CHARGER 0x00E0 #define TWL6025_BASEADD_CHARGER 0x00DA +#define TWL6030_BASEADD_LED 0x00F4 /* subchip/slave 2 0x4A - DFT */ #define TWL6030_BASEADD_DIEID 0x00C0 @@ -188,34 +184,33 @@ static struct twl_mapping twl4030_map[] = { * so they continue to match the order in this table. */ + /* Common IPs */ { 0, TWL4030_BASEADD_USB }, + { 1, TWL4030_BASEADD_PIH }, + { 2, TWL4030_BASEADD_MAIN_CHARGE }, + { 3, TWL4030_BASEADD_PM_MASTER }, + { 3, TWL4030_BASEADD_PM_RECEIVER }, + + { 3, TWL4030_BASEADD_RTC }, + { 2, TWL4030_BASEADD_PWM }, + { 2, TWL4030_BASEADD_LED }, + { 3, TWL4030_BASEADD_SECURED_REG }, + + /* TWL4030 specific IPs */ { 1, TWL4030_BASEADD_AUDIO_VOICE }, { 1, TWL4030_BASEADD_GPIO }, { 1, TWL4030_BASEADD_INTBR }, - { 1, TWL4030_BASEADD_PIH }, - { 1, TWL4030_BASEADD_TEST }, { 2, TWL4030_BASEADD_KEYPAD }, + { 2, TWL4030_BASEADD_MADC }, { 2, TWL4030_BASEADD_INTERRUPTS }, - { 2, TWL4030_BASEADD_LED }, - - { 2, TWL4030_BASEADD_MAIN_CHARGE }, { 2, TWL4030_BASEADD_PRECHARGE }, - { 2, TWL4030_BASEADD_PWM0 }, - { 2, TWL4030_BASEADD_PWM1 }, - { 2, TWL4030_BASEADD_PWMA }, - - { 2, TWL4030_BASEADD_PWMB }, - { 2, TWL5031_BASEADD_ACCESSORY }, - { 2, TWL5031_BASEADD_INTERRUPTS }, { 3, TWL4030_BASEADD_BACKUP }, { 3, TWL4030_BASEADD_INT }, - { 3, TWL4030_BASEADD_PM_MASTER }, - { 3, TWL4030_BASEADD_PM_RECEIVER }, - { 3, TWL4030_BASEADD_RTC }, - { 3, TWL4030_BASEADD_SECURED_REG }, + { 2, TWL5031_BASEADD_ACCESSORY }, + { 2, TWL5031_BASEADD_INTERRUPTS }, }; static struct regmap_config twl4030_regmap_config[4] = { @@ -251,35 +246,25 @@ static struct twl_mapping twl6030_map[] = { * defines for TWL4030_MODULE_* * so they continue to match the order in this table. */ - { SUB_CHIP_ID1, TWL6030_BASEADD_USB }, - { SUB_CHIP_ID_INVAL, TWL6030_BASEADD_AUDIO }, - { SUB_CHIP_ID2, TWL6030_BASEADD_DIEID }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID1, TWL6030_BASEADD_PIH }, - - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID1, TWL6030_BASEADD_GPADC_CTRL }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - - { SUB_CHIP_ID1, TWL6030_BASEADD_CHARGER }, - { SUB_CHIP_ID1, TWL6030_BASEADD_GASGAUGE }, - { SUB_CHIP_ID1, TWL6030_BASEADD_PWM }, - { SUB_CHIP_ID0, TWL6030_BASEADD_ZERO }, - { SUB_CHIP_ID1, TWL6030_BASEADD_ZERO }, - - { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO }, - { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - - { SUB_CHIP_ID0, TWL6030_BASEADD_PM_MASTER }, - { SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_MISC }, - { SUB_CHIP_ID0, TWL6030_BASEADD_RTC }, - { SUB_CHIP_ID0, TWL6030_BASEADD_MEM }, - { SUB_CHIP_ID1, TWL6025_BASEADD_CHARGER }, + + /* Common IPs */ + { 1, TWL6030_BASEADD_USB }, + { 1, TWL6030_BASEADD_PIH }, + { 1, TWL6030_BASEADD_CHARGER }, + { 0, TWL6030_BASEADD_PM_MASTER }, + { 0, TWL6030_BASEADD_PM_SLAVE_MISC }, + + { 0, TWL6030_BASEADD_RTC }, + { 1, TWL6030_BASEADD_PWM }, + { 1, TWL6030_BASEADD_LED }, + { 0, TWL6030_BASEADD_SECURED_REG }, + + /* TWL6030 specific IPs */ + { 0, TWL6030_BASEADD_ZERO }, + { 1, TWL6030_BASEADD_ZERO }, + { 2, TWL6030_BASEADD_ZERO }, + { 1, TWL6030_BASEADD_GPADC_CTRL }, + { 1, TWL6030_BASEADD_GASGAUGE }, }; static struct regmap_config twl6030_regmap_config[3] = { @@ -305,6 +290,14 @@ static struct regmap_config twl6030_regmap_config[3] = { /*----------------------------------------------------------------------*/ +static inline int twl_get_last_module(void) +{ + if (twl_class_is_4030()) + return TWL4030_MODULE_LAST; + else + return TWL6030_MODULE_LAST; +} + /* Exported Functions */ /** @@ -325,7 +318,7 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int sid; struct twl_client *twl; - if (unlikely(mod_no >= TWL_MODULE_LAST)) { + if (unlikely(mod_no >= twl_get_last_module())) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } @@ -367,7 +360,7 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int sid; struct twl_client *twl; - if (unlikely(mod_no >= TWL_MODULE_LAST)) { + if (unlikely(mod_no >= twl_get_last_module())) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } @@ -1228,6 +1221,10 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) if ((id->driver_data) & TWL6030_CLASS) { twl_id = TWL6030_CLASS_ID; twl_map = &twl6030_map[0]; + /* The charger base address is different in twl6025 */ + if ((id->driver_data) & TWL6025_SUBCLASS) + twl_map[TWL_MODULE_MAIN_CHARGE].base = + TWL6025_BASEADD_CHARGER; twl_regmap_config = twl6030_regmap_config; num_slaves = TWL_NUM_SLAVES - 1; } else { diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 1ff54b114efc..72adc8807912 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -39,51 +39,55 @@ * address each module uses within a given i2c slave. */ +/* Module IDs for similar functionalities found in twl4030/twl6030 */ +enum twl_module_ids { + TWL_MODULE_USB, + TWL_MODULE_PIH, + TWL_MODULE_MAIN_CHARGE, + TWL_MODULE_PM_MASTER, + TWL_MODULE_PM_RECEIVER, + + TWL_MODULE_RTC, + TWL_MODULE_PWM, + TWL_MODULE_LED, + TWL_MODULE_SECURED_REG, + + TWL_MODULE_LAST, +}; + +/* Modules only available in twl4030 series */ enum twl4030_module_ids { - TWL4030_MODULE_USB = 0, /* Slave 0 (i2c address 0x48) */ - TWL4030_MODULE_AUDIO_VOICE, /* Slave 1 (i2c address 0x49) */ + TWL4030_MODULE_AUDIO_VOICE = TWL_MODULE_LAST, TWL4030_MODULE_GPIO, TWL4030_MODULE_INTBR, - TWL4030_MODULE_PIH, - TWL4030_MODULE_TEST, - TWL4030_MODULE_KEYPAD, /* Slave 2 (i2c address 0x4a) */ + TWL4030_MODULE_KEYPAD, + TWL4030_MODULE_MADC, TWL4030_MODULE_INTERRUPTS, - TWL4030_MODULE_LED, - - TWL4030_MODULE_MAIN_CHARGE, TWL4030_MODULE_PRECHARGE, - TWL4030_MODULE_PWM0, - TWL4030_MODULE_PWM1, - TWL4030_MODULE_PWMA, + TWL4030_MODULE_BACKUP, + TWL4030_MODULE_INT, - TWL4030_MODULE_PWMB, TWL5031_MODULE_ACCESSORY, TWL5031_MODULE_INTERRUPTS, - TWL4030_MODULE_BACKUP, /* Slave 3 (i2c address 0x4b) */ - TWL4030_MODULE_INT, - TWL4030_MODULE_PM_MASTER, - TWL4030_MODULE_PM_RECEIVER, - TWL4030_MODULE_RTC, - TWL4030_MODULE_SECURED_REG, TWL4030_MODULE_LAST, }; -/* Similar functionalities implemented in TWL4030/6030 */ -#define TWL_MODULE_USB TWL4030_MODULE_USB -#define TWL_MODULE_PIH TWL4030_MODULE_PIH -#define TWL_MODULE_MAIN_CHARGE TWL4030_MODULE_MAIN_CHARGE -#define TWL_MODULE_PM_MASTER TWL4030_MODULE_PM_MASTER -#define TWL_MODULE_PM_RECEIVER TWL4030_MODULE_PM_RECEIVER -#define TWL_MODULE_RTC TWL4030_MODULE_RTC -#define TWL_MODULE_PWM TWL4030_MODULE_PWM0 -#define TWL_MODULE_LED TWL4030_MODULE_LED - -#define TWL6030_MODULE_ID0 13 -#define TWL6030_MODULE_ID1 14 -#define TWL6030_MODULE_ID2 15 +/* Modules only available in twl6030 series */ +enum twl6030_module_ids { + TWL6030_MODULE_ID0 = TWL_MODULE_LAST, + TWL6030_MODULE_ID1, + TWL6030_MODULE_ID2, + TWL6030_MODULE_GPADC, + TWL6030_MODULE_GASGAUGE, + + TWL6030_MODULE_LAST, +}; + +/* Until the clients has been converted to use TWL_MODULE_LED */ +#define TWL4030_MODULE_LED TWL_MODULE_LED #define GPIO_INTR_OFFSET 0 #define KEYPAD_INTR_OFFSET 1 -- cgit v1.2.3-58-ga151 From ac7bc5a953c38c32513d1825decf336c4909ce3b Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:58 +0100 Subject: mfd: twl-core: Remove no longer valid comment regarding to write buffer size With the regmap conversion there is no longeer a need to allocate bigger buffer for writes Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 3 --- include/linux/i2c/twl.h | 3 --- 2 files changed, 6 deletions(-) (limited to 'include') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index e2895a4026a1..9661204fb159 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -308,9 +308,6 @@ EXPORT_SYMBOL(twl_rev); * @reg: register address (just offset will do) * @num_bytes: number of bytes to transfer * - * IMPORTANT: for 'value' parameter: Allocate value num_bytes+1 and - * valid data starts at Offset 1. - * * Returns the result of operation - 0 is success */ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 72adc8807912..e441fd8e7c86 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -182,9 +182,6 @@ int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg); /* * Read and write several 8-bit registers at once. - * - * IMPORTANT: For twl_i2c_write(), allocate num_bytes + 1 - * for the value, and populate your data starting at offset 1. */ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); -- cgit v1.2.3-58-ga151 From fbc6ae363e5e589a28135c051a2ff835e6236d5f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:59 +0100 Subject: mfd: twl-core: Move twl_i2c_read/write_u8 to header as inline function twl_i2c_read/write_u8 become as a simple wrapper over the twl_i2c_read/write. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 28 ---------------------------- include/linux/i2c/twl.h | 17 +++++++++++------ 2 files changed, 11 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 9661204fb159..557f9ee5ec18 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -379,34 +379,6 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) } EXPORT_SYMBOL(twl_i2c_read); -/** - * twl_i2c_write_u8 - Writes a 8 bit register in TWL4030/TWL5030/TWL60X0 - * @mod_no: module number - * @value: the value to be written 8 bit - * @reg: register address (just offset will do) - * - * Returns result of operation - 0 is success - */ -int twl_i2c_write_u8(u8 mod_no, u8 value, u8 reg) -{ - return twl_i2c_write(mod_no, &value, reg, 1); -} -EXPORT_SYMBOL(twl_i2c_write_u8); - -/** - * twl_i2c_read_u8 - Reads a 8 bit register from TWL4030/TWL5030/TWL60X0 - * @mod_no: module number - * @value: the value read 8 bit - * @reg: register address (just offset will do) - * - * Returns result of operation - 0 is success - */ -int twl_i2c_read_u8(u8 mod_no, u8 *value, u8 reg) -{ - return twl_i2c_read(mod_no, value, reg, 1); -} -EXPORT_SYMBOL(twl_i2c_read_u8); - /*----------------------------------------------------------------------*/ /** diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index e441fd8e7c86..488debbef895 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -174,18 +174,23 @@ static inline int twl_class_is_ ##class(void) \ TWL_CLASS_IS(4030, TWL4030_CLASS_ID) TWL_CLASS_IS(6030, TWL6030_CLASS_ID) -/* - * Read and write single 8-bit registers - */ -int twl_i2c_write_u8(u8 mod_no, u8 val, u8 reg); -int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg); - /* * Read and write several 8-bit registers at once. */ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); +/* + * Read and write single 8-bit registers + */ +static inline int twl_i2c_write_u8(u8 mod_no, u8 val, u8 reg) { + return twl_i2c_write(mod_no, &val, reg, 1); +} + +static inline int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg) { + return twl_i2c_read(mod_no, val, reg, 1); +} + int twl_get_type(void); int twl_get_version(void); int twl_get_hfclk_rate(void); -- cgit v1.2.3-58-ga151 From 98c60a0d3afc8f68e6e4b85b93df14e238fec3cb Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 18 Jan 2013 12:40:11 +0100 Subject: mfd: dbx500-prcmu: Add watchdog ID definitions Add definition of watchdog IDs to be used by ux500_wdt driver. Acked-by: Lee Jones Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Samuel Ortiz --- include/linux/mfd/dbx500-prcmu.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include') diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h index c202d6c4d879..c6e0608a21b5 100644 --- a/include/linux/mfd/dbx500-prcmu.h +++ b/include/linux/mfd/dbx500-prcmu.h @@ -146,6 +146,18 @@ enum prcmu_clock { PRCMU_DSI2ESCCLK, }; +/** + * enum prcmu_wdog_id - PRCMU watchdog IDs + * @PRCMU_WDOG_ALL: use all timers + * @PRCMU_WDOG_CPU1: use first CPU timer only + * @PRCMU_WDOG_CPU2: use second CPU timer conly + */ +enum prcmu_wdog_id { + PRCMU_WDOG_ALL = 0x00, + PRCMU_WDOG_CPU1 = 0x01, + PRCMU_WDOG_CPU2 = 0x02, +}; + /** * enum ape_opp - APE OPP states definition * @APE_OPP_INIT: -- cgit v1.2.3-58-ga151 From f0e5bd412fde30de3839c8dfa93a3e19e71ee462 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Tue, 29 Jan 2013 09:57:19 +0100 Subject: watchdog: Add support for ux500_wdt watchdog This patch adds support for the ux500_wdt watchdog that is found in ST-Ericsson Ux500 platform. The driver is based on PRCMU APIs. Acked-by: Linus Walleij Acked-by: Lee Jones Acked-by: Wim Van Sebroeck Signed-off-by: Fabio Baltieri Signed-off-by: Samuel Ortiz --- drivers/watchdog/Kconfig | 12 +++ drivers/watchdog/Makefile | 1 + drivers/watchdog/ux500_wdt.c | 171 ++++++++++++++++++++++++++++++++ include/linux/platform_data/ux500_wdt.h | 19 ++++ 4 files changed, 203 insertions(+) create mode 100644 drivers/watchdog/ux500_wdt.c create mode 100644 include/linux/platform_data/ux500_wdt.h (limited to 'include') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 7f809fd4a57f..26e1fdbddf69 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -364,6 +364,18 @@ config IMX2_WDT To compile this driver as a module, choose M here: the module will be called imx2_wdt. +config UX500_WATCHDOG + tristate "ST-Ericsson Ux500 watchdog" + depends on MFD_DB8500_PRCMU + select WATCHDOG_CORE + default y + help + Say Y here to include Watchdog timer support for the watchdog + existing in the prcmu of ST-Ericsson Ux500 series platforms. + + To compile this driver as a module, choose M here: the + module will be called ux500_wdt. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 97bbdb3a4648..bec86ee6e9e3 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o +obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/ux500_wdt.c new file mode 100644 index 000000000000..a614d84121c3 --- /dev/null +++ b/drivers/watchdog/ux500_wdt.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) ST-Ericsson SA 2011-2013 + * + * License Terms: GNU General Public License v2 + * + * Author: Mathieu Poirier for ST-Ericsson + * Author: Jonas Aaberg for ST-Ericsson + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define WATCHDOG_TIMEOUT 600 /* 10 minutes */ + +#define WATCHDOG_MIN 0 +#define WATCHDOG_MAX28 268435 /* 28 bit resolution in ms == 268435.455 s */ +#define WATCHDOG_MAX32 4294967 /* 32 bit resolution in ms == 4294967.295 s */ + +static unsigned int timeout = WATCHDOG_TIMEOUT; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. default=" + __MODULE_STRING(WATCHDOG_TIMEOUT) "."); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static int ux500_wdt_start(struct watchdog_device *wdd) +{ + return prcmu_enable_a9wdog(PRCMU_WDOG_ALL); +} + +static int ux500_wdt_stop(struct watchdog_device *wdd) +{ + return prcmu_disable_a9wdog(PRCMU_WDOG_ALL); +} + +static int ux500_wdt_keepalive(struct watchdog_device *wdd) +{ + return prcmu_kick_a9wdog(PRCMU_WDOG_ALL); +} + +static int ux500_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + ux500_wdt_stop(wdd); + prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + ux500_wdt_start(wdd); + + return 0; +} + +static const struct watchdog_info ux500_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "Ux500 WDT", + .firmware_version = 1, +}; + +static const struct watchdog_ops ux500_wdt_ops = { + .owner = THIS_MODULE, + .start = ux500_wdt_start, + .stop = ux500_wdt_stop, + .ping = ux500_wdt_keepalive, + .set_timeout = ux500_wdt_set_timeout, +}; + +static struct watchdog_device ux500_wdt = { + .info = &ux500_wdt_info, + .ops = &ux500_wdt_ops, + .min_timeout = WATCHDOG_MIN, + .max_timeout = WATCHDOG_MAX32, +}; + +static int ux500_wdt_probe(struct platform_device *pdev) +{ + int ret; + struct ux500_wdt_data *pdata = pdev->dev.platform_data; + + if (pdata) { + if (pdata->timeout > 0) + timeout = pdata->timeout; + if (pdata->has_28_bits_resolution) + ux500_wdt.max_timeout = WATCHDOG_MAX28; + } + + watchdog_set_nowayout(&ux500_wdt, nowayout); + + /* disable auto off on sleep */ + prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); + + /* set HW initial value */ + prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + + ret = watchdog_register_device(&ux500_wdt); + if (ret) + return ret; + + dev_info(&pdev->dev, "initialized\n"); + + return 0; +} + +static int ux500_wdt_remove(struct platform_device *dev) +{ + watchdog_unregister_device(&ux500_wdt); + + return 0; +} + +#ifdef CONFIG_PM +static int ux500_wdt_suspend(struct platform_device *pdev, + pm_message_t state) +{ + if (watchdog_active(&ux500_wdt)) { + ux500_wdt_stop(&ux500_wdt); + prcmu_config_a9wdog(PRCMU_WDOG_CPU1, true); + + prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + ux500_wdt_start(&ux500_wdt); + } + return 0; +} + +static int ux500_wdt_resume(struct platform_device *pdev) +{ + if (watchdog_active(&ux500_wdt)) { + ux500_wdt_stop(&ux500_wdt); + prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); + + prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + ux500_wdt_start(&ux500_wdt); + } + return 0; +} +#else +#define ux500_wdt_suspend NULL +#define ux500_wdt_resume NULL +#endif + +static struct platform_driver ux500_wdt_driver = { + .probe = ux500_wdt_probe, + .remove = ux500_wdt_remove, + .suspend = ux500_wdt_suspend, + .resume = ux500_wdt_resume, + .driver = { + .owner = THIS_MODULE, + .name = "ux500_wdt", + }, +}; + +module_platform_driver(ux500_wdt_driver); + +MODULE_AUTHOR("Jonas Aaberg "); +MODULE_DESCRIPTION("Ux500 Watchdog Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +MODULE_ALIAS("platform:ux500_wdt"); diff --git a/include/linux/platform_data/ux500_wdt.h b/include/linux/platform_data/ux500_wdt.h new file mode 100644 index 000000000000..1689ff4c3bfd --- /dev/null +++ b/include/linux/platform_data/ux500_wdt.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) ST Ericsson SA 2011 + * + * License Terms: GNU General Public License v2 + * + * STE Ux500 Watchdog platform data + */ +#ifndef __UX500_WDT_H +#define __UX500_WDT_H + +/** + * struct ux500_wdt_data + */ +struct ux500_wdt_data { + unsigned int timeout; + bool has_28_bits_resolution; +}; + +#endif /* __UX500_WDT_H */ -- cgit v1.2.3-58-ga151 From 306df798507d8e009a7d4a5e8ce238a3b107de20 Mon Sep 17 00:00:00 2001 From: Yi Zhang Date: Tue, 22 Jan 2013 10:43:45 +0800 Subject: mfd: 88pm80x: Remove redundant devm_* calls devm_* functions are device managed and make error handling and code simpler; it also fix error exit paths Signed-off-by: Yi Zhang Signed-off-by: Samuel Ortiz --- drivers/mfd/88pm800.c | 10 +++------- drivers/mfd/88pm805.c | 4 ++-- drivers/mfd/88pm80x.c | 22 ++++------------------ include/linux/mfd/88pm80x.h | 2 +- 4 files changed, 10 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c index 391e23e6a647..582bda543520 100644 --- a/drivers/mfd/88pm800.c +++ b/drivers/mfd/88pm800.c @@ -531,7 +531,7 @@ static int pm800_probe(struct i2c_client *client, ret = device_800_init(chip, pdata); if (ret) { dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id); - goto err_800_init; + goto err_subchip_alloc; } ret = pm800_pages_init(chip); @@ -546,10 +546,8 @@ static int pm800_probe(struct i2c_client *client, err_page_init: mfd_remove_devices(chip->dev); device_irq_exit_800(chip); -err_800_init: - devm_kfree(&client->dev, subchip); err_subchip_alloc: - pm80x_deinit(client); + pm80x_deinit(); out_init: return ret; } @@ -562,9 +560,7 @@ static int pm800_remove(struct i2c_client *client) device_irq_exit_800(chip); pm800_pages_exit(chip); - devm_kfree(&client->dev, chip->subchip); - - pm80x_deinit(client); + pm80x_deinit(); return 0; } diff --git a/drivers/mfd/88pm805.c b/drivers/mfd/88pm805.c index e671230be2b1..65d7ac099b20 100644 --- a/drivers/mfd/88pm805.c +++ b/drivers/mfd/88pm805.c @@ -257,7 +257,7 @@ static int pm805_probe(struct i2c_client *client, pdata->plat_config(chip, pdata); err_805_init: - pm80x_deinit(client); + pm80x_deinit(); out_init: return ret; } @@ -269,7 +269,7 @@ static int pm805_remove(struct i2c_client *client) mfd_remove_devices(chip->dev); device_irq_exit_805(chip); - pm80x_deinit(client); + pm80x_deinit(); return 0; } diff --git a/drivers/mfd/88pm80x.c b/drivers/mfd/88pm80x.c index 1adb355d86d1..f736a46eb8c0 100644 --- a/drivers/mfd/88pm80x.c +++ b/drivers/mfd/88pm80x.c @@ -48,14 +48,12 @@ int pm80x_init(struct i2c_client *client, ret = PTR_ERR(map); dev_err(&client->dev, "Failed to allocate register map: %d\n", ret); - goto err_regmap_init; + return ret; } chip->id = id->driver_data; - if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805) { - ret = -EINVAL; - goto err_chip_id; - } + if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805) + return -EINVAL; chip->client = client; chip->regmap = map; @@ -82,19 +80,11 @@ int pm80x_init(struct i2c_client *client, } return 0; - -err_chip_id: - regmap_exit(map); -err_regmap_init: - devm_kfree(&client->dev, chip); - return ret; } EXPORT_SYMBOL_GPL(pm80x_init); -int pm80x_deinit(struct i2c_client *client) +int pm80x_deinit(void) { - struct pm80x_chip *chip = i2c_get_clientdata(client); - /* * workaround: clear the dependency between pm800 and pm805. * would remove it after HW chip fixes the issue. @@ -103,10 +93,6 @@ int pm80x_deinit(struct i2c_client *client) g_pm80x_chip->companion = NULL; else g_pm80x_chip = NULL; - - regmap_exit(chip->regmap); - devm_kfree(&client->dev, chip); - return 0; } EXPORT_SYMBOL_GPL(pm80x_deinit); diff --git a/include/linux/mfd/88pm80x.h b/include/linux/mfd/88pm80x.h index 478672ed0c3d..e94537befabd 100644 --- a/include/linux/mfd/88pm80x.h +++ b/include/linux/mfd/88pm80x.h @@ -365,5 +365,5 @@ static inline int pm80x_dev_resume(struct device *dev) extern int pm80x_init(struct i2c_client *client, const struct i2c_device_id *id); -extern int pm80x_deinit(struct i2c_client *client); +extern int pm80x_deinit(void); #endif /* __LINUX_MFD_88PM80X_H */ -- cgit v1.2.3-58-ga151 From 5f384c1f8be19487f904731d7232120dcfeca8e1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 25 Jan 2013 17:09:23 +0800 Subject: mfd: wm5102: Make DSP scratch registers readable Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 8 ++++++++ include/linux/mfd/arizona/registers.h | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) (limited to 'include') diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index ff8f0804dde3..d754596eb942 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -1795,6 +1795,10 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: case ARIZONA_DSP1_STATUS_3: + case ARIZONA_DSP1_SCRATCH_0: + case ARIZONA_DSP1_SCRATCH_1: + case ARIZONA_DSP1_SCRATCH_2: + case ARIZONA_DSP1_SCRATCH_3: return true; default: if ((reg >= 0x100000 && reg < 0x106000) || @@ -1849,6 +1853,10 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: case ARIZONA_DSP1_STATUS_3: + case ARIZONA_DSP1_SCRATCH_0: + case ARIZONA_DSP1_SCRATCH_1: + case ARIZONA_DSP1_SCRATCH_2: + case ARIZONA_DSP1_SCRATCH_3: case ARIZONA_HEADPHONE_DETECT_2: case ARIZONA_MIC_DETECT_3: return true; diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 1f6fe31a4d5c..718126084ad1 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -982,18 +982,34 @@ #define ARIZONA_DSP1_STATUS_1 0x1104 #define ARIZONA_DSP1_STATUS_2 0x1105 #define ARIZONA_DSP1_STATUS_3 0x1106 +#define ARIZONA_DSP1_SCRATCH_0 0x1140 +#define ARIZONA_DSP1_SCRATCH_1 0x1141 +#define ARIZONA_DSP1_SCRATCH_2 0x1142 +#define ARIZONA_DSP1_SCRATCH_3 0x1143 #define ARIZONA_DSP2_CONTROL_1 0x1200 #define ARIZONA_DSP2_CLOCKING_1 0x1201 #define ARIZONA_DSP2_STATUS_1 0x1204 #define ARIZONA_DSP2_STATUS_2 0x1205 +#define ARIZONA_DSP2_SCRATCH_0 0x1240 +#define ARIZONA_DSP2_SCRATCH_1 0x1241 +#define ARIZONA_DSP2_SCRATCH_2 0x1242 +#define ARIZONA_DSP2_SCRATCH_3 0x1243 #define ARIZONA_DSP3_CONTROL_1 0x1300 #define ARIZONA_DSP3_CLOCKING_1 0x1301 #define ARIZONA_DSP3_STATUS_1 0x1304 #define ARIZONA_DSP3_STATUS_2 0x1305 +#define ARIZONA_DSP3_SCRATCH_0 0x1340 +#define ARIZONA_DSP3_SCRATCH_1 0x1341 +#define ARIZONA_DSP3_SCRATCH_2 0x1342 +#define ARIZONA_DSP3_SCRATCH_3 0x1343 #define ARIZONA_DSP4_CONTROL_1 0x1400 #define ARIZONA_DSP4_CLOCKING_1 0x1401 #define ARIZONA_DSP4_STATUS_1 0x1404 #define ARIZONA_DSP4_STATUS_2 0x1405 +#define ARIZONA_DSP4_SCRATCH_0 0x1440 +#define ARIZONA_DSP4_SCRATCH_1 0x1441 +#define ARIZONA_DSP4_SCRATCH_2 0x1442 +#define ARIZONA_DSP4_SCRATCH_3 0x1443 /* * Field Definitions. -- cgit v1.2.3-58-ga151 From 3730bb8b65f9ee8b7097021f8073b80511af770d Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 29 Jan 2013 15:21:32 +0800 Subject: mfd: rtsx: Fix typo in comment Fix a misspelling word in comment Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- include/linux/mfd/rtsx_pci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 4b117a3f54d4..3f2bf26ca0d1 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -465,7 +465,7 @@ #define SD_RSP_TYPE_R6 0x01 #define SD_RSP_TYPE_R7 0x01 -/* SD_CONFIURE3 */ +/* SD_CONFIGURE3 */ #define SD_RSP_80CLK_TIMEOUT_EN 0x01 /* Card Transfer Reset Register */ -- cgit v1.2.3-58-ga151 From 8ea402f5646e6e36c8cd0a62053ba8939204dceb Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Wed, 30 Jan 2013 10:33:16 +0000 Subject: mfd: vexpress: Add pseudo-GPIO based LEDs The LEDs on the Versatile Express motherboard are controlled through simple memory-mapped register. This patch extends the pseudo-GPIO controller definition for these lines and creates generic "leds-gpio" device using them Signed-off-by: Pawel Moll Signed-off-by: Samuel Ortiz --- drivers/mfd/vexpress-sysreg.c | 73 ++++++++++++++++++++++++++++++++----------- include/linux/vexpress.h | 8 +++++ 2 files changed, 63 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index 77048b18439e..51c3ca263bf5 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -49,6 +49,8 @@ #define SYS_ID_HBI_SHIFT 16 #define SYS_PROCIDx_HBI_SHIFT 0 +#define SYS_LED_LED(n) (1 << (n)) + #define SYS_MCI_CARDIN (1 << 0) #define SYS_MCI_WPROT (1 << 1) @@ -348,22 +350,27 @@ void __init vexpress_sysreg_of_early_init(void) } +#define VEXPRESS_SYSREG_GPIO(_name, _reg, _value) \ + [VEXPRESS_GPIO_##_name] = { \ + .reg = _reg, \ + .value = _reg##_##_value, \ + } + static struct vexpress_sysreg_gpio { unsigned long reg; u32 value; } vexpress_sysreg_gpios[] = { - [VEXPRESS_GPIO_MMC_CARDIN] = { - .reg = SYS_MCI, - .value = SYS_MCI_CARDIN, - }, - [VEXPRESS_GPIO_MMC_WPROT] = { - .reg = SYS_MCI, - .value = SYS_MCI_WPROT, - }, - [VEXPRESS_GPIO_FLASH_WPn] = { - .reg = SYS_FLASH, - .value = SYS_FLASH_WPn, - }, + VEXPRESS_SYSREG_GPIO(MMC_CARDIN, SYS_MCI, CARDIN), + VEXPRESS_SYSREG_GPIO(MMC_WPROT, SYS_MCI, WPROT), + VEXPRESS_SYSREG_GPIO(FLASH_WPn, SYS_FLASH, WPn), + VEXPRESS_SYSREG_GPIO(LED0, SYS_LED, LED(0)), + VEXPRESS_SYSREG_GPIO(LED1, SYS_LED, LED(1)), + VEXPRESS_SYSREG_GPIO(LED2, SYS_LED, LED(2)), + VEXPRESS_SYSREG_GPIO(LED3, SYS_LED, LED(3)), + VEXPRESS_SYSREG_GPIO(LED4, SYS_LED, LED(4)), + VEXPRESS_SYSREG_GPIO(LED5, SYS_LED, LED(5)), + VEXPRESS_SYSREG_GPIO(LED6, SYS_LED, LED(6)), + VEXPRESS_SYSREG_GPIO(LED7, SYS_LED, LED(7)), }; static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip, @@ -372,12 +379,6 @@ static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip, return 0; } -static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - return 0; -} - static int vexpress_sysreg_gpio_get(struct gpio_chip *chip, unsigned offset) { @@ -401,6 +402,14 @@ static void vexpress_sysreg_gpio_set(struct gpio_chip *chip, writel(reg_value, vexpress_sysreg_base + gpio->reg); } +static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + vexpress_sysreg_gpio_set(chip, offset, value); + + return 0; +} + static struct gpio_chip vexpress_sysreg_gpio_chip = { .label = "vexpress-sysreg", .direction_input = vexpress_sysreg_gpio_direction_input, @@ -412,6 +421,30 @@ static struct gpio_chip vexpress_sysreg_gpio_chip = { }; +#define VEXPRESS_SYSREG_GREEN_LED(_name, _default_trigger, _gpio) \ + { \ + .name = "v2m:green:"_name, \ + .default_trigger = _default_trigger, \ + .gpio = VEXPRESS_GPIO_##_gpio, \ + } + +struct gpio_led vexpress_sysreg_leds[] = { + VEXPRESS_SYSREG_GREEN_LED("user1", "heartbeat", LED0), + VEXPRESS_SYSREG_GREEN_LED("user2", "mmc0", LED1), + VEXPRESS_SYSREG_GREEN_LED("user3", "cpu0", LED2), + VEXPRESS_SYSREG_GREEN_LED("user4", "cpu1", LED3), + VEXPRESS_SYSREG_GREEN_LED("user5", "cpu2", LED4), + VEXPRESS_SYSREG_GREEN_LED("user6", "cpu3", LED5), + VEXPRESS_SYSREG_GREEN_LED("user7", "cpu4", LED6), + VEXPRESS_SYSREG_GREEN_LED("user8", "cpu5", LED7), +}; + +struct gpio_led_platform_data vexpress_sysreg_leds_pdata = { + .num_leds = ARRAY_SIZE(vexpress_sysreg_leds), + .leds = vexpress_sysreg_leds, +}; + + static ssize_t vexpress_sysreg_sys_id_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -456,6 +489,10 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) return err; } + platform_device_register_data(vexpress_sysreg_dev, "leds-gpio", + PLATFORM_DEVID_AUTO, &vexpress_sysreg_leds_pdata, + sizeof(vexpress_sysreg_leds_pdata)); + vexpress_sysreg_dev = &pdev->dev; device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id); diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h index c52215ff4245..75818744ab59 100644 --- a/include/linux/vexpress.h +++ b/include/linux/vexpress.h @@ -27,6 +27,14 @@ #define VEXPRESS_GPIO_MMC_CARDIN 0 #define VEXPRESS_GPIO_MMC_WPROT 1 #define VEXPRESS_GPIO_FLASH_WPn 2 +#define VEXPRESS_GPIO_LED0 3 +#define VEXPRESS_GPIO_LED1 4 +#define VEXPRESS_GPIO_LED2 5 +#define VEXPRESS_GPIO_LED3 6 +#define VEXPRESS_GPIO_LED4 7 +#define VEXPRESS_GPIO_LED5 8 +#define VEXPRESS_GPIO_LED6 9 +#define VEXPRESS_GPIO_LED7 10 #define VEXPRESS_RES_FUNC(_site, _func) \ { \ -- cgit v1.2.3-58-ga151 From e12379320b2e1ceffc4211ad174989bc042149d9 Mon Sep 17 00:00:00 2001 From: Roger Tseng Date: Mon, 4 Feb 2013 15:45:59 +0800 Subject: mfd: rtsx: Support RTS5227 Support new model RTS5227. Signed-off-by: Roger Tseng Reviewed-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mfd/Makefile | 2 +- drivers/mfd/rts5227.c | 234 +++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/rtsx_pcr.c | 5 + drivers/mfd/rtsx_pcr.h | 1 + include/linux/mfd/rtsx_pci.h | 5 + 5 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 drivers/mfd/rts5227.c (limited to 'include') diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 8b977f8045ae..b90409c23664 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o -rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o +rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c new file mode 100644 index 000000000000..fc831dcb1480 --- /dev/null +++ b/drivers/mfd/rts5227.c @@ -0,0 +1,234 @@ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + * Author: + * Wei WANG + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + * + * Roger Tseng + * No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan + */ + +#include +#include +#include + +#include "rtsx_pcr.h" + +static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) +{ + u16 cap; + + rtsx_pci_init_cmd(pcr); + + /* Configure GPIO as output */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02); + /* Switch LDO3318 source from DV33 to card_3v3 */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x00); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01); + /* LED shine disabled, set initial shine cycle period */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02); + /* Configure LTR */ + pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &cap); + if (cap & PCI_EXP_LTR_EN) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LTR_CTL, 0xFF, 0xA3); + /* Configure OBFF */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OBFF_CFG, 0x03, 0x03); + /* Configure force_clock_req + * Maybe We should define 0xFF03 as some name + */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, 0xFF03, 0x08, 0x08); + /* Correct driving */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD30_CLK_DRIVE_SEL, 0xFF, 0x96); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD30_CMD_DRIVE_SEL, 0xFF, 0x96); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD30_DAT_DRIVE_SEL, 0xFF, 0x96); + + return rtsx_pci_send_cmd(pcr, 100); +} + +static int rts5227_optimize_phy(struct rtsx_pcr *pcr) +{ + /* Optimize RX sensitivity */ + return rtsx_pci_write_phy_register(pcr, 0x00, 0xBA42); +} + +static int rts5227_turn_on_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02); +} + +static int rts5227_turn_off_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00); +} + +static int rts5227_enable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08); +} + +static int rts5227_disable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00); +} + +static int rts5227_card_power_on(struct rtsx_pcr *pcr, int card) +{ + int err; + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + SD_POWER_MASK, SD_PARTIAL_POWER_ON); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0x02); + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + /* To avoid too large in-rush current */ + udelay(150); + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + SD_POWER_MASK, SD_POWER_ON); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0x06); + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + return 0; +} + +static int rts5227_card_power_off(struct rtsx_pcr *pcr, int card) +{ + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + SD_POWER_MASK | PMOS_STRG_MASK, + SD_POWER_OFF | PMOS_STRG_400mA); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0X00); + return rtsx_pci_send_cmd(pcr, 100); +} + +static int rts5227_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ + int err; + u8 drive_sel; + + if (voltage == OUTPUT_3V3) { + err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); + if (err < 0) + return err; + drive_sel = 0x96; + } else if (voltage == OUTPUT_1V8) { + err = rtsx_pci_write_phy_register(pcr, 0x11, 0x3C02); + if (err < 0) + return err; + err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C80 | 0x24); + if (err < 0) + return err; + drive_sel = 0xB3; + } else { + return -EINVAL; + } + + /* set pad drive */ + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CLK_DRIVE_SEL, + 0xFF, drive_sel); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CMD_DRIVE_SEL, + 0xFF, drive_sel); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DAT_DRIVE_SEL, + 0xFF, drive_sel); + return rtsx_pci_send_cmd(pcr, 100); +} + +static const struct pcr_ops rts5227_pcr_ops = { + .extra_init_hw = rts5227_extra_init_hw, + .optimize_phy = rts5227_optimize_phy, + .turn_on_led = rts5227_turn_on_led, + .turn_off_led = rts5227_turn_off_led, + .enable_auto_blink = rts5227_enable_auto_blink, + .disable_auto_blink = rts5227_disable_auto_blink, + .card_power_on = rts5227_card_power_on, + .card_power_off = rts5227_card_power_off, + .switch_output_voltage = rts5227_switch_output_voltage, + .cd_deglitch = NULL, + .conv_clk_and_div_n = NULL, +}; + +/* SD Pull Control Enable: + * SD_DAT[3:0] ==> pull up + * SD_CD ==> pull up + * SD_WP ==> pull up + * SD_CMD ==> pull up + * SD_CLK ==> pull down + */ +static const u32 rts5227_sd_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9), + 0, +}; + +/* SD Pull Control Disable: + * SD_DAT[3:0] ==> pull down + * SD_CD ==> pull up + * SD_WP ==> pull down + * SD_CMD ==> pull down + * SD_CLK ==> pull down + */ +static const u32 rts5227_sd_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5), + 0, +}; + +/* MS Pull Control Enable: + * MS CD ==> pull up + * others ==> pull down + */ +static const u32 rts5227_ms_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), + 0, +}; + +/* MS Pull Control Disable: + * MS CD ==> pull up + * others ==> pull down + */ +static const u32 rts5227_ms_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), + 0, +}; + +void rts5227_init_params(struct rtsx_pcr *pcr) +{ + pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; + pcr->num_slots = 2; + pcr->ops = &rts5227_pcr_ops; + + pcr->sd_pull_ctl_enable_tbl = rts5227_sd_pull_ctl_enable_tbl; + pcr->sd_pull_ctl_disable_tbl = rts5227_sd_pull_ctl_disable_tbl; + pcr->ms_pull_ctl_enable_tbl = rts5227_ms_pull_ctl_enable_tbl; + pcr->ms_pull_ctl_disable_tbl = rts5227_ms_pull_ctl_disable_tbl; +} diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 9016932f0267..822237e322ba 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -55,6 +55,7 @@ static DEFINE_PCI_DEVICE_TABLE(rtsx_pci_ids) = { { PCI_DEVICE(0x10EC, 0x5209), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5229), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5289), PCI_CLASS_OTHERS << 16, 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5227), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { 0, } }; @@ -998,6 +999,10 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) case 0x5289: rtl8411_init_params(pcr); break; + + case 0x5227: + rts5227_init_params(pcr); + break; } dev_dbg(&(pcr->pci->dev), "PID: 0x%04x, IC version: 0x%02x\n", diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index 33c210be1daa..2b3ab8a04823 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -31,5 +31,6 @@ void rts5209_init_params(struct rtsx_pcr *pcr); void rts5229_init_params(struct rtsx_pcr *pcr); void rtl8411_init_params(struct rtsx_pcr *pcr); +void rts5227_init_params(struct rtsx_pcr *pcr); #endif diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 3f2bf26ca0d1..5d9b81e8aff4 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -581,8 +581,11 @@ #define CARD_GPIO_DIR 0xFD57 #define CARD_GPIO 0xFD58 #define CARD_DATA_SOURCE 0xFD5B +#define SD30_CLK_DRIVE_SEL 0xFD5A #define CARD_SELECT 0xFD5C #define SD30_DRIVE_SEL 0xFD5E +#define SD30_CMD_DRIVE_SEL 0xFD5E +#define SD30_DAT_DRIVE_SEL 0xFD5F #define CARD_CLK_EN 0xFD69 #define SDIO_CTRL 0xFD6B #define CD_PAD_CTL 0xFD73 @@ -655,6 +658,8 @@ #define MSGTXDATA3 0xFE47 #define MSGTXCTL 0xFE48 #define PETXCFG 0xFE49 +#define LTR_CTL 0xFE4A +#define OBFF_CFG 0xFE4C #define CDRESUMECTL 0xFE52 #define WAKE_SEL_CTL 0xFE54 -- cgit v1.2.3-58-ga151 From 4e405ae256b7e04f7c1213136f3bfd9fb76e2023 Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Mon, 4 Feb 2013 23:40:42 +0800 Subject: mfd: max8925: Add irqdomain for dt Add irqdomains for max8925's main irq, wrap irq register operations into irqdomain's map func. it is necessary for dt support. Also, add dt support for max8925 driver. Signed-off-by: Qing Xu Signed-off-by: Haojian Zhuang Signed-off-by: Samuel Ortiz --- drivers/mfd/max8925-core.c | 73 ++++++++++++++++++++++++++------------------- drivers/mfd/max8925-i2c.c | 36 ++++++++++++++++++++-- include/linux/mfd/max8925.h | 3 +- 3 files changed, 78 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index e32466e865b9..0ad8d9a7c15a 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -14,10 +14,13 @@ #include #include #include +#include #include #include #include #include +#include +#include static struct resource bk_resources[] = { { 0x84, 0x84, "mode control", IORESOURCE_REG, }, @@ -639,17 +642,33 @@ static struct irq_chip max8925_irq_chip = { .irq_disable = max8925_irq_disable, }; +static int max8925_irq_domain_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_data(virq, d->host_data); + irq_set_chip_and_handler(virq, &max8925_irq_chip, handle_edge_irq); + irq_set_nested_thread(virq, 1); +#ifdef CONFIG_ARM + set_irq_flags(virq, IRQF_VALID); +#else + irq_set_noprobe(virq); +#endif + return 0; +} + +static struct irq_domain_ops max8925_irq_domain_ops = { + .map = max8925_irq_domain_map, + .xlate = irq_domain_xlate_onetwocell, +}; + + static int max8925_irq_init(struct max8925_chip *chip, int irq, struct max8925_platform_data *pdata) { unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; - int i, ret; - int __irq; + int ret; + struct device_node *node = chip->dev->of_node; - if (!pdata || !pdata->irq_base) { - dev_warn(chip->dev, "No interrupt support on IRQ base\n"); - return -EINVAL; - } /* clear all interrupts */ max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ1); max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ2); @@ -667,35 +686,30 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq, max8925_reg_write(chip->rtc, MAX8925_RTC_IRQ_MASK, 0xff); mutex_init(&chip->irq_lock); - chip->core_irq = irq; - chip->irq_base = pdata->irq_base; - - /* register with genirq */ - for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) { - __irq = i + chip->irq_base; - irq_set_chip_data(__irq, chip); - irq_set_chip_and_handler(__irq, &max8925_irq_chip, - handle_edge_irq); - irq_set_nested_thread(__irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(__irq, IRQF_VALID); -#else - irq_set_noprobe(__irq); -#endif - } - if (!irq) { - dev_warn(chip->dev, "No interrupt support on core IRQ\n"); - goto tsc_irq; + chip->irq_base = irq_alloc_descs(-1, 0, MAX8925_NR_IRQS, 0); + if (chip->irq_base < 0) { + dev_err(chip->dev, "Failed to allocate interrupts, ret:%d\n", + chip->irq_base); + return -EBUSY; } + irq_domain_add_legacy(node, MAX8925_NR_IRQS, chip->irq_base, 0, + &max8925_irq_domain_ops, chip); + + /* request irq handler for pmic main irq*/ + chip->core_irq = irq; + if (!chip->core_irq) + return -EBUSY; ret = request_threaded_irq(irq, NULL, max8925_irq, flags | IRQF_ONESHOT, "max8925", chip); if (ret) { dev_err(chip->dev, "Failed to request core IRQ: %d\n", ret); chip->core_irq = 0; + return -EBUSY; } -tsc_irq: + /* request irq handler for pmic tsc irq*/ + /* mask TSC interrupt */ max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0x0f); @@ -704,7 +718,6 @@ tsc_irq: return 0; } chip->tsc_irq = pdata->tsc_irq; - ret = request_threaded_irq(chip->tsc_irq, NULL, max8925_tsc_irq, flags | IRQF_ONESHOT, "max8925-tsc", chip); if (ret) { @@ -875,11 +888,11 @@ int max8925_device_init(struct max8925_chip *chip, if (pdata && pdata->power) { ret = mfd_add_devices(chip->dev, 0, &power_devs[0], - ARRAY_SIZE(power_devs), + ARRAY_SIZE(power_devs), &power_supply_resources[0], 0, NULL); if (ret < 0) { - dev_err(chip->dev, "Failed to add power supply " - "subdev\n"); + dev_err(chip->dev, + "Failed to add power supply subdev\n"); goto out_dev; } } diff --git a/drivers/mfd/max8925-i2c.c b/drivers/mfd/max8925-i2c.c index 00b5b456063d..92bbebd31598 100644 --- a/drivers/mfd/max8925-i2c.c +++ b/drivers/mfd/max8925-i2c.c @@ -135,13 +135,37 @@ static const struct i2c_device_id max8925_id_table[] = { }; MODULE_DEVICE_TABLE(i2c, max8925_id_table); +static int max8925_dt_init(struct device_node *np, struct device *dev, + struct max8925_platform_data *pdata) +{ + int ret; + + ret = of_property_read_u32(np, "maxim,tsc-irq", &pdata->tsc_irq); + if (ret) { + dev_err(dev, "Not found maxim,tsc-irq property\n"); + return -EINVAL; + } + return 0; +} + static int max8925_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct max8925_platform_data *pdata = client->dev.platform_data; static struct max8925_chip *chip; - - if (!pdata) { + struct device_node *node = client->dev.of_node; + + if (node && !pdata) { + /* parse DT to get platform data */ + pdata = devm_kzalloc(&client->dev, + sizeof(struct max8925_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (max8925_dt_init(node, &client->dev, pdata)) + return -EINVAL; + } else if (!pdata) { pr_info("%s: platform data is missing\n", __func__); return -EINVAL; } @@ -203,11 +227,18 @@ static int max8925_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(max8925_pm_ops, max8925_suspend, max8925_resume); +static const struct of_device_id max8925_dt_ids[] = { + { .compatible = "maxim,max8925", }, + {}, +}; +MODULE_DEVICE_TABLE(of, max8925_dt_ids); + static struct i2c_driver max8925_driver = { .driver = { .name = "max8925", .owner = THIS_MODULE, .pm = &max8925_pm_ops, + .of_match_table = of_match_ptr(max8925_dt_ids), }, .probe = max8925_probe, .remove = max8925_remove, @@ -217,7 +248,6 @@ static struct i2c_driver max8925_driver = { static int __init max8925_i2c_init(void) { int ret; - ret = i2c_add_driver(&max8925_driver); if (ret != 0) pr_err("Failed to register MAX8925 I2C driver: %d\n", ret); diff --git a/include/linux/mfd/max8925.h b/include/linux/mfd/max8925.h index 74d8e2969630..ce8502e9e7dc 100644 --- a/include/linux/mfd/max8925.h +++ b/include/linux/mfd/max8925.h @@ -190,6 +190,8 @@ enum { MAX8925_NR_IRQS, }; + + struct max8925_chip { struct device *dev; struct i2c_client *i2c; @@ -201,7 +203,6 @@ struct max8925_chip { int irq_base; int core_irq; int tsc_irq; - unsigned int wakeup_flag; }; -- cgit v1.2.3-58-ga151 From c3481955f6c78c8dd99921759306d7469c999ec2 Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Fri, 8 Feb 2013 15:24:27 +0800 Subject: mfd: rtsx: Fix issue that booting OS with SD card inserted Realtek card reader supports both SD and MS card. According to the settings of rtsx MFD driver, SD host will be probed before MS host. If we boot/reboot Linux with SD card inserted, the resetting flow of SD card will succeed, and the following resetting flow of MS is sure to fail. Then MS upper-level driver will ask rtsx driver to turn power off. This request leads to the result that the following SD commands fail and SD card can't be accessed again. In this commit, Realtek's SD and MS host driver will check whether the card that upper driver requesting is the one existing in the slot. If not, Realtek's host driver will refuse the operation to make sure the exlusive accessing at the same time. Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/memstick/host/rtsx_pci_ms.c | 7 +++++++ drivers/mfd/rtsx_pcr.c | 30 ++++++++++++++++++++++++++++++ drivers/mmc/host/rtsx_pci_sdmmc.c | 18 ++++++++++++++++++ include/linux/mfd/rtsx_pci.h | 2 ++ 4 files changed, 57 insertions(+) (limited to 'include') diff --git a/drivers/memstick/host/rtsx_pci_ms.c b/drivers/memstick/host/rtsx_pci_ms.c index f5ddb82dadb7..64a779c58a74 100644 --- a/drivers/memstick/host/rtsx_pci_ms.c +++ b/drivers/memstick/host/rtsx_pci_ms.c @@ -426,6 +426,9 @@ static void rtsx_pci_ms_request(struct memstick_host *msh) dev_dbg(ms_dev(host), "--> %s\n", __func__); + if (rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD)) + return; + schedule_work(&host->handle_req); } @@ -441,6 +444,10 @@ static int rtsx_pci_ms_set_param(struct memstick_host *msh, dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n", __func__, param, value); + err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD); + if (err) + return err; + switch (param) { case MEMSTICK_POWER: if (value == MEMSTICK_POWER_ON) diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 822237e322ba..481a98a10ecd 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -708,6 +708,25 @@ int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card) } EXPORT_SYMBOL_GPL(rtsx_pci_card_power_off); +int rtsx_pci_card_exclusive_check(struct rtsx_pcr *pcr, int card) +{ + unsigned int cd_mask[] = { + [RTSX_SD_CARD] = SD_EXIST, + [RTSX_MS_CARD] = MS_EXIST + }; + + if (!pcr->ms_pmos) { + /* When using single PMOS, accessing card is not permitted + * if the existing card is not the designated one. + */ + if (pcr->card_exist & (~cd_mask[card])) + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_pci_card_exclusive_check); + int rtsx_pci_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) { if (pcr->ops->switch_output_voltage) @@ -784,6 +803,9 @@ static void rtsx_pci_card_detect(struct work_struct *work) card_inserted = pcr->ops->cd_deglitch(pcr); card_detect = card_inserted | card_removed; + + pcr->card_exist |= card_inserted; + pcr->card_exist &= ~card_removed; } mutex_unlock(&pcr->pcr_mutex); @@ -976,6 +998,14 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) return err; } + /* No CD interrupt if probing driver with card inserted. + * So we need to initialize pcr->card_exist here. + */ + if (pcr->ops->cd_deglitch) + pcr->card_exist = pcr->ops->cd_deglitch(pcr); + else + pcr->card_exist = rtsx_pci_readl(pcr, RTSX_BIPR) & CARD_EXIST; + return 0; } diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index f74b5adca642..468c92303167 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -678,12 +678,19 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) struct mmc_command *cmd = mrq->cmd; struct mmc_data *data = mrq->data; unsigned int data_size = 0; + int err; if (host->eject) { cmd->error = -ENOMEDIUM; goto finish; } + err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD); + if (err) { + cmd->error = err; + goto finish; + } + mutex_lock(&pcr->pcr_mutex); rtsx_pci_start_run(pcr); @@ -901,6 +908,9 @@ static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (host->eject) return; + if (rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD)) + return; + mutex_lock(&pcr->pcr_mutex); rtsx_pci_start_run(pcr); @@ -1073,6 +1083,10 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) if (host->eject) return -ENOMEDIUM; + err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD); + if (err) + return err; + mutex_lock(&pcr->pcr_mutex); rtsx_pci_start_run(pcr); @@ -1122,6 +1136,10 @@ static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode) if (host->eject) return -ENOMEDIUM; + err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD); + if (err) + return err; + mutex_lock(&pcr->pcr_mutex); rtsx_pci_start_run(pcr); diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 5d9b81e8aff4..26ea7f1b7caf 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -740,6 +740,7 @@ struct rtsx_pcr { unsigned int card_inserted; unsigned int card_removed; + unsigned int card_exist; struct delayed_work carddet_work; struct delayed_work idle_work; @@ -804,6 +805,7 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk); int rtsx_pci_card_power_on(struct rtsx_pcr *pcr, int card); int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card); +int rtsx_pci_card_exclusive_check(struct rtsx_pcr *pcr, int card); int rtsx_pci_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage); unsigned int rtsx_pci_card_exist(struct rtsx_pcr *pcr); void rtsx_pci_complete_unfinished_transfer(struct rtsx_pcr *pcr); -- cgit v1.2.3-58-ga151