diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-03-08 19:02:58 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-03-08 19:02:58 +0100 |
commit | cf2e8c544cd3b33e9e403b7b72404c221bf888d1 (patch) | |
tree | 21b46d362626562921df14d75ee106259b3fa936 /drivers | |
parent | Merge tag 'backlight-next-5.1' of git://git.kernel.org/pub/scm/linux/kernel/g... (diff) | |
parent | mfd: mxs-lradc: Mark expected switch fall-through (diff) | |
download | linux-cf2e8c544cd3b33e9e403b7b72404c221bf888d1.tar.xz linux-cf2e8c544cd3b33e9e403b7b72404c221bf888d1.zip |
Merge tag 'mfd-next-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
Pull MFD updates from Lee Jones:
"New Drivers:
- Add STMPE ADC Input driver
- Add STMicroelectronics STPMIC1 Parent driver
- Add STMicroelectronics STPMIC1 OnKey Misc driver
- Add STMicroelectronics STPMIC1 Watchdog driver
- Add Cirrus Logic Lochnagar Parent driver
- Add TQ-Systems TQMX86 Parent driver
New Device Support:
- Add support for ADC to STMPE
New (or moved) Functionality:
- Move Lightbar functionality to its own driver; cros_ec_lightbar
- Move VBC functionality to its own driver; cros_ec_vbc
- Move VBC functionality to its own driver; cros_ec_vbc
- Move DebugFS functionality to its own driver; cros_ec_debugfs
- Move SYSFS functionality to its own driver; cros_ec_sysfs
- Add support for input voltage options; tps65218
Fixes:
- Use devm_* managed resources; cros_ec
- Device Tree documentation; stmpe, aspeed-lpc, lochnagar
- Trivial Clean-ups; stmpe
- Rip out broken modular code; aat2870-core, adp5520, as3711,
db8500-prcmu, htc-i2cpld, max8925-core, rc5t583, sta2x11-mfd,
syscon, tps65090, tps65910, tps68470 tps80031, wm831x-spi,
wm831x-i2c, wm831x-core, wm8350-i2c, wm8350-core, wm8400-core
- Kconfig fixups; INTEL_SOC_PMIC
- Improve error path; sm501, sec-core
- Use struct_size() helper; sm501
- Constify; at91-usart
- Use pointers instead of copying data; at91-usart
- Deliver proper return value; cros_ec_dev
- Trivial formatting/whitespace; sec-core"
* tag 'mfd-next-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (53 commits)
mfd: mxs-lradc: Mark expected switch fall-through
mfd: sec-core: Cleanup formatting to a consistent style
mfd: tqmx86: IO controller with I2C, Wachdog and GPIO
mfd: intel-lpss: Move linux/pm.h to the local header
mfd: cros_ec_dev: Return number of bytes read with CROS_EC_DEV_IOCRDMEM
mfd: tps68470: Drop unused MODULE_DEVICE_TABLE
mfd: at91-usart: No need to copy mfd_cell in probe
mfd: at91-usart: Constify at91_usart_spi_subdev and at91_usart_serial_subdev
mfd: lochnagar: Add support for the Cirrus Logic Lochnagar
mfd: lochnagar: Add initial binding documentation
dt-bindings: mfd: aspeed-lpc: Make parameter optional
mfd: sec-core: Return gracefully instead of BUG() if device cannot match
mfd: sm501: Use struct_size() in devm_kzalloc()
mfd: sm501: Fix potential NULL pointer dereference
mfd: Kconfig: Fix I2C_DESIGNWARE_PLATFORM dependencies
mfd: tps65218.c: Add input voltage options
mfd: wm8400-core: Make it explicitly non-modular
mfd: wm8350-core: Drop unused module infrastructure from non-modular code
mfd: wm8350-i2c: Make it explicitly non-modular
mfd: wm831x-core: Drop unused module infrastructure from non-modular code
...
Diffstat (limited to 'drivers')
55 files changed, 2191 insertions, 601 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 5debc67df70a..76db6e5cc296 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -817,6 +817,13 @@ config STM32_DFSDM_ADC This driver can also be built as a module. If so, the module will be called stm32-dfsdm-adc. +config STMPE_ADC + tristate "STMicroelectronics STMPE ADC driver" + depends on OF && MFD_STMPE + help + Say yes here to build support for ST Microelectronics STMPE + built-in ADC block (stmpe811). + config STX104 tristate "Apex Embedded Systems STX104 driver" depends on PC104 && X86 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index d50eb47da484..6fcebd167524 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -77,6 +77,7 @@ obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o obj-$(CONFIG_STM32_ADC) += stm32-adc.o obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o +obj-$(CONFIG_STMPE_ADC) += stmpe-adc.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o obj-$(CONFIG_TI_ADC084S021) += ti-adc084s021.o diff --git a/drivers/iio/adc/stmpe-adc.c b/drivers/iio/adc/stmpe-adc.c new file mode 100644 index 000000000000..37f4b74a5d32 --- /dev/null +++ b/drivers/iio/adc/stmpe-adc.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * STMicroelectronics STMPE811 IIO ADC Driver + * + * 4 channel, 10/12-bit ADC + * + * Copyright (C) 2013-2018 Toradex AG <stefan.agner@toradex.com> + */ + +#include <linux/completion.h> +#include <linux/err.h> +#include <linux/iio/iio.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/stmpe.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/device.h> + +#define STMPE_REG_INT_STA 0x0B +#define STMPE_REG_ADC_INT_EN 0x0E +#define STMPE_REG_ADC_INT_STA 0x0F + +#define STMPE_REG_ADC_CTRL1 0x20 +#define STMPE_REG_ADC_CTRL2 0x21 +#define STMPE_REG_ADC_CAPT 0x22 +#define STMPE_REG_ADC_DATA_CH(channel) (0x30 + 2 * (channel)) + +#define STMPE_REG_TEMP_CTRL 0x60 +#define STMPE_TEMP_CTRL_ENABLE BIT(0) +#define STMPE_TEMP_CTRL_ACQ BIT(1) +#define STMPE_TEMP_CTRL_THRES_EN BIT(3) +#define STMPE_START_ONE_TEMP_CONV (STMPE_TEMP_CTRL_ENABLE | \ + STMPE_TEMP_CTRL_ACQ | \ + STMPE_TEMP_CTRL_THRES_EN) +#define STMPE_REG_TEMP_DATA 0x61 +#define STMPE_REG_TEMP_TH 0x63 +#define STMPE_ADC_LAST_NR 7 +#define STMPE_TEMP_CHANNEL (STMPE_ADC_LAST_NR + 1) + +#define STMPE_ADC_CH(channel) ((1 << (channel)) & 0xff) + +#define STMPE_ADC_TIMEOUT msecs_to_jiffies(1000) + +struct stmpe_adc { + struct stmpe *stmpe; + struct clk *clk; + struct device *dev; + struct mutex lock; + + /* We are allocating plus one for the temperature channel */ + struct iio_chan_spec stmpe_adc_iio_channels[STMPE_ADC_LAST_NR + 2]; + + struct completion completion; + + u8 channel; + u32 value; +}; + +static int stmpe_read_voltage(struct stmpe_adc *info, + struct iio_chan_spec const *chan, int *val) +{ + long ret; + + mutex_lock(&info->lock); + + info->channel = (u8)chan->channel; + + if (info->channel > STMPE_ADC_LAST_NR) { + mutex_unlock(&info->lock); + return -EINVAL; + } + + stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_EN, + STMPE_ADC_CH(info->channel)); + + stmpe_reg_write(info->stmpe, STMPE_REG_ADC_CAPT, + STMPE_ADC_CH(info->channel)); + + *val = info->value; + + ret = wait_for_completion_interruptible_timeout + (&info->completion, STMPE_ADC_TIMEOUT); + + if (ret <= 0) { + mutex_unlock(&info->lock); + if (ret == 0) + return -ETIMEDOUT; + else + return ret; + } + + *val = info->value; + + mutex_unlock(&info->lock); + + return 0; +} + +static int stmpe_read_temp(struct stmpe_adc *info, + struct iio_chan_spec const *chan, int *val) +{ + long ret; + + mutex_lock(&info->lock); + + info->channel = (u8)chan->channel; + + if (info->channel != STMPE_TEMP_CHANNEL) { + mutex_unlock(&info->lock); + return -EINVAL; + } + + stmpe_reg_write(info->stmpe, STMPE_REG_TEMP_CTRL, + STMPE_START_ONE_TEMP_CONV); + + ret = wait_for_completion_interruptible_timeout + (&info->completion, STMPE_ADC_TIMEOUT); + + if (ret <= 0) { + mutex_unlock(&info->lock); + if (ret == 0) + return -ETIMEDOUT; + else + return ret; + } + + /* + * absolute temp = +V3.3 * value /7.51 [K] + * scale to [milli °C] + */ + *val = ((449960l * info->value) / 1024l) - 273150; + + mutex_unlock(&info->lock); + + return 0; +} + +static int stmpe_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct stmpe_adc *info = iio_priv(indio_dev); + long ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: + + switch (chan->type) { + case IIO_VOLTAGE: + ret = stmpe_read_voltage(info, chan, val); + break; + + case IIO_TEMP: + ret = stmpe_read_temp(info, chan, val); + break; + default: + return -EINVAL; + } + + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = 3300; + *val2 = info->stmpe->mod_12b ? 12 : 10; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + break; + } + + return -EINVAL; +} + +static irqreturn_t stmpe_adc_isr(int irq, void *dev_id) +{ + struct stmpe_adc *info = (struct stmpe_adc *)dev_id; + u16 data; + + if (info->channel > STMPE_TEMP_CHANNEL) + return IRQ_NONE; + + if (info->channel <= STMPE_ADC_LAST_NR) { + int int_sta; + + int_sta = stmpe_reg_read(info->stmpe, STMPE_REG_ADC_INT_STA); + + /* Is the interrupt relevant */ + if (!(int_sta & STMPE_ADC_CH(info->channel))) + return IRQ_NONE; + + /* Read value */ + stmpe_block_read(info->stmpe, + STMPE_REG_ADC_DATA_CH(info->channel), 2, (u8 *) &data); + + stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA, int_sta); + } else if (info->channel == STMPE_TEMP_CHANNEL) { + /* Read value */ + stmpe_block_read(info->stmpe, STMPE_REG_TEMP_DATA, 2, + (u8 *) &data); + } + + info->value = (u32) be16_to_cpu(data); + complete(&info->completion); + + return IRQ_HANDLED; +} + +static const struct iio_info stmpe_adc_iio_info = { + .read_raw = &stmpe_read_raw, +}; + +static void stmpe_adc_voltage_chan(struct iio_chan_spec *ics, int chan) +{ + ics->type = IIO_VOLTAGE; + ics->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + ics->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + ics->indexed = 1; + ics->channel = chan; +} + +static void stmpe_adc_temp_chan(struct iio_chan_spec *ics, int chan) +{ + ics->type = IIO_TEMP; + ics->info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED); + ics->indexed = 1; + ics->channel = chan; +} + +static int stmpe_adc_init_hw(struct stmpe_adc *adc) +{ + int ret; + struct stmpe *stmpe = adc->stmpe; + + ret = stmpe_enable(stmpe, STMPE_BLOCK_ADC); + if (ret) { + dev_err(stmpe->dev, "Could not enable clock for ADC\n"); + return ret; + } + + ret = stmpe811_adc_common_init(stmpe); + if (ret) { + stmpe_disable(stmpe, STMPE_BLOCK_ADC); + return ret; + } + + /* use temp irq for each conversion completion */ + stmpe_reg_write(stmpe, STMPE_REG_TEMP_TH, 0); + stmpe_reg_write(stmpe, STMPE_REG_TEMP_TH + 1, 0); + + return 0; +} + +static int stmpe_adc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct stmpe_adc *info; + struct device_node *np; + u32 norequest_mask = 0; + int irq_temp, irq_adc; + int num_chan = 0; + int i = 0; + int ret; + + irq_adc = platform_get_irq_byname(pdev, "STMPE_ADC"); + if (irq_adc < 0) + return irq_adc; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct stmpe_adc)); + if (!indio_dev) { + dev_err(&pdev->dev, "failed allocating iio device\n"); + return -ENOMEM; + } + + info = iio_priv(indio_dev); + mutex_init(&info->lock); + + init_completion(&info->completion); + ret = devm_request_threaded_irq(&pdev->dev, irq_adc, NULL, + stmpe_adc_isr, IRQF_ONESHOT, + "stmpe-adc", info); + if (ret < 0) { + dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", + irq_adc); + return ret; + } + + irq_temp = platform_get_irq_byname(pdev, "STMPE_TEMP_SENS"); + if (irq_temp >= 0) { + ret = devm_request_threaded_irq(&pdev->dev, irq_temp, NULL, + stmpe_adc_isr, IRQF_ONESHOT, + "stmpe-adc", info); + if (ret < 0) + dev_warn(&pdev->dev, "failed requesting irq for" + " temp sensor, irq = %d\n", irq_temp); + } + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &stmpe_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + info->stmpe = dev_get_drvdata(pdev->dev.parent); + + np = pdev->dev.of_node; + + if (!np) + dev_err(&pdev->dev, "no device tree node found\n"); + + of_property_read_u32(np, "st,norequest-mask", &norequest_mask); + + for_each_clear_bit(i, (unsigned long *) &norequest_mask, + (STMPE_ADC_LAST_NR + 1)) { + stmpe_adc_voltage_chan(&info->stmpe_adc_iio_channels[num_chan], i); + num_chan++; + } + stmpe_adc_temp_chan(&info->stmpe_adc_iio_channels[num_chan], i); + num_chan++; + indio_dev->channels = info->stmpe_adc_iio_channels; + indio_dev->num_channels = num_chan; + + ret = stmpe_adc_init_hw(info); + if (ret) + return ret; + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static int __maybe_unused stmpe_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct stmpe_adc *info = iio_priv(indio_dev); + + stmpe_adc_init_hw(info); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(stmpe_adc_pm_ops, NULL, stmpe_adc_resume); + +static struct platform_driver stmpe_adc_driver = { + .probe = stmpe_adc_probe, + .driver = { + .name = "stmpe-adc", + .pm = &stmpe_adc_pm_ops, + }, +}; + +module_platform_driver(stmpe_adc_driver); + +MODULE_AUTHOR("Stefan Agner <stefan.agner@toradex.com>"); +MODULE_DESCRIPTION("STMPEXXX ADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:stmpe-adc"); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index ca59a2be9bc5..279fb02a0f14 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -851,4 +851,15 @@ config INPUT_SC27XX_VIBRA To compile this driver as a module, choose M here. The module will be called sc27xx_vibra. +config INPUT_STPMIC1_ONKEY + tristate "STPMIC1 PMIC Onkey support" + depends on MFD_STPMIC1 + help + Say Y to enable support of onkey embedded into STPMIC1 PMIC. onkey + can be used to wakeup from low power modes and force a shut-down on + long press. + + To compile this driver as a module, choose M here: the + module will be called stpmic1_onkey. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 9d0f9d1ff68f..1b44202ad8f7 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o +obj-$(CONFIG_INPUT_STPMIC1_ONKEY) += stpmic1_onkey.o obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON) += tps65218-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o @@ -81,3 +82,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o + diff --git a/drivers/input/misc/stpmic1_onkey.c b/drivers/input/misc/stpmic1_onkey.c new file mode 100644 index 000000000000..7b49c9997df7 --- /dev/null +++ b/drivers/input/misc/stpmic1_onkey.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) STMicroelectronics 2018 +// Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics. + +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/mfd/stpmic1.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> + +/** + * struct stpmic1_onkey - OnKey data + * @input_dev: pointer to input device + * @irq_falling: irq that we are hooked on to + * @irq_rising: irq that we are hooked on to + */ +struct stpmic1_onkey { + struct input_dev *input_dev; + int irq_falling; + int irq_rising; +}; + +static irqreturn_t onkey_falling_irq(int irq, void *ponkey) +{ + struct stpmic1_onkey *onkey = ponkey; + struct input_dev *input_dev = onkey->input_dev; + + input_report_key(input_dev, KEY_POWER, 1); + pm_wakeup_event(input_dev->dev.parent, 0); + input_sync(input_dev); + + return IRQ_HANDLED; +} + +static irqreturn_t onkey_rising_irq(int irq, void *ponkey) +{ + struct stpmic1_onkey *onkey = ponkey; + struct input_dev *input_dev = onkey->input_dev; + + input_report_key(input_dev, KEY_POWER, 0); + pm_wakeup_event(input_dev->dev.parent, 0); + input_sync(input_dev); + + return IRQ_HANDLED; +} + +static int stpmic1_onkey_probe(struct platform_device *pdev) +{ + struct stpmic1 *pmic = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct input_dev *input_dev; + struct stpmic1_onkey *onkey; + unsigned int val, reg = 0; + int error; + + onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL); + if (!onkey) + return -ENOMEM; + + onkey->irq_falling = platform_get_irq_byname(pdev, "onkey-falling"); + if (onkey->irq_falling < 0) { + dev_err(dev, "failed: request IRQ onkey-falling %d\n", + onkey->irq_falling); + return onkey->irq_falling; + } + + onkey->irq_rising = platform_get_irq_byname(pdev, "onkey-rising"); + if (onkey->irq_rising < 0) { + dev_err(dev, "failed: request IRQ onkey-rising %d\n", + onkey->irq_rising); + return onkey->irq_rising; + } + + if (!device_property_read_u32(dev, "power-off-time-sec", &val)) { + if (val > 0 && val <= 16) { + dev_dbg(dev, "power-off-time=%d seconds\n", val); + reg |= PONKEY_PWR_OFF; + reg |= ((16 - val) & PONKEY_TURNOFF_TIMER_MASK); + } else { + dev_err(dev, "power-off-time-sec out of range\n"); + return -EINVAL; + } + } + + if (device_property_present(dev, "st,onkey-clear-cc-flag")) + reg |= PONKEY_CC_FLAG_CLEAR; + + error = regmap_update_bits(pmic->regmap, PKEY_TURNOFF_CR, + PONKEY_TURNOFF_MASK, reg); + if (error) { + dev_err(dev, "PKEY_TURNOFF_CR write failed: %d\n", error); + return error; + } + + if (device_property_present(dev, "st,onkey-pu-inactive")) { + error = regmap_update_bits(pmic->regmap, PADS_PULL_CR, + PONKEY_PU_INACTIVE, + PONKEY_PU_INACTIVE); + if (error) { + dev_err(dev, "ONKEY Pads configuration failed: %d\n", + error); + return error; + } + } + + input_dev = devm_input_allocate_device(dev); + if (!input_dev) { + dev_err(dev, "Can't allocate Pwr Onkey Input Device\n"); + return -ENOMEM; + } + + input_dev->name = "pmic_onkey"; + input_dev->phys = "pmic_onkey/input0"; + + input_set_capability(input_dev, EV_KEY, KEY_POWER); + + onkey->input_dev = input_dev; + + /* interrupt is nested in a thread */ + error = devm_request_threaded_irq(dev, onkey->irq_falling, NULL, + onkey_falling_irq, IRQF_ONESHOT, + dev_name(dev), onkey); + if (error) { + dev_err(dev, "Can't get IRQ Onkey Falling: %d\n", error); + return error; + } + + error = devm_request_threaded_irq(dev, onkey->irq_rising, NULL, + onkey_rising_irq, IRQF_ONESHOT, + dev_name(dev), onkey); + if (error) { + dev_err(dev, "Can't get IRQ Onkey Rising: %d\n", error); + return error; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(dev, "Can't register power button: %d\n", error); + return error; + } + + platform_set_drvdata(pdev, onkey); + device_init_wakeup(dev, true); + + return 0; +} + +static int __maybe_unused stpmic1_onkey_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct stpmic1_onkey *onkey = platform_get_drvdata(pdev); + + if (device_may_wakeup(dev)) { + enable_irq_wake(onkey->irq_falling); + enable_irq_wake(onkey->irq_rising); + } + return 0; +} + +static int __maybe_unused stpmic1_onkey_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct stpmic1_onkey *onkey = platform_get_drvdata(pdev); + + if (device_may_wakeup(dev)) { + disable_irq_wake(onkey->irq_falling); + disable_irq_wake(onkey->irq_rising); + } + return 0; +} + +static SIMPLE_DEV_PM_OPS(stpmic1_onkey_pm, + stpmic1_onkey_suspend, + stpmic1_onkey_resume); + +static const struct of_device_id of_stpmic1_onkey_match[] = { + { .compatible = "st,stpmic1-onkey" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, of_stpmic1_onkey_match); + +static struct platform_driver stpmic1_onkey_driver = { + .probe = stpmic1_onkey_probe, + .driver = { + .name = "stpmic1_onkey", + .of_match_table = of_match_ptr(of_stpmic1_onkey_match), + .pm = &stpmic1_onkey_pm, + }, +}; +module_platform_driver(stpmic1_onkey_driver); + +MODULE_DESCRIPTION("Onkey driver for STPMIC1"); +MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index 2a78e27b4495..cf9c9aa39f6e 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -30,8 +30,6 @@ * with touchscreen controller */ #define STMPE_REG_INT_STA 0x0B -#define STMPE_REG_ADC_CTRL1 0x20 -#define STMPE_REG_ADC_CTRL2 0x21 #define STMPE_REG_TSC_CTRL 0x40 #define STMPE_REG_TSC_CFG 0x41 #define STMPE_REG_FIFO_TH 0x4A @@ -49,17 +47,6 @@ #define STMPE_IRQ_TOUCH_DET 0 -#define SAMPLE_TIME(x) ((x & 0xf) << 4) -#define MOD_12B(x) ((x & 0x1) << 3) -#define REF_SEL(x) ((x & 0x1) << 1) -#define ADC_FREQ(x) (x & 0x3) -#define AVE_CTRL(x) ((x & 0x3) << 6) -#define DET_DELAY(x) ((x & 0x7) << 3) -#define SETTLING(x) (x & 0x7) -#define FRACTION_Z(x) (x & 0x7) -#define I_DRIVE(x) (x & 0x1) -#define OP_MODE(x) ((x & 0x7) << 1) - #define STMPE_TS_NAME "stmpe-ts" #define XY_MASK 0xfff @@ -69,15 +56,6 @@ * @idev: registered input device * @work: a work item used to scan the device * @dev: a pointer back to the MFD cell struct device* - * @sample_time: ADC converstion time in number of clock. - * (0 -> 36 clocks, 1 -> 44 clocks, 2 -> 56 clocks, 3 -> 64 clocks, - * 4 -> 80 clocks, 5 -> 96 clocks, 6 -> 144 clocks), - * recommended is 4. - * @mod_12b: ADC Bit mode (0 -> 10bit ADC, 1 -> 12bit ADC) - * @ref_sel: ADC reference source - * (0 -> internal reference, 1 -> external reference) - * @adc_freq: ADC Clock speed - * (0 -> 1.625 MHz, 1 -> 3.25 MHz, 2 || 3 -> 6.5 MHz) * @ave_ctrl: Sample average control * (0 -> 1 sample, 1 -> 2 samples, 2 -> 4 samples, 3 -> 8 samples) * @touch_det_delay: Touch detect interrupt delay @@ -99,10 +77,6 @@ struct stmpe_touch { struct input_dev *idev; struct delayed_work work; struct device *dev; - u8 sample_time; - u8 mod_12b; - u8 ref_sel; - u8 adc_freq; u8 ave_ctrl; u8 touch_det_delay; u8 settling; @@ -203,7 +177,7 @@ static irqreturn_t stmpe_ts_handler(int irq, void *data) static int stmpe_init_hw(struct stmpe_touch *ts) { int ret; - u8 adc_ctrl1, adc_ctrl1_mask, tsc_cfg, tsc_cfg_mask; + u8 tsc_cfg, tsc_cfg_mask; struct stmpe *stmpe = ts->stmpe; struct device *dev = ts->dev; @@ -213,27 +187,17 @@ static int stmpe_init_hw(struct stmpe_touch *ts) return ret; } - adc_ctrl1 = SAMPLE_TIME(ts->sample_time) | MOD_12B(ts->mod_12b) | - REF_SEL(ts->ref_sel); - adc_ctrl1_mask = SAMPLE_TIME(0xff) | MOD_12B(0xff) | REF_SEL(0xff); - - ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL1, - adc_ctrl1_mask, adc_ctrl1); - if (ret) { - dev_err(dev, "Could not setup ADC\n"); - return ret; - } - - ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL2, - ADC_FREQ(0xff), ADC_FREQ(ts->adc_freq)); + ret = stmpe811_adc_common_init(stmpe); if (ret) { - dev_err(dev, "Could not setup ADC\n"); + stmpe_disable(stmpe, STMPE_BLOCK_TOUCHSCREEN | STMPE_BLOCK_ADC); return ret; } - tsc_cfg = AVE_CTRL(ts->ave_ctrl) | DET_DELAY(ts->touch_det_delay) | - SETTLING(ts->settling); - tsc_cfg_mask = AVE_CTRL(0xff) | DET_DELAY(0xff) | SETTLING(0xff); + tsc_cfg = STMPE_AVE_CTRL(ts->ave_ctrl) | + STMPE_DET_DELAY(ts->touch_det_delay) | + STMPE_SETTLING(ts->settling); + tsc_cfg_mask = STMPE_AVE_CTRL(0xff) | STMPE_DET_DELAY(0xff) | + STMPE_SETTLING(0xff); ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CFG, tsc_cfg_mask, tsc_cfg); if (ret) { @@ -242,14 +206,14 @@ static int stmpe_init_hw(struct stmpe_touch *ts) } ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_FRACTION_Z, - FRACTION_Z(0xff), FRACTION_Z(ts->fraction_z)); + STMPE_FRACTION_Z(0xff), STMPE_FRACTION_Z(ts->fraction_z)); if (ret) { dev_err(dev, "Could not config touch\n"); return ret; } ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_I_DRIVE, - I_DRIVE(0xff), I_DRIVE(ts->i_drive)); + STMPE_I_DRIVE(0xff), STMPE_I_DRIVE(ts->i_drive)); if (ret) { dev_err(dev, "Could not config touch\n"); return ret; @@ -263,7 +227,7 @@ static int stmpe_init_hw(struct stmpe_touch *ts) } ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CTRL, - OP_MODE(0xff), OP_MODE(OP_MOD_XYZ)); + STMPE_OP_MODE(0xff), STMPE_OP_MODE(OP_MOD_XYZ)); if (ret) { dev_err(dev, "Could not set mode\n"); return ret; @@ -303,13 +267,13 @@ static void stmpe_ts_get_platform_info(struct platform_device *pdev, if (np) { if (!of_property_read_u32(np, "st,sample-time", &val)) - ts->sample_time = val; + ts->stmpe->sample_time = val; if (!of_property_read_u32(np, "st,mod-12b", &val)) - ts->mod_12b = val; + ts->stmpe->mod_12b = val; if (!of_property_read_u32(np, "st,ref-sel", &val)) - ts->ref_sel = val; + ts->stmpe->ref_sel = val; if (!of_property_read_u32(np, "st,adc-freq", &val)) - ts->adc_freq = val; + ts->stmpe->adc_freq = val; if (!of_property_read_u32(np, "st,ave-ctrl", &val)) ts->ave_ctrl = val; if (!of_property_read_u32(np, "st,touch-det-delay", &val)) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 76f9909cf396..1b50fa955992 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -215,7 +215,6 @@ config MFD_CROS_EC config MFD_CROS_EC_CHARDEV tristate "Chrome OS Embedded Controller userspace device interface" depends on MFD_CROS_EC - select CROS_EC_CTL ---help--- This driver adds support to talk with the ChromeOS EC from userspace. @@ -519,10 +518,10 @@ config INTEL_SOC_PMIC bool "Support for Crystal Cove PMIC" depends on ACPI && HAS_IOMEM && I2C=y && GPIOLIB && COMMON_CLK depends on X86 || COMPILE_TEST + depends on I2C_DESIGNWARE_PLATFORM=y select MFD_CORE select REGMAP_I2C select REGMAP_IRQ - select I2C_DESIGNWARE_PLATFORM help Select this option to enable support for Crystal Cove PMIC on some Intel SoC systems. The PMIC provides ADC, GPIO, @@ -548,10 +547,10 @@ config INTEL_SOC_PMIC_CHTWC bool "Support for Intel Cherry Trail Whiskey Cove PMIC" depends on ACPI && HAS_IOMEM && I2C=y && COMMON_CLK depends on X86 || COMPILE_TEST + depends on I2C_DESIGNWARE_PLATFORM=y select MFD_CORE select REGMAP_I2C select REGMAP_IRQ - select I2C_DESIGNWARE_PLATFORM help Select this option to enable support for the Intel Cherry Trail Whiskey Cove PMIC found on some Intel Cherry Trail systems. @@ -1205,7 +1204,7 @@ config MFD_STMPE Currently supported devices are: - STMPE811: GPIO, Touchscreen + STMPE811: GPIO, Touchscreen, ADC STMPE1601: GPIO, Keypad STMPE1801: GPIO, Keypad STMPE2401: GPIO, Keypad @@ -1218,6 +1217,7 @@ config MFD_STMPE GPIO: stmpe-gpio Keypad: stmpe-keypad Touchscreen: stmpe-ts + ADC: stmpe-adc menu "STMicroelectronics STMPE Interface Drivers" depends on MFD_STMPE @@ -1420,9 +1420,9 @@ config MFD_TPS65217 config MFD_TPS68470 bool "TI TPS68470 Power Management / LED chips" depends on ACPI && PCI && I2C=y + depends on I2C_DESIGNWARE_PLATFORM=y select MFD_CORE select REGMAP_I2C - select I2C_DESIGNWARE_PLATFORM help If you say yes here you get support for the TPS68470 series of Power Management / LED chips. @@ -1677,6 +1677,14 @@ config MFD_TC6393XB help Support for Toshiba Mobile IO Controller TC6393XB +config MFD_TQMX86 + tristate "TQ-Systems IO controller TQMX86" + select MFD_CORE + help + Say yes here to enable support for various functions of the + TQ-Systems IO controller and watchdog device, found on their + ComExpress CPU modules. + config MFD_VX855 tristate "VIA VX855/VX875 integrated south bridge" depends on PCI @@ -1686,6 +1694,14 @@ config MFD_VX855 VIA VX855/VX875 south bridge. You will need to enable the vx855_spi and/or vx855_gpio drivers for this to do anything useful. +config MFD_LOCHNAGAR + bool "Cirrus Logic Lochnagar Audio Development Board" + select MFD_CORE + select REGMAP_I2C + depends on I2C=y && OF + help + Support for Cirrus Logic Lochnagar audio development board. + config MFD_ARIZONA select REGMAP select REGMAP_IRQ @@ -1872,6 +1888,22 @@ config MFD_STM32_TIMERS for PWM and IIO Timer. This driver allow to share the registers between the others drivers. +config MFD_STPMIC1 + tristate "Support for STPMIC1 PMIC" + depends on (I2C=y && OF) + select REGMAP_I2C + select REGMAP_IRQ + select MFD_CORE + help + Support for ST Microelectronics STPMIC1 PMIC. STPMIC1 has power on + key, watchdog and regulator functionalities which are supported via + the relevant subsystems. This driver provides core support for the + STPMIC1. In order to use the actual functionaltiy of the device other + drivers must be enabled. + + To compile this driver as a module, choose M here: the + module will be called stpmic1. + menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index ee6fb6af655e..b4569ed7f3f3 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -37,6 +37,9 @@ 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 +obj-$(CONFIG_MFD_TQMX86) += tqmx86.o + +obj-$(CONFIG_MFD_LOCHNAGAR) += lochnagar-i2c.o obj-$(CONFIG_MFD_ARIZONA) += arizona-core.o obj-$(CONFIG_MFD_ARIZONA) += arizona-irq.o @@ -234,6 +237,7 @@ obj-$(CONFIG_INTEL_SOC_PMIC_CHTDC_TI) += intel_soc_pmic_chtdc_ti.o obj-$(CONFIG_MFD_MT6397) += mt6397-core.o obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o +obj-$(CONFIG_MFD_STPMIC1) += stpmic1.o obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c index 3ba19a45f199..9d3d90d386c2 100644 --- a/drivers/mfd/aat2870-core.c +++ b/drivers/mfd/aat2870-core.c @@ -20,7 +20,6 @@ */ #include <linux/kernel.h> -#include <linux/module.h> #include <linux/init.h> #include <linux/debugfs.h> #include <linux/slab.h> @@ -349,18 +348,10 @@ static void aat2870_init_debugfs(struct aat2870_data *aat2870) "Failed to create debugfs register file\n"); } -static void aat2870_uninit_debugfs(struct aat2870_data *aat2870) -{ - debugfs_remove_recursive(aat2870->dentry_root); -} #else static inline void aat2870_init_debugfs(struct aat2870_data *aat2870) { } - -static inline void aat2870_uninit_debugfs(struct aat2870_data *aat2870) -{ -} #endif /* CONFIG_DEBUG_FS */ static int aat2870_i2c_probe(struct i2c_client *client, @@ -440,20 +431,6 @@ out_disable: return ret; } -static int aat2870_i2c_remove(struct i2c_client *client) -{ - struct aat2870_data *aat2870 = i2c_get_clientdata(client); - - aat2870_uninit_debugfs(aat2870); - - mfd_remove_devices(aat2870->dev); - aat2870_disable(aat2870); - if (aat2870->uninit) - aat2870->uninit(aat2870); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static int aat2870_i2c_suspend(struct device *dev) { @@ -492,15 +469,14 @@ static const struct i2c_device_id aat2870_i2c_id_table[] = { { "aat2870", 0 }, { } }; -MODULE_DEVICE_TABLE(i2c, aat2870_i2c_id_table); static struct i2c_driver aat2870_i2c_driver = { .driver = { - .name = "aat2870", - .pm = &aat2870_pm_ops, + .name = "aat2870", + .pm = &aat2870_pm_ops, + .suppress_bind_attrs = true, }, .probe = aat2870_i2c_probe, - .remove = aat2870_i2c_remove, .id_table = aat2870_i2c_id_table, }; @@ -509,13 +485,3 @@ static int __init aat2870_init(void) return i2c_add_driver(&aat2870_i2c_driver); } subsys_initcall(aat2870_init); - -static void __exit aat2870_exit(void) -{ - i2c_del_driver(&aat2870_i2c_driver); -} -module_exit(aat2870_exit); - -MODULE_DESCRIPTION("Core support for the AnalogicTech AAT2870"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>"); diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c index be0497b96720..2cdd39cb8a18 100644 --- a/drivers/mfd/adp5520.c +++ b/drivers/mfd/adp5520.c @@ -7,6 +7,8 @@ * * Copyright 2009 Analog Devices Inc. * + * Author: Michael Hennerich <michael.hennerich@analog.com> + * * Derived from da903x: * Copyright (C) 2008 Compulab, Ltd. * Mike Rapoport <mike@compulab.co.il> @@ -18,7 +20,7 @@ */ #include <linux/kernel.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/interrupt.h> @@ -304,18 +306,6 @@ out_free_irq: return ret; } -static int adp5520_remove(struct i2c_client *client) -{ - struct adp5520_chip *chip = dev_get_drvdata(&client->dev); - - if (chip->irq) - free_irq(chip->irq, chip); - - adp5520_remove_subdevs(chip); - adp5520_write(chip->dev, ADP5520_MODE_STATUS, 0); - return 0; -} - #ifdef CONFIG_PM_SLEEP static int adp5520_suspend(struct device *dev) { @@ -346,20 +336,14 @@ static const struct i2c_device_id adp5520_id[] = { { "pmic-adp5501", ID_ADP5501 }, { } }; -MODULE_DEVICE_TABLE(i2c, adp5520_id); static struct i2c_driver adp5520_driver = { .driver = { - .name = "adp5520", - .pm = &adp5520_pm, + .name = "adp5520", + .pm = &adp5520_pm, + .suppress_bind_attrs = true, }, .probe = adp5520_probe, - .remove = adp5520_remove, .id_table = adp5520_id, }; - -module_i2c_driver(adp5520_driver); - -MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); -MODULE_DESCRIPTION("ADP5520(01) PMIC-MFD Driver"); -MODULE_LICENSE("GPL"); +builtin_i2c_driver(adp5520_driver); diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c index 67b12417585d..7a74a874b93c 100644 --- a/drivers/mfd/as3711.c +++ b/drivers/mfd/as3711.c @@ -16,7 +16,6 @@ #include <linux/kernel.h> #include <linux/mfd/as3711.h> #include <linux/mfd/core.h> -#include <linux/module.h> #include <linux/of.h> #include <linux/regmap.h> #include <linux/slab.h> @@ -118,7 +117,6 @@ static const struct of_device_id as3711_of_match[] = { {.compatible = "ams,as3711",}, {} }; -MODULE_DEVICE_TABLE(of, as3711_of_match); #endif static int as3711_i2c_probe(struct i2c_client *client, @@ -202,8 +200,6 @@ static const struct i2c_device_id as3711_i2c_id[] = { {} }; -MODULE_DEVICE_TABLE(i2c, as3711_i2c_id); - static struct i2c_driver as3711_i2c_driver = { .driver = { .name = "as3711", @@ -219,13 +215,3 @@ static int __init as3711_i2c_init(void) } /* Initialise early */ subsys_initcall(as3711_i2c_init); - -static void __exit as3711_i2c_exit(void) -{ - i2c_del_driver(&as3711_i2c_driver); -} -module_exit(as3711_i2c_exit); - -MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); -MODULE_DESCRIPTION("AS3711 PMIC driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/at91-usart.c b/drivers/mfd/at91-usart.c index d20747f612c1..6a8351a4588e 100644 --- a/drivers/mfd/at91-usart.c +++ b/drivers/mfd/at91-usart.c @@ -15,29 +15,29 @@ #include <linux/of.h> #include <linux/property.h> -static struct mfd_cell at91_usart_spi_subdev = { - .name = "at91_usart_spi", - .of_compatible = "microchip,at91sam9g45-usart-spi", - }; +static const struct mfd_cell at91_usart_spi_subdev = { + .name = "at91_usart_spi", + .of_compatible = "microchip,at91sam9g45-usart-spi", +}; -static struct mfd_cell at91_usart_serial_subdev = { - .name = "atmel_usart_serial", - .of_compatible = "atmel,at91rm9200-usart-serial", - }; +static const struct mfd_cell at91_usart_serial_subdev = { + .name = "atmel_usart_serial", + .of_compatible = "atmel,at91rm9200-usart-serial", +}; static int at91_usart_mode_probe(struct platform_device *pdev) { - struct mfd_cell cell; + const struct mfd_cell *cell; u32 opmode = AT91_USART_MODE_SERIAL; device_property_read_u32(&pdev->dev, "atmel,usart-mode", &opmode); switch (opmode) { case AT91_USART_MODE_SPI: - cell = at91_usart_spi_subdev; + cell = &at91_usart_spi_subdev; break; case AT91_USART_MODE_SERIAL: - cell = at91_usart_serial_subdev; + cell = &at91_usart_serial_subdev; break; default: dev_err(&pdev->dev, "atmel,usart-mode has an invalid value %u\n", @@ -45,7 +45,7 @@ static int at91_usart_mode_probe(struct platform_device *pdev) return -EINVAL; } - return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, &cell, 1, + return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, cell, 1, NULL, 0, NULL); } diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index fe6f83766144..6acfe036d522 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -129,8 +129,8 @@ int cros_ec_register(struct cros_ec_device *ec_dev) } } - err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, 1, - NULL, ec_dev->irq, NULL); + err = devm_mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, + 1, NULL, ec_dev->irq, NULL); if (err) { dev_err(dev, "Failed to register Embedded Controller subdevice %d\n", @@ -147,7 +147,7 @@ int cros_ec_register(struct cros_ec_device *ec_dev) * - the EC is responsive at init time (it is not true for a * sensor hub. */ - err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, + err = devm_mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_pd_cell, 1, NULL, ec_dev->irq, NULL); if (err) { dev_err(dev, @@ -181,14 +181,6 @@ int cros_ec_register(struct cros_ec_device *ec_dev) } EXPORT_SYMBOL(cros_ec_register); -int cros_ec_remove(struct cros_ec_device *ec_dev) -{ - mfd_remove_devices(ec_dev->dev); - - return 0; -} -EXPORT_SYMBOL(cros_ec_remove); - #ifdef CONFIG_PM_SLEEP int cros_ec_suspend(struct cros_ec_device *ec_dev) { diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index 2d0fee488c5a..d275deaecb12 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -21,6 +21,7 @@ #include <linux/mfd/core.h> #include <linux/module.h> #include <linux/mod_devicetable.h> +#include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/slab.h> @@ -34,17 +35,9 @@ #define CROS_MAX_DEV 128 static int ec_major; -static const struct attribute_group *cros_ec_groups[] = { - &cros_ec_attr_group, - &cros_ec_lightbar_attr_group, - &cros_ec_vbc_attr_group, - NULL, -}; - static struct class cros_class = { .owner = THIS_MODULE, .name = "chromeos", - .dev_groups = cros_ec_groups, }; /* Basic communication */ @@ -231,7 +224,7 @@ static long ec_device_ioctl_readmem(struct cros_ec_dev *ec, void __user *arg) if (copy_to_user((void __user *)arg, &s_mem, sizeof(s_mem))) return -EFAULT; - return 0; + return num; } static long ec_device_ioctl(struct file *filp, unsigned int cmd, @@ -395,9 +388,20 @@ static const struct mfd_cell cros_usbpd_charger_cells[] = { { .name = "cros-usbpd-charger" } }; +static const struct mfd_cell cros_ec_platform_cells[] = { + { .name = "cros-ec-debugfs" }, + { .name = "cros-ec-lightbar" }, + { .name = "cros-ec-sysfs" }, +}; + +static const struct mfd_cell cros_ec_vbc_cells[] = { + { .name = "cros-ec-vbc" } +}; + static int ec_device_probe(struct platform_device *pdev) { int retval = -ENOMEM; + struct device_node *node; struct device *dev = &pdev->dev; struct cros_ec_platform *ec_platform = dev_get_platdata(dev); struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL); @@ -470,9 +474,6 @@ static int ec_device_probe(struct platform_device *pdev) retval); } - /* Take control of the lightbar from the EC. */ - lb_manual_suspend_ctrl(ec, 1); - /* We can now add the sysfs class, we know which parameter to show */ retval = cdev_device_add(&ec->cdev, &ec->class_dev); if (retval) { @@ -480,8 +481,26 @@ static int ec_device_probe(struct platform_device *pdev) goto failed; } - if (cros_ec_debugfs_init(ec)) - dev_warn(dev, "failed to create debugfs directory\n"); + retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, + cros_ec_platform_cells, + ARRAY_SIZE(cros_ec_platform_cells), + NULL, 0, NULL); + if (retval) + dev_warn(ec->dev, + "failed to add cros-ec platform devices: %d\n", + retval); + + /* Check whether this EC instance has a VBC NVRAM */ + node = ec->ec_dev->dev->of_node; + if (of_property_read_bool(node, "google,has-vbc-nvram")) { + retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, + cros_ec_vbc_cells, + ARRAY_SIZE(cros_ec_vbc_cells), + NULL, 0, NULL); + if (retval) + dev_warn(ec->dev, "failed to add VBC devices: %d\n", + retval); + } return 0; @@ -494,69 +513,25 @@ static int ec_device_remove(struct platform_device *pdev) { struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); - /* Let the EC take over the lightbar again. */ - lb_manual_suspend_ctrl(ec, 0); - - cros_ec_debugfs_remove(ec); - mfd_remove_devices(ec->dev); cdev_del(&ec->cdev); device_unregister(&ec->class_dev); return 0; } -static void ec_device_shutdown(struct platform_device *pdev) -{ - struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); - - /* Be sure to clear up debugfs delayed works */ - cros_ec_debugfs_remove(ec); -} - static const struct platform_device_id cros_ec_id[] = { { DRV_NAME, 0 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, cros_ec_id); -static __maybe_unused int ec_device_suspend(struct device *dev) -{ - struct cros_ec_dev *ec = dev_get_drvdata(dev); - - cros_ec_debugfs_suspend(ec); - - lb_suspend(ec); - - return 0; -} - -static __maybe_unused int ec_device_resume(struct device *dev) -{ - struct cros_ec_dev *ec = dev_get_drvdata(dev); - - cros_ec_debugfs_resume(ec); - - lb_resume(ec); - - return 0; -} - -static const struct dev_pm_ops cros_ec_dev_pm_ops = { -#ifdef CONFIG_PM_SLEEP - .suspend = ec_device_suspend, - .resume = ec_device_resume, -#endif -}; - static struct platform_driver cros_ec_dev_driver = { .driver = { .name = DRV_NAME, - .pm = &cros_ec_dev_pm_ops, }, .id_table = cros_ec_id, .probe = ec_device_probe, .remove = ec_device_remove, - .shutdown = ec_device_shutdown, }; static int __init cros_ec_dev_init(void) diff --git a/drivers/mfd/cros_ec_dev.h b/drivers/mfd/cros_ec_dev.h index 978d836a0248..ec750433455a 100644 --- a/drivers/mfd/cros_ec_dev.h +++ b/drivers/mfd/cros_ec_dev.h @@ -44,10 +44,4 @@ struct cros_ec_readmem { #define CROS_EC_DEV_IOCXCMD _IOWR(CROS_EC_DEV_IOC, 0, struct cros_ec_command) #define CROS_EC_DEV_IOCRDMEM _IOWR(CROS_EC_DEV_IOC, 1, struct cros_ec_readmem) -/* Lightbar utilities */ -extern bool ec_has_lightbar(struct cros_ec_dev *ec); -extern int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable); -extern int lb_suspend(struct cros_ec_dev *ec); -extern int lb_resume(struct cros_ec_dev *ec); - #endif /* _CROS_EC_DEV_H_ */ diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index aec20e1c7d3d..65666b624ae8 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -1,4 +1,6 @@ /* + * DB8500 PRCM Unit driver + * * Copyright (C) STMicroelectronics 2009 * Copyright (C) ST-Ericsson SA 2010 * @@ -10,7 +12,8 @@ * U8500 PRCM Unit interface driver * */ -#include <linux/module.h> +#include <linux/init.h> +#include <linux/export.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/errno.h> @@ -3188,9 +3191,4 @@ static int __init db8500_prcmu_init(void) { return platform_driver_register(&db8500_prcmu_driver); } - core_initcall(db8500_prcmu_init); - -MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com>"); -MODULE_DESCRIPTION("DB8500 PRCM Unit driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/htc-i2cpld.c b/drivers/mfd/htc-i2cpld.c index 01572b5e79e8..af3c66355270 100644 --- a/drivers/mfd/htc-i2cpld.c +++ b/drivers/mfd/htc-i2cpld.c @@ -27,7 +27,6 @@ #include <linux/kernel.h> #include <linux/init.h> -#include <linux/module.h> #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/i2c.h> @@ -614,8 +613,6 @@ static const struct i2c_device_id htcpld_chip_id[] = { { "htcpld-chip", 0 }, { } }; -MODULE_DEVICE_TABLE(i2c, htcpld_chip_id); - static struct i2c_driver htcpld_chip_driver = { .driver = { @@ -643,17 +640,4 @@ static int __init htcpld_core_init(void) /* Probe for our chips */ return platform_driver_probe(&htcpld_core_driver, htcpld_core_probe); } - -static void __exit htcpld_core_exit(void) -{ - i2c_del_driver(&htcpld_chip_driver); - platform_driver_unregister(&htcpld_core_driver); -} - -module_init(htcpld_core_init); -module_exit(htcpld_core_exit); - -MODULE_AUTHOR("Cory Maccarrone <darkstar6262@gmail.com>"); -MODULE_DESCRIPTION("I2C HTC PLD Driver"); -MODULE_LICENSE("GPL"); - +device_initcall(htcpld_core_init); diff --git a/drivers/mfd/intel-lpss-acpi.c b/drivers/mfd/intel-lpss-acpi.c index 7911b0a14a6d..6d9f03363ee7 100644 --- a/drivers/mfd/intel-lpss-acpi.c +++ b/drivers/mfd/intel-lpss-acpi.c @@ -15,7 +15,6 @@ #include <linux/ioport.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/platform_device.h> #include <linux/property.h> diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index 0e5282fc1467..cba2eb166650 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -15,7 +15,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/property.h> diff --git a/drivers/mfd/intel-lpss.h b/drivers/mfd/intel-lpss.h index 865bbeaaf00c..3a120fecd2dc 100644 --- a/drivers/mfd/intel-lpss.h +++ b/drivers/mfd/intel-lpss.h @@ -14,6 +14,8 @@ #ifndef __MFD_INTEL_LPSS_H #define __MFD_INTEL_LPSS_H +#include <linux/pm.h> + struct device; struct resource; struct property_entry; diff --git a/drivers/mfd/lochnagar-i2c.c b/drivers/mfd/lochnagar-i2c.c new file mode 100644 index 000000000000..3a65d9938902 --- /dev/null +++ b/drivers/mfd/lochnagar-i2c.c @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Lochnagar I2C bus interface + * + * Copyright (c) 2012-2018 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + * + * Author: Charles Keepax <ckeepax@opensource.cirrus.com> + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/lockdep.h> +#include <linux/mfd/core.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> + +#include <linux/mfd/lochnagar.h> +#include <linux/mfd/lochnagar1_regs.h> +#include <linux/mfd/lochnagar2_regs.h> + +#define LOCHNAGAR_BOOT_RETRIES 10 +#define LOCHNAGAR_BOOT_DELAY_MS 350 + +#define LOCHNAGAR_CONFIG_POLL_US 10000 + +static bool lochnagar1_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LOCHNAGAR_SOFTWARE_RESET: + case LOCHNAGAR_FIRMWARE_ID1...LOCHNAGAR_FIRMWARE_ID2: + case LOCHNAGAR1_CDC_AIF1_SEL...LOCHNAGAR1_CDC_AIF3_SEL: + case LOCHNAGAR1_CDC_MCLK1_SEL...LOCHNAGAR1_CDC_MCLK2_SEL: + case LOCHNAGAR1_CDC_AIF_CTRL1...LOCHNAGAR1_CDC_AIF_CTRL2: + case LOCHNAGAR1_EXT_AIF_CTRL: + case LOCHNAGAR1_DSP_AIF1_SEL...LOCHNAGAR1_DSP_AIF2_SEL: + case LOCHNAGAR1_DSP_CLKIN_SEL: + case LOCHNAGAR1_DSP_AIF: + case LOCHNAGAR1_GF_AIF1...LOCHNAGAR1_GF_AIF2: + case LOCHNAGAR1_PSIA_AIF: + case LOCHNAGAR1_PSIA1_SEL...LOCHNAGAR1_PSIA2_SEL: + case LOCHNAGAR1_SPDIF_AIF_SEL: + case LOCHNAGAR1_GF_AIF3_SEL...LOCHNAGAR1_GF_AIF4_SEL: + case LOCHNAGAR1_GF_CLKOUT1_SEL: + case LOCHNAGAR1_GF_AIF1_SEL...LOCHNAGAR1_GF_AIF2_SEL: + case LOCHNAGAR1_GF_GPIO2...LOCHNAGAR1_GF_GPIO7: + case LOCHNAGAR1_RST: + case LOCHNAGAR1_LED1...LOCHNAGAR1_LED2: + case LOCHNAGAR1_I2C_CTRL: + return true; + default: + return false; + } +} + +static const struct regmap_config lochnagar1_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + + .max_register = 0x50, + .readable_reg = lochnagar1_readable_register, + + .use_single_read = true, + .use_single_write = true, + + .cache_type = REGCACHE_RBTREE, +}; + +static const struct reg_sequence lochnagar1_patch[] = { + { 0x40, 0x0083 }, + { 0x47, 0x0018 }, + { 0x50, 0x0000 }, +}; + +static bool lochnagar2_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LOCHNAGAR_SOFTWARE_RESET: + case LOCHNAGAR_FIRMWARE_ID1...LOCHNAGAR_FIRMWARE_ID2: + case LOCHNAGAR2_CDC_AIF1_CTRL...LOCHNAGAR2_CDC_AIF3_CTRL: + case LOCHNAGAR2_DSP_AIF1_CTRL...LOCHNAGAR2_DSP_AIF2_CTRL: + case LOCHNAGAR2_PSIA1_CTRL...LOCHNAGAR2_PSIA2_CTRL: + case LOCHNAGAR2_GF_AIF3_CTRL...LOCHNAGAR2_GF_AIF4_CTRL: + case LOCHNAGAR2_GF_AIF1_CTRL...LOCHNAGAR2_GF_AIF2_CTRL: + case LOCHNAGAR2_SPDIF_AIF_CTRL: + case LOCHNAGAR2_USB_AIF1_CTRL...LOCHNAGAR2_USB_AIF2_CTRL: + case LOCHNAGAR2_ADAT_AIF_CTRL: + case LOCHNAGAR2_CDC_MCLK1_CTRL...LOCHNAGAR2_CDC_MCLK2_CTRL: + case LOCHNAGAR2_DSP_CLKIN_CTRL: + case LOCHNAGAR2_PSIA1_MCLK_CTRL...LOCHNAGAR2_PSIA2_MCLK_CTRL: + case LOCHNAGAR2_SPDIF_MCLK_CTRL: + case LOCHNAGAR2_GF_CLKOUT1_CTRL...LOCHNAGAR2_GF_CLKOUT2_CTRL: + case LOCHNAGAR2_ADAT_MCLK_CTRL: + case LOCHNAGAR2_SOUNDCARD_MCLK_CTRL: + case LOCHNAGAR2_GPIO_FPGA_GPIO1...LOCHNAGAR2_GPIO_FPGA_GPIO6: + case LOCHNAGAR2_GPIO_CDC_GPIO1...LOCHNAGAR2_GPIO_CDC_GPIO8: + case LOCHNAGAR2_GPIO_DSP_GPIO1...LOCHNAGAR2_GPIO_DSP_GPIO6: + case LOCHNAGAR2_GPIO_GF_GPIO2...LOCHNAGAR2_GPIO_GF_GPIO7: + case LOCHNAGAR2_GPIO_CDC_AIF1_BCLK...LOCHNAGAR2_GPIO_CDC_AIF3_TXDAT: + case LOCHNAGAR2_GPIO_DSP_AIF1_BCLK...LOCHNAGAR2_GPIO_DSP_AIF2_TXDAT: + case LOCHNAGAR2_GPIO_PSIA1_BCLK...LOCHNAGAR2_GPIO_PSIA2_TXDAT: + case LOCHNAGAR2_GPIO_GF_AIF3_BCLK...LOCHNAGAR2_GPIO_GF_AIF4_TXDAT: + case LOCHNAGAR2_GPIO_GF_AIF1_BCLK...LOCHNAGAR2_GPIO_GF_AIF2_TXDAT: + case LOCHNAGAR2_GPIO_DSP_UART1_RX...LOCHNAGAR2_GPIO_DSP_UART2_TX: + case LOCHNAGAR2_GPIO_GF_UART2_RX...LOCHNAGAR2_GPIO_GF_UART2_TX: + case LOCHNAGAR2_GPIO_USB_UART_RX: + case LOCHNAGAR2_GPIO_CDC_PDMCLK1...LOCHNAGAR2_GPIO_CDC_PDMDAT2: + case LOCHNAGAR2_GPIO_CDC_DMICCLK1...LOCHNAGAR2_GPIO_CDC_DMICDAT4: + case LOCHNAGAR2_GPIO_DSP_DMICCLK1...LOCHNAGAR2_GPIO_DSP_DMICDAT2: + case LOCHNAGAR2_GPIO_I2C2_SCL...LOCHNAGAR2_GPIO_I2C4_SDA: + case LOCHNAGAR2_GPIO_DSP_STANDBY: + case LOCHNAGAR2_GPIO_CDC_MCLK1...LOCHNAGAR2_GPIO_CDC_MCLK2: + case LOCHNAGAR2_GPIO_DSP_CLKIN: + case LOCHNAGAR2_GPIO_PSIA1_MCLK...LOCHNAGAR2_GPIO_PSIA2_MCLK: + case LOCHNAGAR2_GPIO_GF_GPIO1...LOCHNAGAR2_GPIO_GF_GPIO5: + case LOCHNAGAR2_GPIO_DSP_GPIO20: + case LOCHNAGAR2_GPIO_CHANNEL1...LOCHNAGAR2_GPIO_CHANNEL16: + case LOCHNAGAR2_MINICARD_RESETS: + case LOCHNAGAR2_ANALOGUE_PATH_CTRL1...LOCHNAGAR2_ANALOGUE_PATH_CTRL2: + case LOCHNAGAR2_COMMS_CTRL4: + case LOCHNAGAR2_SPDIF_CTRL: + case LOCHNAGAR2_IMON_CTRL1...LOCHNAGAR2_IMON_CTRL4: + case LOCHNAGAR2_IMON_DATA1...LOCHNAGAR2_IMON_DATA2: + case LOCHNAGAR2_POWER_CTRL: + case LOCHNAGAR2_MICVDD_CTRL1: + case LOCHNAGAR2_MICVDD_CTRL2: + case LOCHNAGAR2_VDDCORE_CDC_CTRL1: + case LOCHNAGAR2_VDDCORE_CDC_CTRL2: + case LOCHNAGAR2_SOUNDCARD_AIF_CTRL: + return true; + default: + return false; + } +} + +static bool lochnagar2_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LOCHNAGAR2_GPIO_CHANNEL1...LOCHNAGAR2_GPIO_CHANNEL16: + case LOCHNAGAR2_ANALOGUE_PATH_CTRL1: + case LOCHNAGAR2_IMON_CTRL3...LOCHNAGAR2_IMON_CTRL4: + case LOCHNAGAR2_IMON_DATA1...LOCHNAGAR2_IMON_DATA2: + return true; + default: + return false; + } +} + +static const struct regmap_config lochnagar2_i2c_regmap = { + .reg_bits = 16, + .val_bits = 16, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + + .max_register = 0x1F1F, + .readable_reg = lochnagar2_readable_register, + .volatile_reg = lochnagar2_volatile_register, + + .cache_type = REGCACHE_RBTREE, +}; + +static const struct reg_sequence lochnagar2_patch[] = { + { 0x00EE, 0x0000 }, +}; + +struct lochnagar_config { + int id; + const char * const name; + enum lochnagar_type type; + const struct regmap_config *regmap; + const struct reg_sequence *patch; + int npatch; +}; + +static struct lochnagar_config lochnagar_configs[] = { + { + .id = 0x50, + .name = "lochnagar1", + .type = LOCHNAGAR1, + .regmap = &lochnagar1_i2c_regmap, + .patch = lochnagar1_patch, + .npatch = ARRAY_SIZE(lochnagar1_patch), + }, + { + .id = 0xCB58, + .name = "lochnagar2", + .type = LOCHNAGAR2, + .regmap = &lochnagar2_i2c_regmap, + .patch = lochnagar2_patch, + .npatch = ARRAY_SIZE(lochnagar2_patch), + }, +}; + +static const struct of_device_id lochnagar_of_match[] = { + { .compatible = "cirrus,lochnagar1", .data = &lochnagar_configs[0] }, + { .compatible = "cirrus,lochnagar2", .data = &lochnagar_configs[1] }, + {}, +}; + +static int lochnagar_wait_for_boot(struct regmap *regmap, unsigned int *id) +{ + int i, ret; + + for (i = 0; i < LOCHNAGAR_BOOT_RETRIES; ++i) { + msleep(LOCHNAGAR_BOOT_DELAY_MS); + + /* The reset register will return the device ID when read */ + ret = regmap_read(regmap, LOCHNAGAR_SOFTWARE_RESET, id); + if (!ret) + return ret; + } + + return -ETIMEDOUT; +} + +/** + * lochnagar_update_config - Synchronise the boards analogue configuration to + * the hardware. + * + * @lochnagar: A pointer to the primary core data structure. + * + * Return: Zero on success or an appropriate negative error code on failure. + */ +int lochnagar_update_config(struct lochnagar *lochnagar) +{ + struct regmap *regmap = lochnagar->regmap; + unsigned int done = LOCHNAGAR2_ANALOGUE_PATH_UPDATE_STS_MASK; + int timeout_ms = LOCHNAGAR_BOOT_DELAY_MS * LOCHNAGAR_BOOT_RETRIES; + unsigned int val = 0; + int ret; + + lockdep_assert_held(&lochnagar->analogue_config_lock); + + if (lochnagar->type != LOCHNAGAR2) + return 0; + + /* + * Toggle the ANALOGUE_PATH_UPDATE bit and wait for the device to + * acknowledge that any outstanding changes to the analogue + * configuration have been applied. + */ + ret = regmap_write(regmap, LOCHNAGAR2_ANALOGUE_PATH_CTRL1, 0); + if (ret < 0) + return ret; + + ret = regmap_write(regmap, LOCHNAGAR2_ANALOGUE_PATH_CTRL1, + LOCHNAGAR2_ANALOGUE_PATH_UPDATE_MASK); + if (ret < 0) + return ret; + + ret = regmap_read_poll_timeout(regmap, + LOCHNAGAR2_ANALOGUE_PATH_CTRL1, val, + (val & done), LOCHNAGAR_CONFIG_POLL_US, + timeout_ms * 1000); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(lochnagar_update_config); + +static int lochnagar_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + const struct lochnagar_config *config = NULL; + const struct of_device_id *of_id; + struct lochnagar *lochnagar; + struct gpio_desc *reset, *present; + unsigned int val; + unsigned int firmwareid; + unsigned int devid, rev; + int ret; + + lochnagar = devm_kzalloc(dev, sizeof(*lochnagar), GFP_KERNEL); + if (!lochnagar) + return -ENOMEM; + + of_id = of_match_device(lochnagar_of_match, dev); + if (!of_id) + return -EINVAL; + + config = of_id->data; + + lochnagar->dev = dev; + mutex_init(&lochnagar->analogue_config_lock); + + dev_set_drvdata(dev, lochnagar); + + reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(reset)) { + ret = PTR_ERR(reset); + dev_err(dev, "Failed to get reset GPIO: %d\n", ret); + return ret; + } + + present = devm_gpiod_get_optional(dev, "present", GPIOD_OUT_HIGH); + if (IS_ERR(present)) { + ret = PTR_ERR(present); + dev_err(dev, "Failed to get present GPIO: %d\n", ret); + return ret; + } + + /* Leave the Lochnagar in reset for a reasonable amount of time */ + msleep(20); + + /* Bring Lochnagar out of reset */ + gpiod_set_value_cansleep(reset, 1); + + /* Identify Lochnagar */ + lochnagar->type = config->type; + + lochnagar->regmap = devm_regmap_init_i2c(i2c, config->regmap); + if (IS_ERR(lochnagar->regmap)) { + ret = PTR_ERR(lochnagar->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + /* Wait for Lochnagar to boot */ + ret = lochnagar_wait_for_boot(lochnagar->regmap, &val); + if (ret < 0) { + dev_err(dev, "Failed to read device ID: %d\n", ret); + return ret; + } + + devid = val & LOCHNAGAR_DEVICE_ID_MASK; + rev = val & LOCHNAGAR_REV_ID_MASK; + + if (devid != config->id) { + dev_err(dev, + "ID does not match %s (expected 0x%x got 0x%x)\n", + config->name, config->id, devid); + return -ENODEV; + } + + /* Identify firmware */ + ret = regmap_read(lochnagar->regmap, LOCHNAGAR_FIRMWARE_ID1, &val); + if (ret < 0) { + dev_err(dev, "Failed to read firmware id 1: %d\n", ret); + return ret; + } + + firmwareid = val; + + ret = regmap_read(lochnagar->regmap, LOCHNAGAR_FIRMWARE_ID2, &val); + if (ret < 0) { + dev_err(dev, "Failed to read firmware id 2: %d\n", ret); + return ret; + } + + firmwareid |= (val << config->regmap->val_bits); + + dev_info(dev, "Found %s (0x%x) revision %u firmware 0x%.6x\n", + config->name, devid, rev + 1, firmwareid); + + ret = regmap_register_patch(lochnagar->regmap, config->patch, + config->npatch); + if (ret < 0) { + dev_err(dev, "Failed to register patch: %d\n", ret); + return ret; + } + + ret = devm_of_platform_populate(dev); + if (ret < 0) { + dev_err(dev, "Failed to populate child nodes: %d\n", ret); + return ret; + } + + return ret; +} + +static struct i2c_driver lochnagar_i2c_driver = { + .driver = { + .name = "lochnagar", + .of_match_table = of_match_ptr(lochnagar_of_match), + .suppress_bind_attrs = true, + }, + .probe_new = lochnagar_i2c_probe, +}; + +static int __init lochnagar_i2c_init(void) +{ + int ret; + + ret = i2c_add_driver(&lochnagar_i2c_driver); + if (ret) + pr_err("Failed to register Lochnagar driver: %d\n", ret); + + return ret; +} +subsys_initcall(lochnagar_i2c_init); diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index fd8b15cd84fd..87c724ba9793 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -10,7 +10,7 @@ */ #include <linux/kernel.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/i2c.h> #include <linux/irq.h> #include <linux/interrupt.h> @@ -919,8 +919,3 @@ void max8925_device_exit(struct max8925_chip *chip) free_irq(chip->tsc_irq, chip); mfd_remove_devices(chip->dev); } - - -MODULE_DESCRIPTION("PMIC Driver for Maxim MAX8925"); -MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/mxs-lradc.c b/drivers/mfd/mxs-lradc.c index 98e732a7ae96..a09a8d06302b 100644 --- a/drivers/mfd/mxs-lradc.c +++ b/drivers/mfd/mxs-lradc.c @@ -181,7 +181,7 @@ static int mxs_lradc_probe(struct platform_device *pdev) MXS_LRADC_TOUCHSCREEN_5WIRE; break; } - /* fall through to an error message for i.MX23 */ + /* fall through - to an error message for i.MX23 */ default: dev_err(&pdev->dev, "Unsupported number of touchscreen wires (%d)\n" diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c index fd46de02b715..c5cc5cb3dde7 100644 --- a/drivers/mfd/rc5t583.c +++ b/drivers/mfd/rc5t583.c @@ -23,7 +23,6 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/kernel.h> -#include <linux/module.h> #include <linux/init.h> #include <linux/err.h> #include <linux/slab.h> @@ -298,8 +297,6 @@ static const struct i2c_device_id rc5t583_i2c_id[] = { {} }; -MODULE_DEVICE_TABLE(i2c, rc5t583_i2c_id); - static struct i2c_driver rc5t583_i2c_driver = { .driver = { .name = "rc5t583", @@ -313,14 +310,3 @@ static int __init rc5t583_i2c_init(void) return i2c_add_driver(&rc5t583_i2c_driver); } subsys_initcall(rc5t583_i2c_init); - -static void __exit rc5t583_i2c_exit(void) -{ - i2c_del_driver(&rc5t583_i2c_driver); -} - -module_exit(rc5t583_i2c_exit); - -MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); -MODULE_DESCRIPTION("RICOH RC5T583 power management system device driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index e0835c9df7a1..521319086c81 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -114,7 +114,8 @@ static const struct mfd_cell s2mpu02_devs[] = { #ifdef CONFIG_OF static const struct of_device_id sec_dt_match[] = { - { .compatible = "samsung,s5m8767-pmic", + { + .compatible = "samsung,s5m8767-pmic", .data = (void *)S5M8767X, }, { .compatible = "samsung,s2mps11-pmic", @@ -309,8 +310,8 @@ static void sec_pmic_configure(struct sec_pmic_dev *sec_pmic) * the sub-modules need not instantiate another instance while parsing their * platform data. */ -static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata( - struct device *dev) +static struct sec_platform_data * +sec_pmic_i2c_parse_dt_pdata(struct device *dev) { struct sec_platform_data *pd; @@ -331,8 +332,8 @@ static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata( return pd; } #else -static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata( - struct device *dev) +static struct sec_platform_data * +sec_pmic_i2c_parse_dt_pdata(struct device *dev) { return NULL; } @@ -471,8 +472,9 @@ static int sec_pmic_probe(struct i2c_client *i2c, num_sec_devs = ARRAY_SIZE(s2mpu02_devs); break; default: - /* If this happens the probe function is problem */ - BUG(); + dev_err(&i2c->dev, "Unsupported device type (%lu)\n", + sec_pmic->device_type); + return -ENODEV; } ret = devm_mfd_add_devices(sec_pmic->dev, -1, sec_devs, num_sec_devs, NULL, 0, NULL); diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index a530972c5a7e..d217debf382e 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -1142,9 +1142,11 @@ static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm, return -ENOMEM; /* Create a gpiod lookup using gpiochip-local offsets */ - lookup = devm_kzalloc(&pdev->dev, - sizeof(*lookup) + 3 * sizeof(struct gpiod_lookup), + lookup = devm_kzalloc(&pdev->dev, struct_size(lookup, table, 3), GFP_KERNEL); + if (!lookup) + return -ENOMEM; + lookup->dev_id = "i2c-gpio"; if (iic->pin_sda < 32) lookup->table[0].chip_label = "SM501-LOW"; diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index 3aeafa228baf..cab9aabcaa1f 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -1,4 +1,6 @@ /* + * STA2x11 mfd for GPIO, SCTL and APBREG + * * Copyright (c) 2009-2011 Wind River Systems, Inc. * Copyright (c) 2011 ST Microelectronics (Alessandro Rubini, Davide Ciminaghi) * @@ -18,7 +20,8 @@ */ #include <linux/kernel.h> -#include <linux/module.h> +#include <linux/init.h> +#include <linux/export.h> #include <linux/spinlock.h> #include <linux/errno.h> #include <linux/device.h> @@ -653,8 +656,3 @@ static int __init sta2x11_mfd_init(void) */ subsys_initcall(sta2x11_drivers_init); rootfs_initcall(sta2x11_mfd_init); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Wind River"); -MODULE_DESCRIPTION("STA2x11 mfd for GPIO, SCTL and APBREG"); -MODULE_DEVICE_TABLE(pci, sta2x11_mfd_tbl); diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 7569a4be0608..f2acb1f6a29c 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -464,6 +464,28 @@ static const struct mfd_cell stmpe_ts_cell = { }; /* + * ADC (STMPE811) + */ + +static struct resource stmpe_adc_resources[] = { + { + .name = "STMPE_TEMP_SENS", + .flags = IORESOURCE_IRQ, + }, + { + .name = "STMPE_ADC", + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct mfd_cell stmpe_adc_cell = { + .name = "stmpe-adc", + .of_compatible = "st,stmpe-adc", + .resources = stmpe_adc_resources, + .num_resources = ARRAY_SIZE(stmpe_adc_resources), +}; + +/* * STMPE811 or STMPE610 */ @@ -497,6 +519,11 @@ static struct stmpe_variant_block stmpe811_blocks[] = { .irq = STMPE811_IRQ_TOUCH_DET, .block = STMPE_BLOCK_TOUCHSCREEN, }, + { + .cell = &stmpe_adc_cell, + .irq = STMPE811_IRQ_TEMP_SENS, + .block = STMPE_BLOCK_ADC, + }, }; static int stmpe811_enable(struct stmpe *stmpe, unsigned int blocks, @@ -517,6 +544,35 @@ static int stmpe811_enable(struct stmpe *stmpe, unsigned int blocks, enable ? 0 : mask); } +int stmpe811_adc_common_init(struct stmpe *stmpe) +{ + int ret; + u8 adc_ctrl1, adc_ctrl1_mask; + + adc_ctrl1 = STMPE_SAMPLE_TIME(stmpe->sample_time) | + STMPE_MOD_12B(stmpe->mod_12b) | + STMPE_REF_SEL(stmpe->ref_sel); + adc_ctrl1_mask = STMPE_SAMPLE_TIME(0xff) | STMPE_MOD_12B(0xff) | + STMPE_REF_SEL(0xff); + + ret = stmpe_set_bits(stmpe, STMPE811_REG_ADC_CTRL1, + adc_ctrl1_mask, adc_ctrl1); + if (ret) { + dev_err(stmpe->dev, "Could not setup ADC\n"); + return ret; + } + + ret = stmpe_set_bits(stmpe, STMPE811_REG_ADC_CTRL2, + STMPE_ADC_FREQ(0xff), STMPE_ADC_FREQ(stmpe->adc_freq)); + if (ret) { + dev_err(stmpe->dev, "Could not setup ADC\n"); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(stmpe811_adc_common_init); + static int stmpe811_get_altfunc(struct stmpe *stmpe, enum stmpe_block block) { /* 0 for touchscreen, 1 for GPIO */ @@ -1325,6 +1381,7 @@ int stmpe_probe(struct stmpe_client_info *ci, enum stmpe_partnum partnum) struct device_node *np = ci->dev->of_node; struct stmpe *stmpe; int ret; + u32 val; pdata = devm_kzalloc(ci->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -1342,6 +1399,15 @@ int stmpe_probe(struct stmpe_client_info *ci, enum stmpe_partnum partnum) mutex_init(&stmpe->irq_lock); mutex_init(&stmpe->lock); + if (!of_property_read_u32(np, "st,sample-time", &val)) + stmpe->sample_time = val; + if (!of_property_read_u32(np, "st,mod-12b", &val)) + stmpe->mod_12b = val; + if (!of_property_read_u32(np, "st,ref-sel", &val)) + stmpe->ref_sel = val; + if (!of_property_read_u32(np, "st,adc-freq", &val)) + stmpe->adc_freq = val; + stmpe->dev = ci->dev; stmpe->client = ci->client; stmpe->pdata = pdata; @@ -1433,6 +1499,8 @@ int stmpe_remove(struct stmpe *stmpe) if (!IS_ERR(stmpe->vcc)) regulator_disable(stmpe->vcc); + __stmpe_disable(stmpe, STMPE_BLOCK_ADC); + mfd_remove_devices(stmpe->dev); return 0; diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c new file mode 100644 index 000000000000..7dfbe8906cb8 --- /dev/null +++ b/drivers/mfd/stpmic1.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) STMicroelectronics 2018 +// Author: Pascal Paillet <p.paillet@st.com> + +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/mfd/core.h> +#include <linux/mfd/stpmic1.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/pm_wakeirq.h> +#include <linux/regmap.h> + +#include <dt-bindings/mfd/st,stpmic1.h> + +#define STPMIC1_MAIN_IRQ 0 + +static const struct regmap_range stpmic1_readable_ranges[] = { + regmap_reg_range(TURN_ON_SR, VERSION_SR), + regmap_reg_range(SWOFF_PWRCTRL_CR, LDO6_STDBY_CR), + regmap_reg_range(BST_SW_CR, BST_SW_CR), + regmap_reg_range(INT_PENDING_R1, INT_PENDING_R4), + regmap_reg_range(INT_CLEAR_R1, INT_CLEAR_R4), + regmap_reg_range(INT_MASK_R1, INT_MASK_R4), + regmap_reg_range(INT_SET_MASK_R1, INT_SET_MASK_R4), + regmap_reg_range(INT_CLEAR_MASK_R1, INT_CLEAR_MASK_R4), + regmap_reg_range(INT_SRC_R1, INT_SRC_R1), +}; + +static const struct regmap_range stpmic1_writeable_ranges[] = { + regmap_reg_range(SWOFF_PWRCTRL_CR, LDO6_STDBY_CR), + regmap_reg_range(BST_SW_CR, BST_SW_CR), + regmap_reg_range(INT_CLEAR_R1, INT_CLEAR_R4), + regmap_reg_range(INT_SET_MASK_R1, INT_SET_MASK_R4), + regmap_reg_range(INT_CLEAR_MASK_R1, INT_CLEAR_MASK_R4), +}; + +static const struct regmap_range stpmic1_volatile_ranges[] = { + regmap_reg_range(TURN_ON_SR, VERSION_SR), + regmap_reg_range(WCHDG_CR, WCHDG_CR), + regmap_reg_range(INT_PENDING_R1, INT_PENDING_R4), + regmap_reg_range(INT_SRC_R1, INT_SRC_R4), +}; + +static const struct regmap_access_table stpmic1_readable_table = { + .yes_ranges = stpmic1_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(stpmic1_readable_ranges), +}; + +static const struct regmap_access_table stpmic1_writeable_table = { + .yes_ranges = stpmic1_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(stpmic1_writeable_ranges), +}; + +static const struct regmap_access_table stpmic1_volatile_table = { + .yes_ranges = stpmic1_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(stpmic1_volatile_ranges), +}; + +const struct regmap_config stpmic1_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = PMIC_MAX_REGISTER_ADDRESS, + .rd_table = &stpmic1_readable_table, + .wr_table = &stpmic1_writeable_table, + .volatile_table = &stpmic1_volatile_table, +}; + +static const struct regmap_irq stpmic1_irqs[] = { + REGMAP_IRQ_REG(IT_PONKEY_F, 0, 0x01), + REGMAP_IRQ_REG(IT_PONKEY_R, 0, 0x02), + REGMAP_IRQ_REG(IT_WAKEUP_F, 0, 0x04), + REGMAP_IRQ_REG(IT_WAKEUP_R, 0, 0x08), + REGMAP_IRQ_REG(IT_VBUS_OTG_F, 0, 0x10), + REGMAP_IRQ_REG(IT_VBUS_OTG_R, 0, 0x20), + REGMAP_IRQ_REG(IT_SWOUT_F, 0, 0x40), + REGMAP_IRQ_REG(IT_SWOUT_R, 0, 0x80), + + REGMAP_IRQ_REG(IT_CURLIM_BUCK1, 1, 0x01), + REGMAP_IRQ_REG(IT_CURLIM_BUCK2, 1, 0x02), + REGMAP_IRQ_REG(IT_CURLIM_BUCK3, 1, 0x04), + REGMAP_IRQ_REG(IT_CURLIM_BUCK4, 1, 0x08), + REGMAP_IRQ_REG(IT_OCP_OTG, 1, 0x10), + REGMAP_IRQ_REG(IT_OCP_SWOUT, 1, 0x20), + REGMAP_IRQ_REG(IT_OCP_BOOST, 1, 0x40), + REGMAP_IRQ_REG(IT_OVP_BOOST, 1, 0x80), + + REGMAP_IRQ_REG(IT_CURLIM_LDO1, 2, 0x01), + REGMAP_IRQ_REG(IT_CURLIM_LDO2, 2, 0x02), + REGMAP_IRQ_REG(IT_CURLIM_LDO3, 2, 0x04), + REGMAP_IRQ_REG(IT_CURLIM_LDO4, 2, 0x08), + REGMAP_IRQ_REG(IT_CURLIM_LDO5, 2, 0x10), + REGMAP_IRQ_REG(IT_CURLIM_LDO6, 2, 0x20), + REGMAP_IRQ_REG(IT_SHORT_SWOTG, 2, 0x40), + REGMAP_IRQ_REG(IT_SHORT_SWOUT, 2, 0x80), + + REGMAP_IRQ_REG(IT_TWARN_F, 3, 0x01), + REGMAP_IRQ_REG(IT_TWARN_R, 3, 0x02), + REGMAP_IRQ_REG(IT_VINLOW_F, 3, 0x04), + REGMAP_IRQ_REG(IT_VINLOW_R, 3, 0x08), + REGMAP_IRQ_REG(IT_SWIN_F, 3, 0x40), + REGMAP_IRQ_REG(IT_SWIN_R, 3, 0x80), +}; + +static const struct regmap_irq_chip stpmic1_regmap_irq_chip = { + .name = "pmic_irq", + .status_base = INT_PENDING_R1, + .mask_base = INT_CLEAR_MASK_R1, + .unmask_base = INT_SET_MASK_R1, + .ack_base = INT_CLEAR_R1, + .num_regs = STPMIC1_PMIC_NUM_IRQ_REGS, + .irqs = stpmic1_irqs, + .num_irqs = ARRAY_SIZE(stpmic1_irqs), +}; + +static int stpmic1_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct stpmic1 *ddata; + struct device *dev = &i2c->dev; + int ret; + struct device_node *np = dev->of_node; + u32 reg; + + ddata = devm_kzalloc(dev, sizeof(struct stpmic1), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + i2c_set_clientdata(i2c, ddata); + ddata->dev = dev; + + ddata->regmap = devm_regmap_init_i2c(i2c, &stpmic1_regmap_config); + if (IS_ERR(ddata->regmap)) + return PTR_ERR(ddata->regmap); + + ddata->irq = of_irq_get(np, STPMIC1_MAIN_IRQ); + if (ddata->irq < 0) { + dev_err(dev, "Failed to get main IRQ: %d\n", ddata->irq); + return ddata->irq; + } + + ret = regmap_read(ddata->regmap, VERSION_SR, ®); + if (ret) { + dev_err(dev, "Unable to read PMIC version\n"); + return ret; + } + dev_info(dev, "PMIC Chip Version: 0x%x\n", reg); + + /* Initialize PMIC IRQ Chip & associated IRQ domains */ + ret = devm_regmap_add_irq_chip(dev, ddata->regmap, ddata->irq, + IRQF_ONESHOT | IRQF_SHARED, + 0, &stpmic1_regmap_irq_chip, + &ddata->irq_data); + if (ret) { + dev_err(dev, "IRQ Chip registration failed: %d\n", ret); + return ret; + } + + return devm_of_platform_populate(dev); +} + +#ifdef CONFIG_PM_SLEEP +static int stpmic1_suspend(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct stpmic1 *pmic_dev = i2c_get_clientdata(i2c); + + disable_irq(pmic_dev->irq); + + return 0; +} + +static int stpmic1_resume(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct stpmic1 *pmic_dev = i2c_get_clientdata(i2c); + int ret; + + ret = regcache_sync(pmic_dev->regmap); + if (ret) + return ret; + + enable_irq(pmic_dev->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(stpmic1_pm, stpmic1_suspend, stpmic1_resume); + +static const struct of_device_id stpmic1_of_match[] = { + { .compatible = "st,stpmic1", }, + {}, +}; +MODULE_DEVICE_TABLE(of, stpmic1_of_match); + +static struct i2c_driver stpmic1_driver = { + .driver = { + .name = "stpmic1", + .of_match_table = of_match_ptr(stpmic1_of_match), + .pm = &stpmic1_pm, + }, + .probe = stpmic1_probe, +}; + +module_i2c_driver(stpmic1_driver); + +MODULE_DESCRIPTION("STPMIC1 PMIC Driver"); +MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index b6d05cd934e6..0ecdffb3d967 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -15,7 +15,7 @@ #include <linux/err.h> #include <linux/hwspinlock.h> #include <linux/io.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/list.h> #include <linux/of.h> #include <linux/of_address.h> @@ -272,13 +272,3 @@ static int __init syscon_init(void) return platform_driver_register(&syscon_driver); } postcore_initcall(syscon_init); - -static void __exit syscon_exit(void) -{ - platform_driver_unregister(&syscon_driver); -} -module_exit(syscon_exit); - -MODULE_AUTHOR("Dong Aisheng <dong.aisheng@linaro.org>"); -MODULE_DESCRIPTION("System Control driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index f13e4cd06e89..6968df4d7828 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -2,7 +2,9 @@ * Core driver for TI TPS65090 PMIC family * * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. - + * + * Author: Venu Byravarasu <vbyravarasu@nvidia.com> + * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. @@ -19,7 +21,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/kernel.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/i2c.h> @@ -171,7 +173,6 @@ static const struct of_device_id tps65090_of_match[] = { { .compatible = "ti,tps65090",}, {}, }; -MODULE_DEVICE_TABLE(of, tps65090_of_match); #endif static int tps65090_i2c_probe(struct i2c_client *client, @@ -236,30 +237,19 @@ err_irq_exit: return ret; } -static int tps65090_i2c_remove(struct i2c_client *client) -{ - struct tps65090 *tps65090 = i2c_get_clientdata(client); - - mfd_remove_devices(tps65090->dev); - if (client->irq) - regmap_del_irq_chip(client->irq, tps65090->irq_data); - - return 0; -} static const struct i2c_device_id tps65090_id_table[] = { { "tps65090", 0 }, { }, }; -MODULE_DEVICE_TABLE(i2c, tps65090_id_table); static struct i2c_driver tps65090_driver = { .driver = { .name = "tps65090", + .suppress_bind_attrs = true, .of_match_table = of_match_ptr(tps65090_of_match), }, .probe = tps65090_i2c_probe, - .remove = tps65090_i2c_remove, .id_table = tps65090_id_table, }; @@ -268,13 +258,3 @@ static int __init tps65090_init(void) return i2c_add_driver(&tps65090_driver); } subsys_initcall(tps65090_init); - -static void __exit tps65090_exit(void) -{ - i2c_del_driver(&tps65090_driver); -} -module_exit(tps65090_exit); - -MODULE_DESCRIPTION("TPS65090 core driver"); -MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c index 8bcdecf494d0..a62ea4cb8be7 100644 --- a/drivers/mfd/tps65218.c +++ b/drivers/mfd/tps65218.c @@ -211,6 +211,83 @@ static const struct of_device_id of_tps65218_match_table[] = { }; MODULE_DEVICE_TABLE(of, of_tps65218_match_table); +static int tps65218_voltage_set_strict(struct tps65218 *tps) +{ + u32 strict; + + if (of_property_read_u32(tps->dev->of_node, + "ti,strict-supply-voltage-supervision", + &strict)) + return 0; + + if (strict != 0 && strict != 1) { + dev_err(tps->dev, + "Invalid ti,strict-supply-voltage-supervision value\n"); + return -EINVAL; + } + + tps65218_update_bits(tps, TPS65218_REG_CONFIG1, + TPS65218_CONFIG1_STRICT, + strict ? TPS65218_CONFIG1_STRICT : 0, + TPS65218_PROTECT_L1); + return 0; +} + +static int tps65218_voltage_set_uv_hyst(struct tps65218 *tps) +{ + u32 hyst; + + if (of_property_read_u32(tps->dev->of_node, + "ti,under-voltage-hyst-microvolt", &hyst)) + return 0; + + if (hyst != 400000 && hyst != 200000) { + dev_err(tps->dev, + "Invalid ti,under-voltage-hyst-microvolt value\n"); + return -EINVAL; + } + + tps65218_update_bits(tps, TPS65218_REG_CONFIG2, + TPS65218_CONFIG2_UVLOHYS, + hyst == 400000 ? TPS65218_CONFIG2_UVLOHYS : 0, + TPS65218_PROTECT_L1); + return 0; +} + +static int tps65218_voltage_set_uvlo(struct tps65218 *tps) +{ + u32 uvlo; + int uvloval; + + if (of_property_read_u32(tps->dev->of_node, + "ti,under-voltage-limit-microvolt", &uvlo)) + return 0; + + switch (uvlo) { + case 2750000: + uvloval = TPS65218_CONFIG1_UVLO_2750000; + break; + case 2950000: + uvloval = TPS65218_CONFIG1_UVLO_2950000; + break; + case 3250000: + uvloval = TPS65218_CONFIG1_UVLO_3250000; + break; + case 3350000: + uvloval = TPS65218_CONFIG1_UVLO_3350000; + break; + default: + dev_err(tps->dev, + "Invalid ti,under-voltage-limit-microvolt value\n"); + return -EINVAL; + } + + tps65218_update_bits(tps, TPS65218_REG_CONFIG1, + TPS65218_CONFIG1_UVLO_MASK, uvloval, + TPS65218_PROTECT_L1); + return 0; +} + static int tps65218_probe(struct i2c_client *client, const struct i2c_device_id *ids) { @@ -249,6 +326,18 @@ static int tps65218_probe(struct i2c_client *client, tps->rev = chipid & TPS65218_CHIPID_REV_MASK; + ret = tps65218_voltage_set_strict(tps); + if (ret) + return ret; + + ret = tps65218_voltage_set_uvlo(tps); + if (ret) + return ret; + + ret = tps65218_voltage_set_uv_hyst(tps); + if (ret) + return ret; + ret = mfd_add_devices(tps->dev, PLATFORM_DEVID_AUTO, tps65218_cells, ARRAY_SIZE(tps65218_cells), NULL, 0, regmap_irq_get_domain(tps->irq_data)); diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index bf16cbe6fd88..aa3d472a10ff 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -1,5 +1,5 @@ /* - * tps65910.c -- TI TPS6591x + * tps65910.c -- TI TPS6591x chip family multi-function driver * * Copyright 2010 Texas Instruments Inc. * @@ -13,8 +13,6 @@ * */ -#include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/init.h> #include <linux/err.h> #include <linux/slab.h> @@ -374,7 +372,6 @@ static const struct of_device_id tps65910_of_match[] = { { .compatible = "ti,tps65911", .data = (void *)TPS65911}, { }, }; -MODULE_DEVICE_TABLE(of, tps65910_of_match); static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, unsigned long *chip_id) @@ -527,8 +524,6 @@ static const struct i2c_device_id tps65910_i2c_id[] = { { "tps65911", TPS65911 }, { } }; -MODULE_DEVICE_TABLE(i2c, tps65910_i2c_id); - static struct i2c_driver tps65910_i2c_driver = { .driver = { @@ -545,14 +540,3 @@ static int __init tps65910_i2c_init(void) } /* init early so consumer devices can complete system boot */ subsys_initcall(tps65910_i2c_init); - -static void __exit tps65910_i2c_exit(void) -{ - i2c_del_driver(&tps65910_i2c_driver); -} -module_exit(tps65910_i2c_exit); - -MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>"); -MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>"); -MODULE_DESCRIPTION("TPS6591x chip family multi-function driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/tps68470.c b/drivers/mfd/tps68470.c index a5981a79b29a..4a4df4ffd18c 100644 --- a/drivers/mfd/tps68470.c +++ b/drivers/mfd/tps68470.c @@ -86,7 +86,6 @@ static const struct acpi_device_id tps68470_acpi_ids[] = { {"INT3472"}, {}, }; -MODULE_DEVICE_TABLE(acpi, tps68470_acpi_ids); static struct i2c_driver tps68470_driver = { .driver = { diff --git a/drivers/mfd/tps80031.c b/drivers/mfd/tps80031.c index 608c7f77830e..865257ade8ac 100644 --- a/drivers/mfd/tps80031.c +++ b/drivers/mfd/tps80031.c @@ -30,7 +30,6 @@ #include <linux/irq.h> #include <linux/mfd/core.h> #include <linux/mfd/tps80031.h> -#include <linux/module.h> #include <linux/pm.h> #include <linux/regmap.h> #include <linux/slab.h> @@ -516,40 +515,18 @@ fail_client_reg: return ret; } -static int tps80031_remove(struct i2c_client *client) -{ - struct tps80031 *tps80031 = i2c_get_clientdata(client); - int i; - - if (tps80031_power_off_dev == tps80031) { - tps80031_power_off_dev = NULL; - pm_power_off = NULL; - } - - mfd_remove_devices(tps80031->dev); - - regmap_del_irq_chip(client->irq, tps80031->irq_data); - - for (i = 0; i < TPS80031_NUM_SLAVES; i++) { - if (tps80031->clients[i] != client) - i2c_unregister_device(tps80031->clients[i]); - } - return 0; -} - static const struct i2c_device_id tps80031_id_table[] = { { "tps80031", TPS80031 }, { "tps80032", TPS80032 }, { } }; -MODULE_DEVICE_TABLE(i2c, tps80031_id_table); static struct i2c_driver tps80031_driver = { .driver = { - .name = "tps80031", + .name = "tps80031", + .suppress_bind_attrs = true, }, .probe = tps80031_probe, - .remove = tps80031_remove, .id_table = tps80031_id_table, }; @@ -558,13 +535,3 @@ static int __init tps80031_init(void) return i2c_add_driver(&tps80031_driver); } subsys_initcall(tps80031_init); - -static void __exit tps80031_exit(void) -{ - i2c_del_driver(&tps80031_driver); -} -module_exit(tps80031_exit); - -MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); -MODULE_DESCRIPTION("TPS80031 core driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/tqmx86.c b/drivers/mfd/tqmx86.c new file mode 100644 index 000000000000..22d2f02d855c --- /dev/null +++ b/drivers/mfd/tqmx86.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * TQ-Systems PLD MFD core driver, based on vendor driver by + * Vadim V.Vlasov <vvlasov@dev.rtsoft.ru> + * + * Copyright (c) 2015 TQ-Systems GmbH + * Copyright (c) 2019 Andrew Lunn <andrew@lunn.ch> + */ + +#include <linux/delay.h> +#include <linux/dmi.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/mfd/core.h> +#include <linux/module.h> +#include <linux/platform_data/i2c-ocores.h> +#include <linux/platform_device.h> + +#define TQMX86_IOBASE 0x160 +#define TQMX86_IOSIZE 0x3f +#define TQMX86_IOBASE_I2C 0x1a0 +#define TQMX86_IOSIZE_I2C 0xa +#define TQMX86_IOBASE_WATCHDOG 0x18b +#define TQMX86_IOSIZE_WATCHDOG 0x2 +#define TQMX86_IOBASE_GPIO 0x18d +#define TQMX86_IOSIZE_GPIO 0x4 + +#define TQMX86_REG_BOARD_ID 0x20 +#define TQMX86_REG_BOARD_ID_E38M 1 +#define TQMX86_REG_BOARD_ID_50UC 2 +#define TQMX86_REG_BOARD_ID_E38C 3 +#define TQMX86_REG_BOARD_ID_60EB 4 +#define TQMX86_REG_BOARD_ID_E39M 5 +#define TQMX86_REG_BOARD_ID_E39C 6 +#define TQMX86_REG_BOARD_ID_E39x 7 +#define TQMX86_REG_BOARD_ID_70EB 8 +#define TQMX86_REG_BOARD_ID_80UC 9 +#define TQMX86_REG_BOARD_ID_90UC 10 +#define TQMX86_REG_BOARD_REV 0x21 +#define TQMX86_REG_IO_EXT_INT 0x26 +#define TQMX86_REG_IO_EXT_INT_NONE 0 +#define TQMX86_REG_IO_EXT_INT_7 1 +#define TQMX86_REG_IO_EXT_INT_9 2 +#define TQMX86_REG_IO_EXT_INT_12 3 +#define TQMX86_REG_IO_EXT_INT_MASK 0x3 +#define TQMX86_REG_IO_EXT_INT_GPIO_SHIFT 4 + +#define TQMX86_REG_I2C_DETECT 0x47 +#define TQMX86_REG_I2C_DETECT_SOFT 0xa5 +#define TQMX86_REG_I2C_INT_EN 0x49 + +static uint gpio_irq; +module_param(gpio_irq, uint, 0); +MODULE_PARM_DESC(gpio_irq, "GPIO IRQ number (7, 9, 12)"); + +static const struct resource tqmx_i2c_soft_resources[] = { + DEFINE_RES_IO(TQMX86_IOBASE_I2C, TQMX86_IOSIZE_I2C), +}; + +static const struct resource tqmx_watchdog_resources[] = { + DEFINE_RES_IO(TQMX86_IOBASE_WATCHDOG, TQMX86_IOSIZE_WATCHDOG), +}; + +/* + * The IRQ resource must be first, since it is updated with the + * configured IRQ in the probe function. + */ +static struct resource tqmx_gpio_resources[] = { + DEFINE_RES_IRQ(0), + DEFINE_RES_IO(TQMX86_IOBASE_GPIO, TQMX86_IOSIZE_GPIO), +}; + +static struct i2c_board_info tqmx86_i2c_devices[] = { + { + /* 4K EEPROM at 0x50 */ + I2C_BOARD_INFO("24c32", 0x50), + }, +}; + +static struct ocores_i2c_platform_data ocores_platfom_data = { + .num_devices = ARRAY_SIZE(tqmx86_i2c_devices), + .devices = tqmx86_i2c_devices, +}; + +static const struct mfd_cell tqmx86_i2c_soft_dev[] = { + { + .name = "ocores-i2c", + .platform_data = &ocores_platfom_data, + .pdata_size = sizeof(ocores_platfom_data), + .resources = tqmx_i2c_soft_resources, + .num_resources = ARRAY_SIZE(tqmx_i2c_soft_resources), + }, +}; + +static const struct mfd_cell tqmx86_devs[] = { + { + .name = "tqmx86-wdt", + .resources = tqmx_watchdog_resources, + .num_resources = ARRAY_SIZE(tqmx_watchdog_resources), + .ignore_resource_conflicts = true, + }, + { + .name = "tqmx86-gpio", + .resources = tqmx_gpio_resources, + .num_resources = ARRAY_SIZE(tqmx_gpio_resources), + .ignore_resource_conflicts = true, + }, +}; + +static const char *tqmx86_board_id_to_name(u8 board_id) +{ + switch (board_id) { + case TQMX86_REG_BOARD_ID_E38M: + return "TQMxE38M"; + case TQMX86_REG_BOARD_ID_50UC: + return "TQMx50UC"; + case TQMX86_REG_BOARD_ID_E38C: + return "TQMxE38C"; + case TQMX86_REG_BOARD_ID_60EB: + return "TQMx60EB"; + case TQMX86_REG_BOARD_ID_E39M: + return "TQMxE39M"; + case TQMX86_REG_BOARD_ID_E39C: + return "TQMxE39C"; + case TQMX86_REG_BOARD_ID_E39x: + return "TQMxE39x"; + case TQMX86_REG_BOARD_ID_70EB: + return "TQMx70EB"; + case TQMX86_REG_BOARD_ID_80UC: + return "TQMx80UC"; + case TQMX86_REG_BOARD_ID_90UC: + return "TQMx90UC"; + default: + return "Unknown"; + } +} + +static int tqmx86_board_id_to_clk_rate(u8 board_id) +{ + switch (board_id) { + case TQMX86_REG_BOARD_ID_50UC: + case TQMX86_REG_BOARD_ID_60EB: + case TQMX86_REG_BOARD_ID_70EB: + case TQMX86_REG_BOARD_ID_80UC: + case TQMX86_REG_BOARD_ID_90UC: + return 24000; + case TQMX86_REG_BOARD_ID_E39M: + case TQMX86_REG_BOARD_ID_E39C: + case TQMX86_REG_BOARD_ID_E39x: + return 25000; + case TQMX86_REG_BOARD_ID_E38M: + case TQMX86_REG_BOARD_ID_E38C: + return 33000; + default: + return 0; + } +} + +static int tqmx86_probe(struct platform_device *pdev) +{ + u8 board_id, rev, i2c_det, i2c_ien, io_ext_int_val; + struct device *dev = &pdev->dev; + u8 gpio_irq_cfg, readback; + const char *board_name; + void __iomem *io_base; + int err; + + switch (gpio_irq) { + case 0: + gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_NONE; + break; + case 7: + gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_7; + break; + case 9: + gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_9; + break; + case 12: + gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_12; + break; + default: + pr_err("tqmx86: Invalid GPIO IRQ (%d)\n", gpio_irq); + return -EINVAL; + } + + io_base = devm_ioport_map(dev, TQMX86_IOBASE, TQMX86_IOSIZE); + if (!io_base) + return -ENOMEM; + + board_id = ioread8(io_base + TQMX86_REG_BOARD_ID); + board_name = tqmx86_board_id_to_name(board_id); + rev = ioread8(io_base + TQMX86_REG_BOARD_REV); + + dev_info(dev, + "Found %s - Board ID %d, PCB Revision %d, PLD Revision %d\n", + board_name, board_id, rev >> 4, rev & 0xf); + + i2c_det = ioread8(io_base + TQMX86_REG_I2C_DETECT); + i2c_ien = ioread8(io_base + TQMX86_REG_I2C_INT_EN); + + if (gpio_irq_cfg) { + io_ext_int_val = + gpio_irq_cfg << TQMX86_REG_IO_EXT_INT_GPIO_SHIFT; + iowrite8(io_ext_int_val, io_base + TQMX86_REG_IO_EXT_INT); + readback = ioread8(io_base + TQMX86_REG_IO_EXT_INT); + if (readback != io_ext_int_val) { + dev_warn(dev, "GPIO interrupts not supported.\n"); + return -EINVAL; + } + + /* Assumes the IRQ resource is first. */ + tqmx_gpio_resources[0].start = gpio_irq; + } + + ocores_platfom_data.clock_khz = tqmx86_board_id_to_clk_rate(board_id); + + if (i2c_det == TQMX86_REG_I2C_DETECT_SOFT) { + err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, + tqmx86_i2c_soft_dev, + ARRAY_SIZE(tqmx86_i2c_soft_dev), + NULL, 0, NULL); + if (err) + return err; + } + + return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, + tqmx86_devs, + ARRAY_SIZE(tqmx86_devs), + NULL, 0, NULL); +} + +static int tqmx86_create_platform_device(const struct dmi_system_id *id) +{ + struct platform_device *pdev; + int err; + + pdev = platform_device_alloc("tqmx86", -1); + if (!pdev) + return -ENOMEM; + + err = platform_device_add(pdev); + if (err) + platform_device_put(pdev); + + return err; +} + +static const struct dmi_system_id tqmx86_dmi_table[] __initconst = { + { + .ident = "TQMX86", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TQ-Group"), + DMI_MATCH(DMI_PRODUCT_NAME, "TQMx"), + }, + .callback = tqmx86_create_platform_device, + }, + {} +}; +MODULE_DEVICE_TABLE(dmi, tqmx86_dmi_table); + +static struct platform_driver tqmx86_driver = { + .driver = { + .name = "tqmx86", + }, + .probe = tqmx86_probe, +}; + +static int __init tqmx86_init(void) +{ + if (!dmi_check_system(tqmx86_dmi_table)) + return -ENODEV; + + return platform_driver_register(&tqmx86_driver); +} + +module_init(tqmx86_init); + +MODULE_DESCRIPTION("TQx86 PLD Core Driver"); +MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:tqmx86"); diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index e70d35ef5c6d..25fbbaf39cb9 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -13,7 +13,8 @@ */ #include <linux/kernel.h> -#include <linux/module.h> +#include <linux/init.h> +#include <linux/export.h> #include <linux/bcd.h> #include <linux/delay.h> #include <linux/mfd/core.h> @@ -1892,14 +1893,6 @@ err: return ret; } -void wm831x_device_exit(struct wm831x *wm831x) -{ - wm831x_otp_exit(wm831x); - mfd_remove_devices(wm831x->dev); - free_irq(wm831x_irq(wm831x, WM831X_IRQ_AUXADC_DATA), wm831x); - wm831x_irq_exit(wm831x); -} - int wm831x_device_suspend(struct wm831x *wm831x) { int reg, mask; @@ -1944,7 +1937,3 @@ void wm831x_device_shutdown(struct wm831x *wm831x) } } EXPORT_SYMBOL_GPL(wm831x_device_shutdown); - -MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mark Brown"); diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c index 22f7054d1b28..0f3af42f7268 100644 --- a/drivers/mfd/wm831x-i2c.c +++ b/drivers/mfd/wm831x-i2c.c @@ -13,7 +13,7 @@ */ #include <linux/kernel.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/i2c.h> #include <linux/delay.h> #include <linux/mfd/core.h> @@ -68,15 +68,6 @@ static int wm831x_i2c_probe(struct i2c_client *i2c, return wm831x_device_init(wm831x, i2c->irq); } -static int wm831x_i2c_remove(struct i2c_client *i2c) -{ - struct wm831x *wm831x = i2c_get_clientdata(i2c); - - wm831x_device_exit(wm831x); - - return 0; -} - static int wm831x_i2c_suspend(struct device *dev) { struct wm831x *wm831x = dev_get_drvdata(dev); @@ -103,7 +94,6 @@ static const struct i2c_device_id wm831x_i2c_id[] = { { "wm8326", WM8326 }, { } }; -MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id); static const struct dev_pm_ops wm831x_pm_ops = { .suspend = wm831x_i2c_suspend, @@ -115,9 +105,9 @@ static struct i2c_driver wm831x_i2c_driver = { .name = "wm831x", .pm = &wm831x_pm_ops, .of_match_table = of_match_ptr(wm831x_of_match), + .suppress_bind_attrs = true, }, .probe = wm831x_i2c_probe, - .remove = wm831x_i2c_remove, .id_table = wm831x_i2c_id, }; @@ -132,9 +122,3 @@ static int __init wm831x_i2c_init(void) return ret; } subsys_initcall(wm831x_i2c_init); - -static void __exit wm831x_i2c_exit(void) -{ - i2c_del_driver(&wm831x_i2c_driver); -} -module_exit(wm831x_i2c_exit); diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index 018ce652ae57..dd4dab419940 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -13,7 +13,7 @@ */ #include <linux/kernel.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/pm.h> @@ -67,15 +67,6 @@ static int wm831x_spi_probe(struct spi_device *spi) return wm831x_device_init(wm831x, spi->irq); } -static int wm831x_spi_remove(struct spi_device *spi) -{ - struct wm831x *wm831x = spi_get_drvdata(spi); - - wm831x_device_exit(wm831x); - - return 0; -} - static int wm831x_spi_suspend(struct device *dev) { struct wm831x *wm831x = dev_get_drvdata(dev); @@ -108,17 +99,16 @@ static const struct spi_device_id wm831x_spi_ids[] = { { "wm8326", WM8326 }, { }, }; -MODULE_DEVICE_TABLE(spi, wm831x_spi_ids); static struct spi_driver wm831x_spi_driver = { .driver = { .name = "wm831x", .pm = &wm831x_spi_pm, .of_match_table = of_match_ptr(wm831x_of_match), + .suppress_bind_attrs = true, }, .id_table = wm831x_spi_ids, .probe = wm831x_spi_probe, - .remove = wm831x_spi_remove, }; static int __init wm831x_spi_init(void) @@ -132,13 +122,3 @@ static int __init wm831x_spi_init(void) return 0; } subsys_initcall(wm831x_spi_init); - -static void __exit wm831x_spi_exit(void) -{ - spi_unregister_driver(&wm831x_spi_driver); -} -module_exit(wm831x_spi_exit); - -MODULE_DESCRIPTION("SPI support for WM831x/2x AudioPlus PMICs"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mark Brown"); diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index 8a07c5634aee..9e1070f26b11 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -13,7 +13,8 @@ */ #include <linux/kernel.h> -#include <linux/module.h> +#include <linux/init.h> +#include <linux/export.h> #include <linux/slab.h> #include <linux/bug.h> #include <linux/device.h> @@ -442,30 +443,3 @@ err: return ret; } EXPORT_SYMBOL_GPL(wm8350_device_init); - -void wm8350_device_exit(struct wm8350 *wm8350) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(wm8350->pmic.led); i++) - platform_device_unregister(wm8350->pmic.led[i].pdev); - - for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++) - platform_device_unregister(wm8350->pmic.pdev[i]); - - platform_device_unregister(wm8350->wdt.pdev); - platform_device_unregister(wm8350->rtc.pdev); - platform_device_unregister(wm8350->power.pdev); - platform_device_unregister(wm8350->hwmon.pdev); - platform_device_unregister(wm8350->gpio.pdev); - platform_device_unregister(wm8350->codec.pdev); - - if (wm8350->irq_base) - free_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, wm8350); - - wm8350_irq_exit(wm8350); -} -EXPORT_SYMBOL_GPL(wm8350_device_exit); - -MODULE_DESCRIPTION("WM8350 AudioPlus PMIC core driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c index 9358f03b7938..b4194e068e1b 100644 --- a/drivers/mfd/wm8350-i2c.c +++ b/drivers/mfd/wm8350-i2c.c @@ -13,8 +13,6 @@ * */ -#include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/err.h> #include <linux/init.h> #include <linux/i2c.h> @@ -48,30 +46,19 @@ static int wm8350_i2c_probe(struct i2c_client *i2c, return wm8350_device_init(wm8350, i2c->irq, pdata); } -static int wm8350_i2c_remove(struct i2c_client *i2c) -{ - struct wm8350 *wm8350 = i2c_get_clientdata(i2c); - - wm8350_device_exit(wm8350); - - return 0; -} - static const struct i2c_device_id wm8350_i2c_id[] = { { "wm8350", 0 }, { "wm8351", 0 }, { "wm8352", 0 }, { } }; -MODULE_DEVICE_TABLE(i2c, wm8350_i2c_id); - static struct i2c_driver wm8350_i2c_driver = { .driver = { .name = "wm8350", + .suppress_bind_attrs = true, }, .probe = wm8350_i2c_probe, - .remove = wm8350_i2c_remove, .id_table = wm8350_i2c_id, }; @@ -81,12 +68,3 @@ static int __init wm8350_i2c_init(void) } /* init early so consumer devices can complete system boot */ subsys_initcall(wm8350_i2c_init); - -static void __exit wm8350_i2c_exit(void) -{ - i2c_del_driver(&wm8350_i2c_driver); -} -module_exit(wm8350_i2c_exit); - -MODULE_DESCRIPTION("I2C support for the WM8350 AudioPlus PMIC"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index 8a98a2fc74e1..79756c83f5f0 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -12,7 +12,7 @@ * */ -#include <linux/module.h> +#include <linux/init.h> #include <linux/bug.h> #include <linux/err.h> #include <linux/i2c.h> @@ -150,7 +150,6 @@ static const struct i2c_device_id wm8400_i2c_id[] = { { "wm8400", 0 }, { } }; -MODULE_DEVICE_TABLE(i2c, wm8400_i2c_id); static struct i2c_driver wm8400_i2c_driver = { .driver = { @@ -161,7 +160,7 @@ static struct i2c_driver wm8400_i2c_driver = { }; #endif -static int __init wm8400_module_init(void) +static int __init wm8400_driver_init(void) { int ret = -ENODEV; @@ -173,15 +172,4 @@ static int __init wm8400_module_init(void) return ret; } -subsys_initcall(wm8400_module_init); - -static void __exit wm8400_module_exit(void) -{ -#if IS_ENABLED(CONFIG_I2C) - i2c_del_driver(&wm8400_i2c_driver); -#endif -} -module_exit(wm8400_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +subsys_initcall(wm8400_driver_init); diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 16b1615958aa..5e2fde5ff63d 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -49,9 +49,6 @@ config CHROMEOS_TBMC To compile this driver as a module, choose M here: the module will be called chromeos_tbmc. -config CROS_EC_CTL - tristate - config CROS_EC_I2C tristate "ChromeOS Embedded Controller (I2C)" depends on MFD_CROS_EC && I2C @@ -111,4 +108,48 @@ config CROS_KBD_LED_BACKLIGHT To compile this driver as a module, choose M here: the module will be called cros_kbd_led_backlight. +config CROS_EC_LIGHTBAR + tristate "Chromebook Pixel's lightbar support" + depends on MFD_CROS_EC_CHARDEV + default MFD_CROS_EC_CHARDEV + help + This option exposes the Chromebook Pixel's lightbar to + userspace. + + To compile this driver as a module, choose M here: the + module will be called cros_ec_lightbar. + +config CROS_EC_VBC + tristate "ChromeOS EC vboot context support" + depends on MFD_CROS_EC_CHARDEV && OF + default MFD_CROS_EC_CHARDEV + help + This option exposes the ChromeOS EC vboot context nvram to + userspace. + + To compile this driver as a module, choose M here: the + module will be called cros_ec_vbc. + +config CROS_EC_DEBUGFS + tristate "Export ChromeOS EC internals in DebugFS" + depends on MFD_CROS_EC_CHARDEV && DEBUG_FS + default MFD_CROS_EC_CHARDEV + help + This option exposes the ChromeOS EC device internals to + userspace. + + To compile this driver as a module, choose M here: the + module will be called cros_ec_debugfs. + +config CROS_EC_SYSFS + tristate "ChromeOS EC control and information through sysfs" + depends on MFD_CROS_EC_CHARDEV && SYSFS + default MFD_CROS_EC_CHARDEV + help + This option exposes some sysfs attributes to control and get + information from ChromeOS EC. + + To compile this driver as a module, choose M here: the + module will be called cros_ec_sysfs. + endif # CHROMEOS_PLATFORMS diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index cd591bf872bb..fdbee501931b 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -3,9 +3,6 @@ obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o obj-$(CONFIG_CHROMEOS_TBMC) += chromeos_tbmc.o -cros_ec_ctl-objs := cros_ec_sysfs.o cros_ec_lightbar.o \ - cros_ec_vbc.o cros_ec_debugfs.o -obj-$(CONFIG_CROS_EC_CTL) += cros_ec_ctl.o obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_reg.o @@ -13,3 +10,7 @@ cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC) += cros_ec_lpc_mec.o obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o +obj-$(CONFIG_CROS_EC_LIGHTBAR) += cros_ec_lightbar.o +obj-$(CONFIG_CROS_EC_VBC) += cros_ec_vbc.o +obj-$(CONFIG_CROS_EC_DEBUGFS) += cros_ec_debugfs.o +obj-$(CONFIG_CROS_EC_SYSFS) += cros_ec_sysfs.o diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c index c62ee8e610a0..6cacac53dfce 100644 --- a/drivers/platform/chrome/cros_ec_debugfs.c +++ b/drivers/platform/chrome/cros_ec_debugfs.c @@ -23,12 +23,16 @@ #include <linux/fs.h> #include <linux/mfd/cros_ec.h> #include <linux/mfd/cros_ec_commands.h> +#include <linux/module.h> #include <linux/mutex.h> +#include <linux/platform_device.h> #include <linux/poll.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/wait.h> +#define DRV_NAME "cros-ec-debugfs" + #define LOG_SHIFT 14 #define LOG_SIZE (1 << LOG_SHIFT) #define LOG_POLL_SEC 10 @@ -423,8 +427,9 @@ static int cros_ec_create_pdinfo(struct cros_ec_debugfs *debug_info) return 0; } -int cros_ec_debugfs_init(struct cros_ec_dev *ec) +static int cros_ec_debugfs_probe(struct platform_device *pd) { + struct cros_ec_dev *ec = dev_get_drvdata(pd->dev.parent); struct cros_ec_platform *ec_platform = dev_get_platdata(ec->dev); const char *name = ec_platform->ec_name; struct cros_ec_debugfs *debug_info; @@ -453,40 +458,57 @@ int cros_ec_debugfs_init(struct cros_ec_dev *ec) ec->debug_info = debug_info; + dev_set_drvdata(&pd->dev, ec); + return 0; remove_debugfs: debugfs_remove_recursive(debug_info->dir); return ret; } -EXPORT_SYMBOL(cros_ec_debugfs_init); -void cros_ec_debugfs_remove(struct cros_ec_dev *ec) +static int cros_ec_debugfs_remove(struct platform_device *pd) { - if (!ec->debug_info) - return; + struct cros_ec_dev *ec = dev_get_drvdata(pd->dev.parent); debugfs_remove_recursive(ec->debug_info->dir); cros_ec_cleanup_console_log(ec->debug_info); + + return 0; } -EXPORT_SYMBOL(cros_ec_debugfs_remove); -void cros_ec_debugfs_suspend(struct cros_ec_dev *ec) +static int __maybe_unused cros_ec_debugfs_suspend(struct device *dev) { - /* - * cros_ec_debugfs_init() failures are non-fatal; it's also possible - * that we initted things but decided that console log wasn't supported. - * We'll use the same set of checks that cros_ec_debugfs_remove() + - * cros_ec_cleanup_console_log() end up using to handle those cases. - */ - if (ec->debug_info && ec->debug_info->log_buffer.buf) - cancel_delayed_work_sync(&ec->debug_info->log_poll_work); + struct cros_ec_dev *ec = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&ec->debug_info->log_poll_work); + + return 0; } -EXPORT_SYMBOL(cros_ec_debugfs_suspend); -void cros_ec_debugfs_resume(struct cros_ec_dev *ec) +static int __maybe_unused cros_ec_debugfs_resume(struct device *dev) { - if (ec->debug_info && ec->debug_info->log_buffer.buf) - schedule_delayed_work(&ec->debug_info->log_poll_work, 0); + struct cros_ec_dev *ec = dev_get_drvdata(dev); + + schedule_delayed_work(&ec->debug_info->log_poll_work, 0); + + return 0; } -EXPORT_SYMBOL(cros_ec_debugfs_resume); + +static SIMPLE_DEV_PM_OPS(cros_ec_debugfs_pm_ops, + cros_ec_debugfs_suspend, cros_ec_debugfs_resume); + +static struct platform_driver cros_ec_debugfs_driver = { + .driver = { + .name = DRV_NAME, + .pm = &cros_ec_debugfs_pm_ops, + }, + .probe = cros_ec_debugfs_probe, + .remove = cros_ec_debugfs_remove, +}; + +module_platform_driver(cros_ec_debugfs_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Debug logs for ChromeOS EC"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/platform/chrome/cros_ec_i2c.c b/drivers/platform/chrome/cros_ec_i2c.c index ef9b4763356f..9a009eaa4ada 100644 --- a/drivers/platform/chrome/cros_ec_i2c.c +++ b/drivers/platform/chrome/cros_ec_i2c.c @@ -317,15 +317,6 @@ static int cros_ec_i2c_probe(struct i2c_client *client, return 0; } -static int cros_ec_i2c_remove(struct i2c_client *client) -{ - struct cros_ec_device *ec_dev = i2c_get_clientdata(client); - - cros_ec_remove(ec_dev); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static int cros_ec_i2c_suspend(struct device *dev) { @@ -376,7 +367,6 @@ static struct i2c_driver cros_ec_driver = { .pm = &cros_ec_i2c_pm_ops, }, .probe = cros_ec_i2c_probe, - .remove = cros_ec_i2c_remove, .id_table = cros_ec_i2c_id, }; diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index 68193bb53383..c3e4e6e5211d 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -33,6 +33,8 @@ #include <linux/uaccess.h> #include <linux/slab.h> +#define DRV_NAME "cros-ec-lightbar" + /* Rate-limit the lightbar interface to prevent DoS. */ static unsigned long lb_interval_jiffies = 50 * HZ / 1000; @@ -41,7 +43,6 @@ static unsigned long lb_interval_jiffies = 50 * HZ / 1000; * If this is true, we won't do anything during suspend/resume. */ static bool userspace_control; -static struct cros_ec_dev *ec_with_lightbar; static ssize_t interval_msec_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -373,15 +374,12 @@ error: return ret; } -int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable) +static int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable) { struct ec_params_lightbar *param; struct cros_ec_command *msg; int ret; - if (ec != ec_with_lightbar) - return 0; - msg = alloc_lightbar_cmd_msg(ec); if (!msg) return -ENOMEM; @@ -408,25 +406,6 @@ error: return ret; } -EXPORT_SYMBOL(lb_manual_suspend_ctrl); - -int lb_suspend(struct cros_ec_dev *ec) -{ - if (userspace_control || ec != ec_with_lightbar) - return 0; - - return lb_send_empty_cmd(ec, LIGHTBAR_CMD_SUSPEND); -} -EXPORT_SYMBOL(lb_suspend); - -int lb_resume(struct cros_ec_dev *ec) -{ - if (userspace_control || ec != ec_with_lightbar) - return 0; - - return lb_send_empty_cmd(ec, LIGHTBAR_CMD_RESUME); -} -EXPORT_SYMBOL(lb_resume); static ssize_t sequence_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -584,36 +563,91 @@ static struct attribute *__lb_cmds_attrs[] = { NULL, }; -bool ec_has_lightbar(struct cros_ec_dev *ec) +struct attribute_group cros_ec_lightbar_attr_group = { + .name = "lightbar", + .attrs = __lb_cmds_attrs, +}; + +static int cros_ec_lightbar_probe(struct platform_device *pd) { - return !!get_lightbar_version(ec, NULL, NULL); + struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); + struct cros_ec_platform *pdata = dev_get_platdata(ec_dev->dev); + struct device *dev = &pd->dev; + int ret; + + /* + * Only instantiate the lightbar if the EC name is 'cros_ec'. Other EC + * devices like 'cros_pd' doesn't have a lightbar. + */ + if (strcmp(pdata->ec_name, CROS_EC_DEV_NAME) != 0) + return -ENODEV; + + /* + * Ask then for the lightbar version, if it's 0 then the 'cros_ec' + * doesn't have a lightbar. + */ + if (!get_lightbar_version(ec_dev, NULL, NULL)) + return -ENODEV; + + /* Take control of the lightbar from the EC. */ + lb_manual_suspend_ctrl(ec_dev, 1); + + ret = sysfs_create_group(&ec_dev->class_dev.kobj, + &cros_ec_lightbar_attr_group); + if (ret < 0) + dev_err(dev, "failed to create %s attributes. err=%d\n", + cros_ec_lightbar_attr_group.name, ret); + + return ret; } -static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj, - struct attribute *a, int n) +static int cros_ec_lightbar_remove(struct platform_device *pd) { - struct device *dev = container_of(kobj, struct device, kobj); - struct cros_ec_dev *ec = to_cros_ec_dev(dev); - struct platform_device *pdev = to_platform_device(ec->dev); - struct cros_ec_platform *pdata = pdev->dev.platform_data; - int is_cros_ec; + struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); - is_cros_ec = strcmp(pdata->ec_name, CROS_EC_DEV_NAME); + sysfs_remove_group(&ec_dev->class_dev.kobj, + &cros_ec_lightbar_attr_group); - if (is_cros_ec != 0) - return 0; + /* Let the EC take over the lightbar again. */ + lb_manual_suspend_ctrl(ec_dev, 0); - /* Only instantiate this stuff if the EC has a lightbar */ - if (ec_has_lightbar(ec)) { - ec_with_lightbar = ec; - return a->mode; - } return 0; } -struct attribute_group cros_ec_lightbar_attr_group = { - .name = "lightbar", - .attrs = __lb_cmds_attrs, - .is_visible = cros_ec_lightbar_attrs_are_visible, +static int __maybe_unused cros_ec_lightbar_resume(struct device *dev) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(dev); + + if (userspace_control) + return 0; + + return lb_send_empty_cmd(ec_dev, LIGHTBAR_CMD_RESUME); +} + +static int __maybe_unused cros_ec_lightbar_suspend(struct device *dev) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(dev); + + if (userspace_control) + return 0; + + return lb_send_empty_cmd(ec_dev, LIGHTBAR_CMD_SUSPEND); +} + +static SIMPLE_DEV_PM_OPS(cros_ec_lightbar_pm_ops, + cros_ec_lightbar_suspend, cros_ec_lightbar_resume); + +static struct platform_driver cros_ec_lightbar_driver = { + .driver = { + .name = DRV_NAME, + .pm = &cros_ec_lightbar_pm_ops, + }, + .probe = cros_ec_lightbar_probe, + .remove = cros_ec_lightbar_remove, }; -EXPORT_SYMBOL(cros_ec_lightbar_attr_group); + +module_platform_driver(cros_ec_lightbar_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Expose the Chromebook Pixel's lightbar to userspace"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index e1b75775cd4a..14684a56e40f 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -327,7 +327,6 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) static int cros_ec_lpc_remove(struct platform_device *pdev) { - struct cros_ec_device *ec_dev; struct acpi_device *adev; adev = ACPI_COMPANION(&pdev->dev); @@ -335,9 +334,6 @@ static int cros_ec_lpc_remove(struct platform_device *pdev) acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY, cros_ec_lpc_acpi_notify); - ec_dev = platform_get_drvdata(pdev); - cros_ec_remove(ec_dev); - return 0; } diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c index 2060d1483043..6cfbc2835beb 100644 --- a/drivers/platform/chrome/cros_ec_spi.c +++ b/drivers/platform/chrome/cros_ec_spi.c @@ -685,16 +685,6 @@ static int cros_ec_spi_probe(struct spi_device *spi) return 0; } -static int cros_ec_spi_remove(struct spi_device *spi) -{ - struct cros_ec_device *ec_dev; - - ec_dev = spi_get_drvdata(spi); - cros_ec_remove(ec_dev); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static int cros_ec_spi_suspend(struct device *dev) { @@ -733,7 +723,6 @@ static struct spi_driver cros_ec_driver_spi = { .pm = &cros_ec_spi_pm_ops, }, .probe = cros_ec_spi_probe, - .remove = cros_ec_spi_remove, .id_table = cros_ec_spi_id, }; diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index f34a50121064..0ff5aa30c070 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -34,6 +34,8 @@ #include <linux/types.h> #include <linux/uaccess.h> +#define DRV_NAME "cros-ec-sysfs" + /* Accessor functions */ static ssize_t reboot_show(struct device *dev, @@ -353,7 +355,39 @@ struct attribute_group cros_ec_attr_group = { .attrs = __ec_attrs, .is_visible = cros_ec_ctrl_visible, }; -EXPORT_SYMBOL(cros_ec_attr_group); + +static int cros_ec_sysfs_probe(struct platform_device *pd) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); + struct device *dev = &pd->dev; + int ret; + + ret = sysfs_create_group(&ec_dev->class_dev.kobj, &cros_ec_attr_group); + if (ret < 0) + dev_err(dev, "failed to create attributes. err=%d\n", ret); + + return ret; +} + +static int cros_ec_sysfs_remove(struct platform_device *pd) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); + + sysfs_remove_group(&ec_dev->class_dev.kobj, &cros_ec_attr_group); + + return 0; +} + +static struct platform_driver cros_ec_sysfs_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = cros_ec_sysfs_probe, + .remove = cros_ec_sysfs_remove, +}; + +module_platform_driver(cros_ec_sysfs_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("ChromeOS EC control driver"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/platform/chrome/cros_ec_vbc.c b/drivers/platform/chrome/cros_ec_vbc.c index 5356f26bc022..af9bfe6f385c 100644 --- a/drivers/platform/chrome/cros_ec_vbc.c +++ b/drivers/platform/chrome/cros_ec_vbc.c @@ -22,8 +22,11 @@ #include <linux/platform_device.h> #include <linux/mfd/cros_ec.h> #include <linux/mfd/cros_ec_commands.h> +#include <linux/module.h> #include <linux/slab.h> +#define DRV_NAME "cros-ec-vbc" + static ssize_t vboot_context_read(struct file *filp, struct kobject *kobj, struct bin_attribute *att, char *buf, loff_t pos, size_t count) @@ -105,21 +108,6 @@ static ssize_t vboot_context_write(struct file *filp, struct kobject *kobj, return data_sz; } -static umode_t cros_ec_vbc_is_visible(struct kobject *kobj, - struct bin_attribute *a, int n) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct cros_ec_dev *ec = to_cros_ec_dev(dev); - struct device_node *np = ec->ec_dev->dev->of_node; - - if (IS_ENABLED(CONFIG_OF) && np) { - if (of_property_read_bool(np, "google,has-vbc-nvram")) - return a->attr.mode; - } - - return 0; -} - static BIN_ATTR_RW(vboot_context, 16); static struct bin_attribute *cros_ec_vbc_bin_attrs[] = { @@ -130,6 +118,43 @@ static struct bin_attribute *cros_ec_vbc_bin_attrs[] = { struct attribute_group cros_ec_vbc_attr_group = { .name = "vbc", .bin_attrs = cros_ec_vbc_bin_attrs, - .is_bin_visible = cros_ec_vbc_is_visible, }; -EXPORT_SYMBOL(cros_ec_vbc_attr_group); + +static int cros_ec_vbc_probe(struct platform_device *pd) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); + struct device *dev = &pd->dev; + int ret; + + ret = sysfs_create_group(&ec_dev->class_dev.kobj, + &cros_ec_vbc_attr_group); + if (ret < 0) + dev_err(dev, "failed to create %s attributes. err=%d\n", + cros_ec_vbc_attr_group.name, ret); + + return ret; +} + +static int cros_ec_vbc_remove(struct platform_device *pd) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); + + sysfs_remove_group(&ec_dev->class_dev.kobj, + &cros_ec_vbc_attr_group); + + return 0; +} + +static struct platform_driver cros_ec_vbc_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = cros_ec_vbc_probe, + .remove = cros_ec_vbc_remove, +}; + +module_platform_driver(cros_ec_vbc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Expose the vboot context nvram to userspace"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 57f017d74a97..65c3c42b5e7c 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -817,6 +817,18 @@ config STM32_WATCHDOG To compile this driver as a module, choose M here: the module will be called stm32_iwdg. +config STPMIC1_WATCHDOG + tristate "STPMIC1 PMIC watchdog support" + depends on MFD_STPMIC1 + select WATCHDOG_CORE + help + Say Y here to include watchdog support embedded into STPMIC1 PMIC. + If the watchdog timer expires, stpmic1 will shut down all its power + supplies. + + To compile this driver as a module, choose M here: the + module will be called spmic1_wdt. + config UNIPHIER_WATCHDOG tristate "UniPhier watchdog support" depends on ARCH_UNIPHIER || COMPILE_TEST diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index a0917ef28e07..4e78a8c73f0c 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -220,3 +220,4 @@ obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o obj-$(CONFIG_MENZ069_WATCHDOG) += menz69_wdt.o obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o +obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o diff --git a/drivers/watchdog/stpmic1_wdt.c b/drivers/watchdog/stpmic1_wdt.c new file mode 100644 index 000000000000..ad431d8ad95f --- /dev/null +++ b/drivers/watchdog/stpmic1_wdt.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) STMicroelectronics 2018 +// Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics. + +#include <linux/kernel.h> +#include <linux/mfd/stpmic1.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/watchdog.h> + +/* WATCHDOG CONTROL REGISTER bit */ +#define WDT_START BIT(0) +#define WDT_PING BIT(1) +#define WDT_START_MASK BIT(0) +#define WDT_PING_MASK BIT(1) +#define WDT_STOP 0 + +#define PMIC_WDT_MIN_TIMEOUT 1 +#define PMIC_WDT_MAX_TIMEOUT 256 +#define PMIC_WDT_DEFAULT_TIMEOUT 30 + +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) ")"); + +struct stpmic1_wdt { + struct stpmic1 *pmic; + struct watchdog_device wdtdev; +}; + +static int pmic_wdt_start(struct watchdog_device *wdd) +{ + struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd); + + return regmap_update_bits(wdt->pmic->regmap, + WCHDG_CR, WDT_START_MASK, WDT_START); +} + +static int pmic_wdt_stop(struct watchdog_device *wdd) +{ + struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd); + + return regmap_update_bits(wdt->pmic->regmap, + WCHDG_CR, WDT_START_MASK, WDT_STOP); +} + +static int pmic_wdt_ping(struct watchdog_device *wdd) +{ + struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd); + + return regmap_update_bits(wdt->pmic->regmap, + WCHDG_CR, WDT_PING_MASK, WDT_PING); +} + +static int pmic_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd); + + wdd->timeout = timeout; + /* timeout is equal to register value + 1 */ + return regmap_write(wdt->pmic->regmap, WCHDG_TIMER_CR, timeout - 1); +} + +static const struct watchdog_info pmic_watchdog_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "STPMIC1 PMIC Watchdog", +}; + +static const struct watchdog_ops pmic_watchdog_ops = { + .owner = THIS_MODULE, + .start = pmic_wdt_start, + .stop = pmic_wdt_stop, + .ping = pmic_wdt_ping, + .set_timeout = pmic_wdt_set_timeout, +}; + +static int pmic_wdt_probe(struct platform_device *pdev) +{ + int ret; + struct stpmic1 *pmic; + struct stpmic1_wdt *wdt; + + if (!pdev->dev.parent) + return -EINVAL; + + pmic = dev_get_drvdata(pdev->dev.parent); + if (!pmic) + return -EINVAL; + + wdt = devm_kzalloc(&pdev->dev, sizeof(struct stpmic1_wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->pmic = pmic; + + wdt->wdtdev.info = &pmic_watchdog_info; + wdt->wdtdev.ops = &pmic_watchdog_ops; + wdt->wdtdev.min_timeout = PMIC_WDT_MIN_TIMEOUT; + wdt->wdtdev.max_timeout = PMIC_WDT_MAX_TIMEOUT; + wdt->wdtdev.parent = &pdev->dev; + + wdt->wdtdev.timeout = PMIC_WDT_DEFAULT_TIMEOUT; + watchdog_init_timeout(&wdt->wdtdev, 0, &pdev->dev); + + watchdog_set_nowayout(&wdt->wdtdev, nowayout); + watchdog_set_drvdata(&wdt->wdtdev, wdt); + + ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev); + if (ret) + return ret; + + dev_dbg(wdt->pmic->dev, "PMIC Watchdog driver probed\n"); + return 0; +} + +static const struct of_device_id of_pmic_wdt_match[] = { + { .compatible = "st,stpmic1-wdt" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, of_pmic_wdt_match); + +static struct platform_driver stpmic1_wdt_driver = { + .probe = pmic_wdt_probe, + .driver = { + .name = "stpmic1-wdt", + .of_match_table = of_pmic_wdt_match, + }, +}; +module_platform_driver(stpmic1_wdt_driver); + +MODULE_DESCRIPTION("Watchdog driver for STPMIC1 device"); +MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>"); +MODULE_LICENSE("GPL v2"); |