提交 532c2b92 编写于 作者: L Linus Torvalds

Merge tag 'mfd-next-4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd

Pull MFD updates from Lee Jones:
 "New Drivers:
   - Add Cirrus Logic Madera Codec (CS47L35, CS47L85 and CS47L90/91) driver
   - Add ChromeOS EC CEC driver
   - Add ROHM BD71837 PMIC driver

  New Device Support:
   - Add support for Dialog Semi DA9063L PMIC variant to DA9063
   - Add support for Intel Ice Lake to Intel-PLSS-PCI
   - Add support for X-Powers AXP806 to AXP20x

  New Functionality:
   - Add support for USB Charging to the ChromeOS Embedded Controller
   - Add support for HDMI CEC to the ChromeOS Embedded Controller
   - Add support for HDMI CEC to Intel HDMI
   - Add support for accessory detection to Madera devices
   - Allow individual pins to be configured via DT' wlf,csnaddr-pd
   - Provide legacy platform specific EEPROM/Watchdog commands; rave-sp

  Fix-upsL
   - Trivial renaming/spelling fixes; cros_ec, da9063-*
   - Convert to Managed Resources (devm_*); da9063-*, ti_am335x_tscadc
   - Transition to helper macros/functions; da9063-*
   - Constify; kempld-core
   - Improve error path/messages; wm8994-core
   - Disable IRQs locally instead of relying on USB subsystem; dln2
   - Remove unused code; rave-sp
   - New exports; sec-core

  Bug Fixes:
   - Fix possible false I2C transaction error; arizona-core
   - Fix declared memory area size; hi655x-pmic
   - Fix checksum type; rave-sp
   - Fix incorrect default serial port configuration: rave-sp
   - Fix incorrect coherent DMA mask for sub-devices; sm501"

* tag 'mfd-next-4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (60 commits)
  mfd: madera: Add register definitions for accessory detect
  mfd: sm501: Set coherent_dma_mask when creating subdevices
  mfd: bd71837: Devicetree bindings for ROHM BD71837 PMIC
  mfd: bd71837: Core driver for ROHM BD71837 PMIC
  media: platform: cros-ec-cec: Fix dependency on MFD_CROS_EC
  mfd: sec-core: Export OF module alias table
  mfd: as3722: Disable auto-power-on when AC OK
  mfd: axp20x: Support AXP806 in I2C mode
  mfd: axp20x: Add self-working mode support for AXP806
  dt-bindings: mfd: axp20x: Add "self-working" mode for AXP806
  mfd: wm8994: Allow to configure CS/ADDR Pulldown from dts
  mfd: wm8994: Allow to configure Speaker Mode Pullup from dts
  mfd: rave-sp: Emulate CMD_GET_STATUS on device that don't support it
  mfd: rave-sp: Add legacy watchdog ping command translation
  mfd: rave-sp: Add legacy EEPROM access command translation
  mfd: rave-sp: Initialize flow control and parity of the port
  mfd: rave-sp: Fix incorrectly specified checksum type
  mfd: rave-sp: Remove unused defines
  mfd: hi655x: Fix regmap area declared size for hi655x
  mfd: ti_am335x_tscadc: Fix struct clk memory leak
  ...
......@@ -20,6 +20,8 @@ Optional properties:
- ams,enable-internal-i2c-pullup: Boolean property, to enable internal pullup on
i2c scl/sda pins. Missing this will disable internal pullup on i2c
scl/sda lines.
- ams,enable-ac-ok-power-on: Boolean property, to enable exit out of power off
mode with AC_OK pin (pin enabled in power off mode).
Optional submodule and their properties:
=======================================
......
......@@ -44,8 +44,11 @@ Optional properties:
board is driving OTG VBus or not.
(axp221 / axp223 / axp803/ axp813 only)
- x-powers,master-mode: Boolean (axp806 only). Set this when the PMIC is
wired for master mode. The default is slave mode.
- x-powers,self-working-mode and
x-powers,master-mode: Boolean (axp806 only). Set either of these when the
PMIC is wired for self-working mode or master mode.
If neither is set then slave mode is assumed.
This corresponds to how the MODESET pin is wired.
- <input>-supply: a phandle to the regulator supply node. May be omitted if
inputs are unregulated, such as using the IPSOUT output
......
Cirrus Logic Madera class audio codecs Multi-Functional Device
These devices are audio SoCs with extensive digital capabilities and a range
of analogue I/O.
See also the child driver bindings in:
bindings/pinctrl/cirrus,madera-pinctrl.txt
bindings/regulator/arizona-regulator.txt
bindings/sound/madera.txt
Required properties:
- compatible : One of the following chip-specific strings:
"cirrus,cs47l35"
"cirrus,cs47l85"
"cirrus,cs47l90"
"cirrus,cs47l91"
"cirrus,wm1840"
- reg : I2C slave address when connected using I2C, chip select number when
using SPI.
- DCVDD-supply : Power supply for the device as defined in
bindings/regulator/regulator.txt
Mandatory on CS47L35, CS47L90, CS47L91
Optional on CS47L85, WM1840
- AVDD-supply, DBVDD1-supply, DBVDD2-supply, CPVDD1-supply, CPVDD2-supply :
Power supplies for the device
- DBVDD3-supply, DBVDD4-supply : Power supplies for the device
(CS47L85, CS47L90, CS47L91, WM1840)
- SPKVDDL-supply, SPKVDDR-supply : Power supplies for the device
(CS47L85, WM1840)
- SPKVDD-supply : Power supply for the device
(CS47L35)
- interrupt-controller : Indicates that this device is an interrupt controller
- #interrupt-cells: the number of cells to describe an IRQ, must be 2.
The first cell is the IRQ number.
The second cell is the flags, encoded as the trigger masks from
bindings/interrupt-controller/interrupts.txt
- gpio-controller : Indicates this device is a GPIO controller.
- #gpio-cells : Must be 2. The first cell is the pin number. The second cell
is reserved for future use and must be zero
- interrupt-parent : The parent interrupt controller.
- interrupts : The interrupt line the /IRQ signal for the device is
connected to.
Optional properties:
- MICVDD-supply : Power supply, only need to be specified if
powered externally
- reset-gpios : One entry specifying the GPIO controlling /RESET.
As defined in bindings/gpio.txt.
Although optional, it is strongly recommended to use a hardware reset
- MICBIASx : Initial data for the MICBIAS regulators, as covered in
Documentation/devicetree/bindings/regulator/regulator.txt.
One for each MICBIAS generator (MICBIAS1, MICBIAS2, ...)
(all codecs)
One for each output pin (MICBIAS1A, MIBCIAS1B, MICBIAS2A, ...)
(all except CS47L85, WM1840)
The following following additional property is supported for the generator
nodes:
- cirrus,ext-cap : Set to 1 if the MICBIAS has external decoupling
capacitors attached.
Optional child nodes:
micvdd : Node containing initialization data for the micvdd regulator
See bindings/regulator/arizona-regulator.txt
ldo1 : Node containing initialization data for the LDO1 regulator
See bindings/regulator/arizona-regulator.txt
(cs47l85, wm1840)
Example:
cs47l85@0 {
compatible = "cirrus,cs47l85";
reg = <0>;
reset-gpios = <&gpio 0>;
interrupt-controller;
#interrupt-cells = <2>;
interrupts = <&host_irq1>;
interrupt-parent = <&gic>;
gpio-controller;
#gpio-cells = <2>;
};
* ROHM BD71837 Power Management Integrated Circuit bindings
BD71837MWV is a programmable Power Management IC for powering single-core,
dual-core, and quad-core SoCs such as NXP-i.MX 8M. It is optimized for
low BOM cost and compact solution footprint. It integrates 8 Buck
egulators and 7 LDOs to provide all the power rails required by the SoC and
the commonly used peripherals.
Datasheet for PMIC is available at:
https://www.rohm.com/datasheet/BD71837MWV/bd71837mwv-e
Required properties:
- compatible : Should be "rohm,bd71837".
- reg : I2C slave address.
- interrupt-parent : Phandle to the parent interrupt controller.
- interrupts : The interrupt line the device is connected to.
- clocks : The parent clock connected to PMIC. If this is missing
32768 KHz clock is assumed.
- #clock-cells : Should be 0.
- regulators: : List of child nodes that specify the regulators.
Please see ../regulator/rohm,bd71837-regulator.txt
Optional properties:
- clock-output-names : Should contain name for output clock.
Example:
/* external oscillator node */
osc: oscillator {
compatible = "fixed-clock";
#clock-cells = <1>;
clock-frequency = <32768>;
clock-output-names = "osc";
};
pmic: pmic@4b {
compatible = "rohm,bd71837";
reg = <0x4b>;
interrupt-parent = <&gpio1>;
interrupts = <29 GPIO_ACTIVE_LOW>;
interrupt-names = "irq";
#clock-cells = <0>;
clocks = <&osc 0>;
clock-output-names = "bd71837-32k-out";
regulators {
buck1: BUCK1 {
regulator-name = "buck1";
regulator-min-microvolt = <700000>;
regulator-max-microvolt = <1300000>;
regulator-boot-on;
regulator-ramp-delay = <1250>;
};
};
};
/* Clock consumer node */
rtc@0 {
compatible = "company,my-rtc";
clock-names = "my-clock";
clocks = <&pmic>;
};
......@@ -22,7 +22,7 @@ Required properties:
The valid regulator-compatible values are:
tps65910: vrtc, vio, vdd1, vdd2, vdd3, vdig1, vdig2, vpll, vdac, vaux1,
vaux2, vaux33, vmmc, vbb
tps65911: vrtc, vio, vdd1, vdd3, vddctrl, ldo1, ldo2, ldo3, ldo4, ldo5,
tps65911: vrtc, vio, vdd1, vdd2, vddctrl, ldo1, ldo2, ldo3, ldo4, ldo5,
ldo6, ldo7, ldo8
- xxx-supply: Input voltage supply regulator.
......
Cirrus Logic Madera class audio codecs pinctrl driver
The Cirrus Logic Madera codecs provide a number of GPIO functions for
interfacing to external hardware and to provide logic outputs to other devices.
Certain groups of GPIO pins also have an alternate function, normally as an
audio interface.
The set of available GPIOs, functions and alternate function groups differs
between codecs so refer to the datasheet for the codec for further information
on what is supported on that device.
The properties for this driver exist within the parent MFD driver node.
See also
the core bindings for the parent MFD driver:
Documentation/devicetree/bindings/mfd/madera.txt
the generic pinmix bindings:
Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
Required properties of parent mfd node:
- pinctrl-names : must be "default"
- pinctrl-0 : a phandle to the node containing the subnodes containing default
configurations
Required subnodes:
One subnode is required to contain the default settings. It contains an
arbitrary number of configuration subnodes, one for each group or pin
configuration you want to apply as a default.
Required properties of configuration subnodes:
- groups : name of one pin group to configure. One of:
aif1, aif2, aif3, aif4, mif1, mif2, mif3, pdmspk1, pdmspk2,
dmic4, dmic5, dmic6,
gpio1, gpio2, ..., gpio40
The gpioN groups select the single pin of this name for configuration
Optional properties of configuration subnodes:
Any configuration option not explicitly listed in the dts will be left at
chip default setting.
- function : name of function to assign to this group. One of:
aif1, aif2, aif3, aif4, mif1, mif2, mif3, pdmspk1, pdmspk2,
dmic3, dmic4, dmic5, dmic6,
io, dsp-gpio, irq1, irq2,
fll1-clk, fll1-lock, fll2-clk, fll2-lock, fll3-clk, fll3-lock,
fllao-clk, fllao-lock,
opclk, opclk-async, pwm1, pwm2, spdif,
asrc1-in1-lock, asrc1-in2-lock, asrc2-in1-lock, asrc2-in2-lock,
spkl-short-circuit, spkr-short-circuit, spk-shutdown,
spk-overheat-shutdown, spk-overheat-warn,
timer1-sts, timer2-sts, timer3-sts, timer4-sts, timer5-sts, timer6-sts,
timer7-sts, timer8-sts,
log1-fifo-ne, log2-fifo-ne, log3-fifo-ne, log4-fifo-ne, log5-fifo-ne,
log6-fifo-ne, log7-fifo-ne, log8-fifo-ne,
- bias-disable : disable pull-up and pull-down
- bias-bus-hold : enable buskeeper
- bias-pull-up : output is pulled-up
- bias-pull-down : output is pulled-down
- drive-push-pull : CMOS output
- drive-open-drain : open-drain output
- drive-strength : drive strength in mA. Valid values are 4 or 8
- input-schmitt-enable : enable schmitt-trigger mode
- input-schmitt-disable : disable schmitt-trigger mode
- input-debounce : A value of 0 disables debounce, a value !=0 enables
debounce
- output-low : set the pin to output mode with low level
- output-high : set the pin to output mode with high level
Example:
cs47l85@0 {
compatible = "cirrus,cs47l85";
pinctrl-names = "default";
pinctrl-0 = <&cs47l85_defaults>;
cs47l85_defaults: cs47l85-gpio-defaults {
aif1 {
groups = "aif1";
function = "aif1";
bias-bus-hold;
};
aif2 {
groups = "aif2";
function = "aif2";
bias-bus-hold;
};
opclk {
groups = "gpio1";
function = "opclk";
bias-pull-up;
drive-strength = <8>;
};
};
};
......@@ -3562,6 +3562,22 @@ M: Christian Benvenuti <benve@cisco.com>
S: Supported
F: drivers/infiniband/hw/usnic/
CIRRUS LOGIC MADERA CODEC DRIVERS
M: Charles Keepax <ckeepax@opensource.cirrus.com>
M: Richard Fitzgerald <rf@opensource.cirrus.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
L: patches@opensource.cirrus.com
T: git https://github.com/CirrusLogic/linux-drivers.git
W: https://github.com/CirrusLogic/linux-drivers/wiki
S: Supported
F: Documentation/devicetree/bindings/mfd/madera.txt
F: Documentation/devicetree/bindings/pinctrl/cirrus,madera-pinctrl.txt
F: include/linux/mfd/madera/*
F: drivers/gpio/gpio-madera*
F: drivers/mfd/madera*
F: drivers/mfd/cs47l*
F: drivers/pinctrl/cirrus/*
CLANG-FORMAT FILE
M: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
S: Maintained
......
......@@ -155,8 +155,8 @@ CONFIG_THERMAL_EMULATION=y
CONFIG_WATCHDOG=y
CONFIG_S3C2410_WATCHDOG=y
CONFIG_MFD_CROS_EC=y
CONFIG_MFD_CROS_EC_I2C=y
CONFIG_MFD_CROS_EC_SPI=y
CONFIG_CROS_EC_I2C=y
CONFIG_CROS_EC_SPI=y
CONFIG_MFD_MAX14577=y
CONFIG_MFD_MAX77686=y
CONFIG_MFD_MAX77693=y
......
......@@ -490,8 +490,8 @@ CONFIG_MFD_AC100=y
CONFIG_MFD_AXP20X_I2C=y
CONFIG_MFD_AXP20X_RSB=y
CONFIG_MFD_CROS_EC=m
CONFIG_MFD_CROS_EC_I2C=m
CONFIG_MFD_CROS_EC_SPI=m
CONFIG_CROS_EC_I2C=m
CONFIG_CROS_EC_SPI=m
CONFIG_MFD_DA9063=m
CONFIG_MFD_MAX14577=y
CONFIG_MFD_MAX77686=y
......
......@@ -398,8 +398,8 @@ CONFIG_MFD_AS3711=y
CONFIG_MFD_BCM590XX=m
CONFIG_MFD_AXP20X=y
CONFIG_MFD_CROS_EC=m
CONFIG_MFD_CROS_EC_I2C=m
CONFIG_MFD_CROS_EC_SPI=m
CONFIG_CROS_EC_I2C=m
CONFIG_CROS_EC_SPI=m
CONFIG_MFD_ASIC3=y
CONFIG_PMIC_DA903X=y
CONFIG_HTC_EGPIO=y
......
......@@ -373,8 +373,8 @@ CONFIG_UNIPHIER_WATCHDOG=y
CONFIG_BCM2835_WDT=y
CONFIG_MFD_AXP20X_RSB=y
CONFIG_MFD_CROS_EC=y
CONFIG_MFD_CROS_EC_I2C=y
CONFIG_MFD_CROS_EC_SPI=y
CONFIG_CROS_EC_I2C=y
CONFIG_CROS_EC_SPI=y
CONFIG_MFD_CROS_EC_CHARDEV=m
CONFIG_MFD_EXYNOS_LPASS=m
CONFIG_MFD_HI6421_PMIC=y
......
......@@ -1049,6 +1049,12 @@ config GPIO_LP87565
This driver can also be built as a module. If so, the module will be
called gpio-lp87565.
config GPIO_MADERA
tristate "Cirrus Logic Madera class codecs"
depends on PINCTRL_MADERA
help
Support for GPIOs on Cirrus Logic Madera class codecs.
config GPIO_MAX77620
tristate "GPIO support for PMIC MAX77620 and MAX20024"
depends on MFD_MAX77620
......
......@@ -71,6 +71,7 @@ obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o
obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o
obj-$(CONFIG_GPIO_LP87565) += gpio-lp87565.o
obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o
obj-$(CONFIG_GPIO_MADERA) += gpio-madera.o
obj-$(CONFIG_GPIO_MAX3191X) += gpio-max3191x.o
obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o
obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o
......
// SPDX-License-Identifier: GPL-2.0
/*
* GPIO support for Cirrus Logic Madera codecs
*
* Copyright (C) 2015-2018 Cirrus Logic
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation; version 2.
*/
#include <linux/gpio/driver.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mfd/madera/core.h>
#include <linux/mfd/madera/pdata.h>
#include <linux/mfd/madera/registers.h>
struct madera_gpio {
struct madera *madera;
/* storage space for the gpio_chip we're using */
struct gpio_chip gpio_chip;
};
static int madera_gpio_get_direction(struct gpio_chip *chip,
unsigned int offset)
{
struct madera_gpio *madera_gpio = gpiochip_get_data(chip);
struct madera *madera = madera_gpio->madera;
unsigned int reg_offset = 2 * offset;
unsigned int val;
int ret;
ret = regmap_read(madera->regmap, MADERA_GPIO1_CTRL_2 + reg_offset,
&val);
if (ret < 0)
return ret;
return !!(val & MADERA_GP1_DIR_MASK);
}
static int madera_gpio_direction_in(struct gpio_chip *chip, unsigned int offset)
{
struct madera_gpio *madera_gpio = gpiochip_get_data(chip);
struct madera *madera = madera_gpio->madera;
unsigned int reg_offset = 2 * offset;
return regmap_update_bits(madera->regmap,
MADERA_GPIO1_CTRL_2 + reg_offset,
MADERA_GP1_DIR_MASK, MADERA_GP1_DIR);
}
static int madera_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct madera_gpio *madera_gpio = gpiochip_get_data(chip);
struct madera *madera = madera_gpio->madera;
unsigned int reg_offset = 2 * offset;
unsigned int val;
int ret;
ret = regmap_read(madera->regmap, MADERA_GPIO1_CTRL_1 + reg_offset,
&val);
if (ret < 0)
return ret;
return !!(val & MADERA_GP1_LVL_MASK);
}
static int madera_gpio_direction_out(struct gpio_chip *chip,
unsigned int offset, int value)
{
struct madera_gpio *madera_gpio = gpiochip_get_data(chip);
struct madera *madera = madera_gpio->madera;
unsigned int reg_offset = 2 * offset;
unsigned int reg_val = value ? MADERA_GP1_LVL : 0;
int ret;
ret = regmap_update_bits(madera->regmap,
MADERA_GPIO1_CTRL_2 + reg_offset,
MADERA_GP1_DIR_MASK, 0);
if (ret < 0)
return ret;
return regmap_update_bits(madera->regmap,
MADERA_GPIO1_CTRL_1 + reg_offset,
MADERA_GP1_LVL_MASK, reg_val);
}
static void madera_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct madera_gpio *madera_gpio = gpiochip_get_data(chip);
struct madera *madera = madera_gpio->madera;
unsigned int reg_offset = 2 * offset;
unsigned int reg_val = value ? MADERA_GP1_LVL : 0;
int ret;
ret = regmap_update_bits(madera->regmap,
MADERA_GPIO1_CTRL_1 + reg_offset,
MADERA_GP1_LVL_MASK, reg_val);
/* set() doesn't return an error so log a warning */
if (ret)
dev_warn(madera->dev, "Failed to write to 0x%x (%d)\n",
MADERA_GPIO1_CTRL_1 + reg_offset, ret);
}
static struct gpio_chip madera_gpio_chip = {
.label = "madera",
.owner = THIS_MODULE,
.request = gpiochip_generic_request,
.free = gpiochip_generic_free,
.get_direction = madera_gpio_get_direction,
.direction_input = madera_gpio_direction_in,
.get = madera_gpio_get,
.direction_output = madera_gpio_direction_out,
.set = madera_gpio_set,
.set_config = gpiochip_generic_config,
.can_sleep = true,
};
static int madera_gpio_probe(struct platform_device *pdev)
{
struct madera *madera = dev_get_drvdata(pdev->dev.parent);
struct madera_pdata *pdata = dev_get_platdata(madera->dev);
struct madera_gpio *madera_gpio;
int ret;
madera_gpio = devm_kzalloc(&pdev->dev, sizeof(*madera_gpio),
GFP_KERNEL);
if (!madera_gpio)
return -ENOMEM;
madera_gpio->madera = madera;
/* Construct suitable gpio_chip from the template in madera_gpio_chip */
madera_gpio->gpio_chip = madera_gpio_chip;
madera_gpio->gpio_chip.parent = pdev->dev.parent;
switch (madera->type) {
case CS47L35:
madera_gpio->gpio_chip.ngpio = CS47L35_NUM_GPIOS;
break;
case CS47L85:
case WM1840:
madera_gpio->gpio_chip.ngpio = CS47L85_NUM_GPIOS;
break;
case CS47L90:
case CS47L91:
madera_gpio->gpio_chip.ngpio = CS47L90_NUM_GPIOS;
break;
default:
dev_err(&pdev->dev, "Unknown chip variant %d\n", madera->type);
return -EINVAL;
}
/* We want to be usable on systems that don't use devicetree or acpi */
if (pdata && pdata->gpio_base)
madera_gpio->gpio_chip.base = pdata->gpio_base;
else
madera_gpio->gpio_chip.base = -1;
ret = devm_gpiochip_add_data(&pdev->dev,
&madera_gpio->gpio_chip,
madera_gpio);
if (ret < 0) {
dev_dbg(&pdev->dev, "Could not register gpiochip, %d\n", ret);
return ret;
}
/*
* This is part of a composite MFD device which can only be used with
* the corresponding pinctrl driver. On all supported silicon the GPIO
* to pinctrl mapping is fixed in the silicon, so we register it
* explicitly instead of requiring a redundant gpio-ranges in the
* devicetree.
* In any case we also want to work on systems that don't use devicetree
* or acpi.
*/
ret = gpiochip_add_pin_range(&madera_gpio->gpio_chip, "madera-pinctrl",
0, 0, madera_gpio->gpio_chip.ngpio);
if (ret) {
dev_dbg(&pdev->dev, "Failed to add pin range (%d)\n", ret);
return ret;
}
return 0;
}
static struct platform_driver madera_gpio_driver = {
.driver = {
.name = "madera-gpio",
},
.probe = madera_gpio_probe,
};
module_platform_driver(madera_gpio_driver);
MODULE_SOFTDEP("pre: pinctrl-madera");
MODULE_DESCRIPTION("GPIO interface for Madera codecs");
MODULE_AUTHOR("Nariman Poushin <nariman@opensource.cirrus.com>");
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:madera-gpio");
......@@ -24,6 +24,7 @@ config DRM_I915
select IOSF_MBI
select CRC32
select SND_HDA_I915 if SND_HDA_CORE
select CEC_CORE if CEC_NOTIFIER
help
Choose this option if you have a system that has "Intel Graphics
Media Accelerator" or "HD Graphics" integrated graphics,
......
......@@ -126,6 +126,30 @@ enum port {
#define port_name(p) ((p) + 'A')
/*
* Ports identifier referenced from other drivers.
* Expected to remain stable over time
*/
static inline const char *port_identifier(enum port port)
{
switch (port) {
case PORT_A:
return "Port A";
case PORT_B:
return "Port B";
case PORT_C:
return "Port C";
case PORT_D:
return "Port D";
case PORT_E:
return "Port E";
case PORT_F:
return "Port F";
default:
return "<invalid>";
}
}
enum tc_port {
PORT_TC_NONE = -1,
......
......@@ -39,6 +39,7 @@
#include <drm/drm_dp_mst_helper.h>
#include <drm/drm_rect.h>
#include <drm/drm_atomic.h>
#include <media/cec-notifier.h>
/**
* __wait_for - magic wait macro
......@@ -1016,6 +1017,7 @@ struct intel_hdmi {
bool has_audio;
bool rgb_quant_range_selectable;
struct intel_connector *attached_connector;
struct cec_notifier *cec_notifier;
};
struct intel_dp_mst_encoder;
......
......@@ -1899,6 +1899,8 @@ intel_hdmi_set_edid(struct drm_connector *connector)
connected = true;
}
cec_notifier_set_phys_addr_from_edid(intel_hdmi->cec_notifier, edid);
return connected;
}
......@@ -1907,6 +1909,7 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
{
enum drm_connector_status status;
struct drm_i915_private *dev_priv = to_i915(connector->dev);
struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.id, connector->name);
......@@ -1922,6 +1925,9 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
if (status != connector_status_connected)
cec_notifier_phys_addr_invalidate(intel_hdmi->cec_notifier);
return status;
}
......@@ -2062,6 +2068,8 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder,
static void intel_hdmi_destroy(struct drm_connector *connector)
{
if (intel_attached_hdmi(connector)->cec_notifier)
cec_notifier_put(intel_attached_hdmi(connector)->cec_notifier);
kfree(to_intel_connector(connector)->detect_edid);
drm_connector_cleanup(connector);
kfree(connector);
......@@ -2382,6 +2390,11 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
u32 temp = I915_READ(PEG_BAND_GAP_DATA);
I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
}
intel_hdmi->cec_notifier = cec_notifier_get_conn(dev->dev,
port_identifier(port));
if (!intel_hdmi->cec_notifier)
DRM_DEBUG_KMS("CEC notifier get failed\n");
}
void intel_hdmi_init(struct drm_i915_private *dev_priv,
......
......@@ -721,7 +721,7 @@ config KEYBOARD_CROS_EC
help
Say Y here to enable the matrix keyboard used by ChromeOS devices
and implemented on the ChromeOS EC. You must enable one bus option
(MFD_CROS_EC_I2C or MFD_CROS_EC_SPI) to use this.
(CROS_EC_I2C or CROS_EC_SPI) to use this.
To compile this driver as a module, choose M here: the
module will be called cros_ec_keyb.
......
......@@ -21,6 +21,7 @@ struct cec_notifier {
struct list_head head;
struct kref kref;
struct device *dev;
const char *conn;
struct cec_adapter *cec_adap;
void (*callback)(struct cec_adapter *adap, u16 pa);
......@@ -30,13 +31,14 @@ struct cec_notifier {
static LIST_HEAD(cec_notifiers);
static DEFINE_MUTEX(cec_notifiers_lock);
struct cec_notifier *cec_notifier_get(struct device *dev)
struct cec_notifier *cec_notifier_get_conn(struct device *dev, const char *conn)
{
struct cec_notifier *n;
mutex_lock(&cec_notifiers_lock);
list_for_each_entry(n, &cec_notifiers, head) {
if (n->dev == dev) {
if (n->dev == dev &&
(!conn || !strcmp(n->conn, conn))) {
kref_get(&n->kref);
mutex_unlock(&cec_notifiers_lock);
return n;
......@@ -46,6 +48,8 @@ struct cec_notifier *cec_notifier_get(struct device *dev)
if (!n)
goto unlock;
n->dev = dev;
if (conn)
n->conn = kstrdup(conn, GFP_KERNEL);
n->phys_addr = CEC_PHYS_ADDR_INVALID;
mutex_init(&n->lock);
kref_init(&n->kref);
......@@ -54,7 +58,7 @@ struct cec_notifier *cec_notifier_get(struct device *dev)
mutex_unlock(&cec_notifiers_lock);
return n;
}
EXPORT_SYMBOL_GPL(cec_notifier_get);
EXPORT_SYMBOL_GPL(cec_notifier_get_conn);
static void cec_notifier_release(struct kref *kref)
{
......@@ -62,6 +66,7 @@ static void cec_notifier_release(struct kref *kref)
container_of(kref, struct cec_notifier, kref);
list_del(&n->head);
kfree(n->conn);
kfree(n);
}
......
......@@ -536,6 +536,17 @@ menuconfig CEC_PLATFORM_DRIVERS
if CEC_PLATFORM_DRIVERS
config VIDEO_CROS_EC_CEC
tristate "ChromeOS EC CEC driver"
depends on MFD_CROS_EC
select CEC_CORE
select CEC_NOTIFIER
---help---
If you say yes here you will get support for the
ChromeOS Embedded Controller's CEC.
The CEC bus is present in the HDMI connector and enables communication
between compatible devices.
config VIDEO_MESON_AO_CEC
tristate "Amlogic Meson AO CEC driver"
depends on ARCH_MESON || COMPILE_TEST
......
......@@ -94,3 +94,5 @@ obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss/
obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/
obj-y += meson/
obj-y += cros-ec-cec/
obj-$(CONFIG_VIDEO_CROS_EC_CEC) += cros-ec-cec.o
// SPDX-License-Identifier: GPL-2.0+
/*
* CEC driver for ChromeOS Embedded Controller
*
* Copyright (c) 2018 BayLibre, SAS
* Author: Neil Armstrong <narmstrong@baylibre.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dmi.h>
#include <linux/pci.h>
#include <linux/cec.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <media/cec.h>
#include <media/cec-notifier.h>
#include <linux/mfd/cros_ec.h>
#include <linux/mfd/cros_ec_commands.h>
#define DRV_NAME "cros-ec-cec"
/**
* struct cros_ec_cec - Driver data for EC CEC
*
* @cros_ec: Pointer to EC device
* @notifier: Notifier info for responding to EC events
* @adap: CEC adapter
* @notify: CEC notifier pointer
* @rx_msg: storage for a received message
*/
struct cros_ec_cec {
struct cros_ec_device *cros_ec;
struct notifier_block notifier;
struct cec_adapter *adap;
struct cec_notifier *notify;
struct cec_msg rx_msg;
};
static void handle_cec_message(struct cros_ec_cec *cros_ec_cec)
{
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
uint8_t *cec_message = cros_ec->event_data.data.cec_message;
unsigned int len = cros_ec->event_size;
cros_ec_cec->rx_msg.len = len;
memcpy(cros_ec_cec->rx_msg.msg, cec_message, len);
cec_received_msg(cros_ec_cec->adap, &cros_ec_cec->rx_msg);
}
static void handle_cec_event(struct cros_ec_cec *cros_ec_cec)
{
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
uint32_t events = cros_ec->event_data.data.cec_events;
if (events & EC_MKBP_CEC_SEND_OK)
cec_transmit_attempt_done(cros_ec_cec->adap,
CEC_TX_STATUS_OK);
/* FW takes care of all retries, tell core to avoid more retries */
if (events & EC_MKBP_CEC_SEND_FAILED)
cec_transmit_attempt_done(cros_ec_cec->adap,
CEC_TX_STATUS_MAX_RETRIES |
CEC_TX_STATUS_NACK);
}
static int cros_ec_cec_event(struct notifier_block *nb,
unsigned long queued_during_suspend,
void *_notify)
{
struct cros_ec_cec *cros_ec_cec;
struct cros_ec_device *cros_ec;
cros_ec_cec = container_of(nb, struct cros_ec_cec, notifier);
cros_ec = cros_ec_cec->cros_ec;
if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_EVENT) {
handle_cec_event(cros_ec_cec);
return NOTIFY_OK;
}
if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_MESSAGE) {
handle_cec_message(cros_ec_cec);
return NOTIFY_OK;
}
return NOTIFY_DONE;
}
static int cros_ec_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr)
{
struct cros_ec_cec *cros_ec_cec = adap->priv;
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
struct {
struct cros_ec_command msg;
struct ec_params_cec_set data;
} __packed msg = {};
int ret;
msg.msg.command = EC_CMD_CEC_SET;
msg.msg.outsize = sizeof(msg.data);
msg.data.cmd = CEC_CMD_LOGICAL_ADDRESS;
msg.data.val = logical_addr;
ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
if (ret < 0) {
dev_err(cros_ec->dev,
"error setting CEC logical address on EC: %d\n", ret);
return ret;
}
return 0;
}
static int cros_ec_cec_transmit(struct cec_adapter *adap, u8 attempts,
u32 signal_free_time, struct cec_msg *cec_msg)
{
struct cros_ec_cec *cros_ec_cec = adap->priv;
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
struct {
struct cros_ec_command msg;
struct ec_params_cec_write data;
} __packed msg = {};
int ret;
msg.msg.command = EC_CMD_CEC_WRITE_MSG;
msg.msg.outsize = cec_msg->len;
memcpy(msg.data.msg, cec_msg->msg, cec_msg->len);
ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
if (ret < 0) {
dev_err(cros_ec->dev,
"error writing CEC msg on EC: %d\n", ret);
return ret;
}
return 0;
}
static int cros_ec_cec_adap_enable(struct cec_adapter *adap, bool enable)
{
struct cros_ec_cec *cros_ec_cec = adap->priv;
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
struct {
struct cros_ec_command msg;
struct ec_params_cec_set data;
} __packed msg = {};
int ret;
msg.msg.command = EC_CMD_CEC_SET;
msg.msg.outsize = sizeof(msg.data);
msg.data.cmd = CEC_CMD_ENABLE;
msg.data.val = enable;
ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
if (ret < 0) {
dev_err(cros_ec->dev,
"error %sabling CEC on EC: %d\n",
(enable ? "en" : "dis"), ret);
return ret;
}
return 0;
}
static const struct cec_adap_ops cros_ec_cec_ops = {
.adap_enable = cros_ec_cec_adap_enable,
.adap_log_addr = cros_ec_cec_set_log_addr,
.adap_transmit = cros_ec_cec_transmit,
};
#ifdef CONFIG_PM_SLEEP
static int cros_ec_cec_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
if (device_may_wakeup(dev))
enable_irq_wake(cros_ec_cec->cros_ec->irq);
return 0;
}
static int cros_ec_cec_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
if (device_may_wakeup(dev))
disable_irq_wake(cros_ec_cec->cros_ec->irq);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(cros_ec_cec_pm_ops,
cros_ec_cec_suspend, cros_ec_cec_resume);
#if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI)
/*
* The Firmware only handles a single CEC interface tied to a single HDMI
* connector we specify along with the DRM device name handling the HDMI output
*/
struct cec_dmi_match {
char *sys_vendor;
char *product_name;
char *devname;
char *conn;
};
static const struct cec_dmi_match cec_dmi_match_table[] = {
/* Google Fizz */
{ "Google", "Fizz", "0000:00:02.0", "Port B" },
};
static int cros_ec_cec_get_notifier(struct device *dev,
struct cec_notifier **notify)
{
int i;
for (i = 0 ; i < ARRAY_SIZE(cec_dmi_match_table) ; ++i) {
const struct cec_dmi_match *m = &cec_dmi_match_table[i];
if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&
dmi_match(DMI_PRODUCT_NAME, m->product_name)) {
struct device *d;
/* Find the device, bail out if not yet registered */
d = bus_find_device_by_name(&pci_bus_type, NULL,
m->devname);
if (!d)
return -EPROBE_DEFER;
*notify = cec_notifier_get_conn(d, m->conn);
return 0;
}
}
/* Hardware support must be added in the cec_dmi_match_table */
dev_warn(dev, "CEC notifier not configured for this hardware\n");
return -ENODEV;
}
#else
static int cros_ec_cec_get_notifier(struct device *dev,
struct cec_notifier **notify)
{
return -ENODEV;
}
#endif
static int cros_ec_cec_probe(struct platform_device *pdev)
{
struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
struct cros_ec_device *cros_ec = ec_dev->ec_dev;
struct cros_ec_cec *cros_ec_cec;
int ret;
cros_ec_cec = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_cec),
GFP_KERNEL);
if (!cros_ec_cec)
return -ENOMEM;
platform_set_drvdata(pdev, cros_ec_cec);
cros_ec_cec->cros_ec = cros_ec;
ret = cros_ec_cec_get_notifier(&pdev->dev, &cros_ec_cec->notify);
if (ret)
return ret;
ret = device_init_wakeup(&pdev->dev, 1);
if (ret) {
dev_err(&pdev->dev, "failed to initialize wakeup\n");
return ret;
}
cros_ec_cec->adap = cec_allocate_adapter(&cros_ec_cec_ops, cros_ec_cec,
DRV_NAME, CEC_CAP_DEFAULTS, 1);
if (IS_ERR(cros_ec_cec->adap))
return PTR_ERR(cros_ec_cec->adap);
/* Get CEC events from the EC. */
cros_ec_cec->notifier.notifier_call = cros_ec_cec_event;
ret = blocking_notifier_chain_register(&cros_ec->event_notifier,
&cros_ec_cec->notifier);
if (ret) {
dev_err(&pdev->dev, "failed to register notifier\n");
cec_delete_adapter(cros_ec_cec->adap);
return ret;
}
ret = cec_register_adapter(cros_ec_cec->adap, &pdev->dev);
if (ret < 0) {
cec_delete_adapter(cros_ec_cec->adap);
return ret;
}
cec_register_cec_notifier(cros_ec_cec->adap, cros_ec_cec->notify);
return 0;
}
static int cros_ec_cec_remove(struct platform_device *pdev)
{
struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
int ret;
ret = blocking_notifier_chain_unregister(
&cros_ec_cec->cros_ec->event_notifier,
&cros_ec_cec->notifier);
if (ret) {
dev_err(dev, "failed to unregister notifier\n");
return ret;
}
cec_unregister_adapter(cros_ec_cec->adap);
if (cros_ec_cec->notify)
cec_notifier_put(cros_ec_cec->notify);
return 0;
}
static struct platform_driver cros_ec_cec_driver = {
.probe = cros_ec_cec_probe,
.remove = cros_ec_cec_remove,
.driver = {
.name = DRV_NAME,
.pm = &cros_ec_cec_pm_ops,
},
};
module_platform_driver(cros_ec_cec_driver);
MODULE_DESCRIPTION("CEC driver for ChromeOS ECs");
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
......@@ -202,26 +202,6 @@ config MFD_CROS_EC
You also need to enable the driver for the bus you are using. The
protocol for talking to the EC is defined by the bus driver.
config MFD_CROS_EC_I2C
tristate "ChromeOS Embedded Controller (I2C)"
depends on MFD_CROS_EC && I2C
help
If you say Y here, you get support for talking to the ChromeOS
EC through an I2C bus. This uses a simple byte-level protocol with
a checksum. Failing accesses will be retried three times to
improve reliability.
config MFD_CROS_EC_SPI
tristate "ChromeOS Embedded Controller (SPI)"
depends on MFD_CROS_EC && SPI
---help---
If you say Y here, you get support for talking to the ChromeOS EC
through a SPI bus, using a byte-level protocol. Since the EC's
response time cannot be guaranteed, we support ignoring
'pre-amble' bytes before the response actually starts.
config MFD_CROS_EC_CHARDEV
tristate "Chrome OS Embedded Controller userspace device interface"
depends on MFD_CROS_EC
......@@ -232,6 +212,56 @@ config MFD_CROS_EC_CHARDEV
If you have a supported Chromebook, choose Y or M here.
The module will be called cros_ec_dev.
config MFD_MADERA
tristate "Cirrus Logic Madera codecs"
select MFD_CORE
select REGMAP
select REGMAP_IRQ
select MADERA_IRQ
select PINCTRL
select PINCTRL_MADERA
help
Support for the Cirrus Logic Madera platform audio codecs
config MFD_MADERA_I2C
tristate "Cirrus Logic Madera codecs with I2C"
depends on MFD_MADERA
depends on I2C
select REGMAP_I2C
help
Support for the Cirrus Logic Madera platform audio SoC
core functionality controlled via I2C.
config MFD_MADERA_SPI
tristate "Cirrus Logic Madera codecs with SPI"
depends on MFD_MADERA
depends on SPI_MASTER
select REGMAP_SPI
help
Support for the Cirrus Logic Madera platform audio SoC
core functionality controlled via SPI.
config MFD_CS47L35
bool "Cirrus Logic CS47L35"
select PINCTRL_CS47L35
depends on MFD_MADERA
help
Support for Cirrus Logic CS47L35 Smart Codec
config MFD_CS47L85
bool "Cirrus Logic CS47L85"
select PINCTRL_CS47L85
depends on MFD_MADERA
help
Support for Cirrus Logic CS47L85 Smart Codec
config MFD_CS47L90
bool "Cirrus Logic CS47L90/91"
select PINCTRL_CS47L90
depends on MFD_MADERA
help
Support for Cirrus Logic CS47L90 and CS47L91 Smart Codecs
config MFD_ASIC3
bool "Compaq ASIC3"
depends on GPIOLIB && ARM
......@@ -1787,6 +1817,19 @@ config MFD_STW481X
in various ST Microelectronics and ST-Ericsson embedded
Nomadik series.
config MFD_ROHM_BD718XX
tristate "ROHM BD71837 Power Management IC"
depends on I2C=y
depends on OF
select REGMAP_I2C
select REGMAP_IRQ
select MFD_CORE
help
Select this option to get support for the ROHM BD71837
Power Management ICs. BD71837 is designed to power processors like
NXP i.MX8. It contains 8 BUCK outputs and 7 LDOs, voltage monitoring
and emergency shut down as well as 32,768KHz clock output.
config MFD_STM32_LPTIMER
tristate "Support for STM32 Low-Power Timer"
depends on (ARCH_STM32 && OF) || COMPILE_TEST
......
......@@ -14,8 +14,6 @@ obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o
cros_ec_core-objs := cros_ec.o
obj-$(CONFIG_MFD_CROS_EC) += cros_ec_core.o
obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o
obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o
obj-$(CONFIG_MFD_CROS_EC_CHARDEV) += cros_ec_dev.o
obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o
......@@ -72,6 +70,20 @@ wm8994-objs := wm8994-core.o wm8994-irq.o wm8994-regmap.o
obj-$(CONFIG_MFD_WM8994) += wm8994.o
obj-$(CONFIG_MFD_WM97xx) += wm97xx-core.o
madera-objs := madera-core.o
ifeq ($(CONFIG_MFD_CS47L35),y)
madera-objs += cs47l35-tables.o
endif
ifeq ($(CONFIG_MFD_CS47L85),y)
madera-objs += cs47l85-tables.o
endif
ifeq ($(CONFIG_MFD_CS47L90),y)
madera-objs += cs47l90-tables.o
endif
obj-$(CONFIG_MFD_MADERA) += madera.o
obj-$(CONFIG_MFD_MADERA_I2C) += madera-i2c.o
obj-$(CONFIG_MFD_MADERA_SPI) += madera-spi.o
obj-$(CONFIG_TPS6105X) += tps6105x.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_TPS6507X) += tps6507x.o
......@@ -227,4 +239,5 @@ obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o
obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o
obj-$(CONFIG_MFD_SC27XX_PMIC) += sprd-sc27xx-spi.o
obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o
obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o
......@@ -24,6 +24,7 @@
#include <linux/regulator/consumer.h>
#include <linux/regulator/machine.h>
#include <linux/slab.h>
#include <linux/ktime.h>
#include <linux/platform_device.h>
#include <linux/mfd/arizona/core.h>
......@@ -236,22 +237,39 @@ static irqreturn_t arizona_overclocked(int irq, void *data)
#define ARIZONA_REG_POLL_DELAY_US 7500
static inline bool arizona_poll_reg_delay(ktime_t timeout)
{
if (ktime_compare(ktime_get(), timeout) > 0)
return false;
usleep_range(ARIZONA_REG_POLL_DELAY_US / 2, ARIZONA_REG_POLL_DELAY_US);
return true;
}
static int arizona_poll_reg(struct arizona *arizona,
int timeout_ms, unsigned int reg,
unsigned int mask, unsigned int target)
{
ktime_t timeout = ktime_add_us(ktime_get(), timeout_ms * USEC_PER_MSEC);
unsigned int val = 0;
int ret;
ret = regmap_read_poll_timeout(arizona->regmap,
reg, val, ((val & mask) == target),
ARIZONA_REG_POLL_DELAY_US,
timeout_ms * 1000);
if (ret)
dev_err(arizona->dev, "Polling reg 0x%x timed out: %x\n",
reg, val);
do {
ret = regmap_read(arizona->regmap, reg, &val);
return ret;
if ((val & mask) == target)
return 0;
} while (arizona_poll_reg_delay(timeout));
if (ret) {
dev_err(arizona->dev, "Failed polling reg 0x%x: %d\n",
reg, ret);
return ret;
}
dev_err(arizona->dev, "Polling reg 0x%x timed out: %x\n", reg, val);
return -ETIMEDOUT;
}
static int arizona_wait_for_boot(struct arizona *arizona)
......
......@@ -349,6 +349,8 @@ static int as3722_i2c_of_probe(struct i2c_client *i2c,
"ams,enable-internal-int-pullup");
as3722->en_intern_i2c_pullup = of_property_read_bool(np,
"ams,enable-internal-i2c-pullup");
as3722->en_ac_ok_pwr_on = of_property_read_bool(np,
"ams,enable-ac-ok-power-on");
as3722->irq_flags = irqd_get_trigger_type(irq_data);
dev_dbg(&i2c->dev, "IRQ flags are 0x%08lx\n", as3722->irq_flags);
return 0;
......@@ -360,6 +362,7 @@ static int as3722_i2c_probe(struct i2c_client *i2c,
struct as3722 *as3722;
unsigned long irq_flags;
int ret;
u8 val = 0;
as3722 = devm_kzalloc(&i2c->dev, sizeof(struct as3722), GFP_KERNEL);
if (!as3722)
......@@ -398,6 +401,15 @@ static int as3722_i2c_probe(struct i2c_client *i2c,
if (ret < 0)
return ret;
if (as3722->en_ac_ok_pwr_on)
val = AS3722_CTRL_SEQU1_AC_OK_PWR_ON;
ret = as3722_update_bits(as3722, AS3722_CTRL_SEQU1_REG,
AS3722_CTRL_SEQU1_AC_OK_PWR_ON, val);
if (ret < 0) {
dev_err(as3722->dev, "CTRLsequ1 update failed: %d\n", ret);
return ret;
}
ret = devm_mfd_add_devices(&i2c->dev, -1, as3722_devs,
ARRAY_SIZE(as3722_devs), NULL, 0,
regmap_irq_get_domain(as3722->irq_data));
......
......@@ -65,6 +65,7 @@ static const struct of_device_id axp20x_i2c_of_match[] = {
{ .compatible = "x-powers,axp202", .data = (void *)AXP202_ID },
{ .compatible = "x-powers,axp209", .data = (void *)AXP209_ID },
{ .compatible = "x-powers,axp221", .data = (void *)AXP221_ID },
{ .compatible = "x-powers,axp806", .data = (void *)AXP806_ID },
{ },
};
MODULE_DEVICE_TABLE(of, axp20x_i2c_of_match);
......@@ -74,6 +75,7 @@ static const struct i2c_device_id axp20x_i2c_id[] = {
{ "axp202", 0 },
{ "axp209", 0 },
{ "axp221", 0 },
{ "axp806", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id);
......
......@@ -221,6 +221,11 @@ static const struct resource axp803_pek_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP803_IRQ_PEK_FAL_EDGE, "PEK_DBF"),
};
static const struct resource axp806_pek_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP806_IRQ_POK_RISE, "PEK_DBR"),
DEFINE_RES_IRQ_NAMED(AXP806_IRQ_POK_FALL, "PEK_DBF"),
};
static const struct resource axp809_pek_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP809_IRQ_PEK_RIS_EDGE, "PEK_DBR"),
DEFINE_RES_IRQ_NAMED(AXP809_IRQ_PEK_FAL_EDGE, "PEK_DBF"),
......@@ -730,6 +735,15 @@ static const struct mfd_cell axp803_cells[] = {
{ .name = "axp20x-regulator" },
};
static const struct mfd_cell axp806_self_working_cells[] = {
{
.name = "axp221-pek",
.num_resources = ARRAY_SIZE(axp806_pek_resources),
.resources = axp806_pek_resources,
},
{ .name = "axp20x-regulator" },
};
static const struct mfd_cell axp806_cells[] = {
{
.id = 2,
......@@ -842,8 +856,14 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
axp20x->regmap_irq_chip = &axp803_regmap_irq_chip;
break;
case AXP806_ID:
axp20x->nr_cells = ARRAY_SIZE(axp806_cells);
axp20x->cells = axp806_cells;
if (of_property_read_bool(axp20x->dev->of_node,
"x-powers,self-working-mode")) {
axp20x->nr_cells = ARRAY_SIZE(axp806_self_working_cells);
axp20x->cells = axp806_self_working_cells;
} else {
axp20x->nr_cells = ARRAY_SIZE(axp806_cells);
axp20x->cells = axp806_cells;
}
axp20x->regmap_cfg = &axp806_regmap_config;
axp20x->regmap_irq_chip = &axp806_regmap_irq_chip;
break;
......@@ -901,7 +921,9 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
*/
if (axp20x->variant == AXP806_ID) {
if (of_property_read_bool(axp20x->dev->of_node,
"x-powers,master-mode"))
"x-powers,master-mode") ||
of_property_read_bool(axp20x->dev->of_node,
"x-powers,self-working-mode"))
regmap_write(axp20x->regmap, AXP806_REG_ADDR_EXT,
AXP806_REG_ADDR_EXT_ADDR_MASTER_MODE);
else
......
......@@ -378,10 +378,18 @@ static void cros_ec_sensors_register(struct cros_ec_dev *ec)
kfree(msg);
}
static const struct mfd_cell cros_ec_cec_cells[] = {
{ .name = "cros-ec-cec" }
};
static const struct mfd_cell cros_ec_rtc_cells[] = {
{ .name = "cros-ec-rtc" }
};
static const struct mfd_cell cros_usbpd_charger_cells[] = {
{ .name = "cros-usbpd-charger" }
};
static int ec_device_probe(struct platform_device *pdev)
{
int retval = -ENOMEM;
......@@ -420,6 +428,18 @@ static int ec_device_probe(struct platform_device *pdev)
if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
cros_ec_sensors_register(ec);
/* Check whether this EC instance has CEC host command support */
if (cros_ec_check_features(ec, EC_FEATURE_CEC)) {
retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
cros_ec_cec_cells,
ARRAY_SIZE(cros_ec_cec_cells),
NULL, 0, NULL);
if (retval)
dev_err(ec->dev,
"failed to add cros-ec-cec device: %d\n",
retval);
}
/* Check whether this EC instance has RTC host command support */
if (cros_ec_check_features(ec, EC_FEATURE_RTC)) {
retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
......@@ -432,6 +452,18 @@ static int ec_device_probe(struct platform_device *pdev)
retval);
}
/* Check whether this EC instance has the PD charge manager */
if (cros_ec_check_features(ec, EC_FEATURE_USB_PD)) {
retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
cros_usbpd_charger_cells,
ARRAY_SIZE(cros_usbpd_charger_cells),
NULL, 0, NULL);
if (retval)
dev_err(ec->dev,
"failed to add cros-usbpd-charger device: %d\n",
retval);
}
/* Take control of the lightbar from the EC. */
lb_manual_suspend_ctrl(ec, 1);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -76,7 +76,7 @@ static struct resource da9063_hwmon_resources[] = {
};
static const struct mfd_cell da9063_devs[] = {
static const struct mfd_cell da9063_common_devs[] = {
{
.name = DA9063_DRVNAME_REGULATORS,
.num_resources = ARRAY_SIZE(da9063_regulators_resources),
......@@ -100,15 +100,19 @@ static const struct mfd_cell da9063_devs[] = {
.resources = da9063_onkey_resources,
.of_compatible = "dlg,da9063-onkey",
},
{
.name = DA9063_DRVNAME_VIBRATION,
},
};
/* Only present on DA9063 , not on DA9063L */
static const struct mfd_cell da9063_devs[] = {
{
.name = DA9063_DRVNAME_RTC,
.num_resources = ARRAY_SIZE(da9063_rtc_resources),
.resources = da9063_rtc_resources,
.of_compatible = "dlg,da9063-rtc",
},
{
.name = DA9063_DRVNAME_VIBRATION,
},
};
static int da9063_clear_fault_log(struct da9063 *da9063)
......@@ -192,7 +196,7 @@ int da9063_device_init(struct da9063 *da9063, unsigned int irq)
dev_err(da9063->dev, "Cannot read chip model id.\n");
return -EIO;
}
if (model != PMIC_DA9063) {
if (model != PMIC_CHIP_ID_DA9063) {
dev_err(da9063->dev, "Invalid chip model id: 0x%02x\n", model);
return -ENODEV;
}
......@@ -215,7 +219,6 @@ int da9063_device_init(struct da9063 *da9063, unsigned int irq)
return -ENODEV;
}
da9063->model = model;
da9063->variant_code = variant_code;
ret = da9063_irq_init(da9063);
......@@ -226,19 +229,26 @@ int da9063_device_init(struct da9063 *da9063, unsigned int irq)
da9063->irq_base = regmap_irq_chip_get_base(da9063->regmap_irq);
ret = mfd_add_devices(da9063->dev, -1, da9063_devs,
ARRAY_SIZE(da9063_devs), NULL, da9063->irq_base,
NULL);
if (ret)
dev_err(da9063->dev, "Cannot add MFD cells\n");
ret = devm_mfd_add_devices(da9063->dev, PLATFORM_DEVID_NONE,
da9063_common_devs,
ARRAY_SIZE(da9063_common_devs),
NULL, da9063->irq_base, NULL);
if (ret) {
dev_err(da9063->dev, "Failed to add child devices\n");
return ret;
}
return ret;
}
if (da9063->type == PMIC_TYPE_DA9063) {
ret = devm_mfd_add_devices(da9063->dev, PLATFORM_DEVID_NONE,
da9063_devs, ARRAY_SIZE(da9063_devs),
NULL, da9063->irq_base, NULL);
if (ret) {
dev_err(da9063->dev, "Failed to add child devices\n");
return ret;
}
}
void da9063_device_exit(struct da9063 *da9063)
{
mfd_remove_devices(da9063->dev);
da9063_irq_exit(da9063);
return ret;
}
MODULE_DESCRIPTION("PMIC driver for Dialog DA9063");
......
......@@ -29,78 +29,33 @@
#include <linux/regulator/of_regulator.h>
static const struct regmap_range da9063_ad_readable_ranges[] = {
{
.range_min = DA9063_REG_PAGE_CON,
.range_max = DA9063_AD_REG_SECOND_D,
}, {
.range_min = DA9063_REG_SEQ,
.range_max = DA9063_REG_ID_32_31,
}, {
.range_min = DA9063_REG_SEQ_A,
.range_max = DA9063_REG_AUTO3_LOW,
}, {
.range_min = DA9063_REG_T_OFFSET,
.range_max = DA9063_AD_REG_GP_ID_19,
}, {
.range_min = DA9063_REG_CHIP_ID,
.range_max = DA9063_REG_CHIP_VARIANT,
},
regmap_reg_range(DA9063_REG_PAGE_CON, DA9063_AD_REG_SECOND_D),
regmap_reg_range(DA9063_REG_SEQ, DA9063_REG_ID_32_31),
regmap_reg_range(DA9063_REG_SEQ_A, DA9063_REG_AUTO3_LOW),
regmap_reg_range(DA9063_REG_T_OFFSET, DA9063_AD_REG_GP_ID_19),
regmap_reg_range(DA9063_REG_CHIP_ID, DA9063_REG_CHIP_VARIANT),
};
static const struct regmap_range da9063_ad_writeable_ranges[] = {
{
.range_min = DA9063_REG_PAGE_CON,
.range_max = DA9063_REG_PAGE_CON,
}, {
.range_min = DA9063_REG_FAULT_LOG,
.range_max = DA9063_REG_VSYS_MON,
}, {
.range_min = DA9063_REG_COUNT_S,
.range_max = DA9063_AD_REG_ALARM_Y,
}, {
.range_min = DA9063_REG_SEQ,
.range_max = DA9063_REG_ID_32_31,
}, {
.range_min = DA9063_REG_SEQ_A,
.range_max = DA9063_REG_AUTO3_LOW,
}, {
.range_min = DA9063_REG_CONFIG_I,
.range_max = DA9063_AD_REG_MON_REG_4,
}, {
.range_min = DA9063_AD_REG_GP_ID_0,
.range_max = DA9063_AD_REG_GP_ID_19,
},
regmap_reg_range(DA9063_REG_PAGE_CON, DA9063_REG_PAGE_CON),
regmap_reg_range(DA9063_REG_FAULT_LOG, DA9063_REG_VSYS_MON),
regmap_reg_range(DA9063_REG_COUNT_S, DA9063_AD_REG_ALARM_Y),
regmap_reg_range(DA9063_REG_SEQ, DA9063_REG_ID_32_31),
regmap_reg_range(DA9063_REG_SEQ_A, DA9063_REG_AUTO3_LOW),
regmap_reg_range(DA9063_REG_CONFIG_I, DA9063_AD_REG_MON_REG_4),
regmap_reg_range(DA9063_AD_REG_GP_ID_0, DA9063_AD_REG_GP_ID_19),
};
static const struct regmap_range da9063_ad_volatile_ranges[] = {
{
.range_min = DA9063_REG_PAGE_CON,
.range_max = DA9063_REG_EVENT_D,
}, {
.range_min = DA9063_REG_CONTROL_A,
.range_max = DA9063_REG_CONTROL_B,
}, {
.range_min = DA9063_REG_CONTROL_E,
.range_max = DA9063_REG_CONTROL_F,
}, {
.range_min = DA9063_REG_BCORE2_CONT,
.range_max = DA9063_REG_LDO11_CONT,
}, {
.range_min = DA9063_REG_DVC_1,
.range_max = DA9063_REG_ADC_MAN,
}, {
.range_min = DA9063_REG_ADC_RES_L,
.range_max = DA9063_AD_REG_SECOND_D,
}, {
.range_min = DA9063_REG_SEQ,
.range_max = DA9063_REG_SEQ,
}, {
.range_min = DA9063_REG_EN_32K,
.range_max = DA9063_REG_EN_32K,
}, {
.range_min = DA9063_AD_REG_MON_REG_5,
.range_max = DA9063_AD_REG_MON_REG_6,
},
regmap_reg_range(DA9063_REG_PAGE_CON, DA9063_REG_EVENT_D),
regmap_reg_range(DA9063_REG_CONTROL_A, DA9063_REG_CONTROL_B),
regmap_reg_range(DA9063_REG_CONTROL_E, DA9063_REG_CONTROL_F),
regmap_reg_range(DA9063_REG_BCORE2_CONT, DA9063_REG_LDO11_CONT),
regmap_reg_range(DA9063_REG_DVC_1, DA9063_REG_ADC_MAN),
regmap_reg_range(DA9063_REG_ADC_RES_L, DA9063_AD_REG_SECOND_D),
regmap_reg_range(DA9063_REG_SEQ, DA9063_REG_SEQ),
regmap_reg_range(DA9063_REG_EN_32K, DA9063_REG_EN_32K),
regmap_reg_range(DA9063_AD_REG_MON_REG_5, DA9063_AD_REG_MON_REG_6),
};
static const struct regmap_access_table da9063_ad_readable_table = {
......@@ -119,78 +74,33 @@ static const struct regmap_access_table da9063_ad_volatile_table = {
};
static const struct regmap_range da9063_bb_readable_ranges[] = {
{
.range_min = DA9063_REG_PAGE_CON,
.range_max = DA9063_BB_REG_SECOND_D,
}, {
.range_min = DA9063_REG_SEQ,
.range_max = DA9063_REG_ID_32_31,
}, {
.range_min = DA9063_REG_SEQ_A,
.range_max = DA9063_REG_AUTO3_LOW,
}, {
.range_min = DA9063_REG_T_OFFSET,
.range_max = DA9063_BB_REG_GP_ID_19,
}, {
.range_min = DA9063_REG_CHIP_ID,
.range_max = DA9063_REG_CHIP_VARIANT,
},
regmap_reg_range(DA9063_REG_PAGE_CON, DA9063_BB_REG_SECOND_D),
regmap_reg_range(DA9063_REG_SEQ, DA9063_REG_ID_32_31),
regmap_reg_range(DA9063_REG_SEQ_A, DA9063_REG_AUTO3_LOW),
regmap_reg_range(DA9063_REG_T_OFFSET, DA9063_BB_REG_GP_ID_19),
regmap_reg_range(DA9063_REG_CHIP_ID, DA9063_REG_CHIP_VARIANT),
};
static const struct regmap_range da9063_bb_writeable_ranges[] = {
{
.range_min = DA9063_REG_PAGE_CON,
.range_max = DA9063_REG_PAGE_CON,
}, {
.range_min = DA9063_REG_FAULT_LOG,
.range_max = DA9063_REG_VSYS_MON,
}, {
.range_min = DA9063_REG_COUNT_S,
.range_max = DA9063_BB_REG_ALARM_Y,
}, {
.range_min = DA9063_REG_SEQ,
.range_max = DA9063_REG_ID_32_31,
}, {
.range_min = DA9063_REG_SEQ_A,
.range_max = DA9063_REG_AUTO3_LOW,
}, {
.range_min = DA9063_REG_CONFIG_I,
.range_max = DA9063_BB_REG_MON_REG_4,
}, {
.range_min = DA9063_BB_REG_GP_ID_0,
.range_max = DA9063_BB_REG_GP_ID_19,
},
regmap_reg_range(DA9063_REG_PAGE_CON, DA9063_REG_PAGE_CON),
regmap_reg_range(DA9063_REG_FAULT_LOG, DA9063_REG_VSYS_MON),
regmap_reg_range(DA9063_REG_COUNT_S, DA9063_BB_REG_ALARM_Y),
regmap_reg_range(DA9063_REG_SEQ, DA9063_REG_ID_32_31),
regmap_reg_range(DA9063_REG_SEQ_A, DA9063_REG_AUTO3_LOW),
regmap_reg_range(DA9063_REG_CONFIG_I, DA9063_BB_REG_MON_REG_4),
regmap_reg_range(DA9063_BB_REG_GP_ID_0, DA9063_BB_REG_GP_ID_19),
};
static const struct regmap_range da9063_bb_volatile_ranges[] = {
{
.range_min = DA9063_REG_PAGE_CON,
.range_max = DA9063_REG_EVENT_D,
}, {
.range_min = DA9063_REG_CONTROL_A,
.range_max = DA9063_REG_CONTROL_B,
}, {
.range_min = DA9063_REG_CONTROL_E,
.range_max = DA9063_REG_CONTROL_F,
}, {
.range_min = DA9063_REG_BCORE2_CONT,
.range_max = DA9063_REG_LDO11_CONT,
}, {
.range_min = DA9063_REG_DVC_1,
.range_max = DA9063_REG_ADC_MAN,
}, {
.range_min = DA9063_REG_ADC_RES_L,
.range_max = DA9063_BB_REG_SECOND_D,
}, {
.range_min = DA9063_REG_SEQ,
.range_max = DA9063_REG_SEQ,
}, {
.range_min = DA9063_REG_EN_32K,
.range_max = DA9063_REG_EN_32K,
}, {
.range_min = DA9063_BB_REG_MON_REG_5,
.range_max = DA9063_BB_REG_MON_REG_6,
},
regmap_reg_range(DA9063_REG_PAGE_CON, DA9063_REG_EVENT_D),
regmap_reg_range(DA9063_REG_CONTROL_A, DA9063_REG_CONTROL_B),
regmap_reg_range(DA9063_REG_CONTROL_E, DA9063_REG_CONTROL_F),
regmap_reg_range(DA9063_REG_BCORE2_CONT, DA9063_REG_LDO11_CONT),
regmap_reg_range(DA9063_REG_DVC_1, DA9063_REG_ADC_MAN),
regmap_reg_range(DA9063_REG_ADC_RES_L, DA9063_BB_REG_SECOND_D),
regmap_reg_range(DA9063_REG_SEQ, DA9063_REG_SEQ),
regmap_reg_range(DA9063_REG_EN_32K, DA9063_REG_EN_32K),
regmap_reg_range(DA9063_BB_REG_MON_REG_5, DA9063_BB_REG_MON_REG_6),
};
static const struct regmap_access_table da9063_bb_readable_table = {
......@@ -208,6 +118,50 @@ static const struct regmap_access_table da9063_bb_volatile_table = {
.n_yes_ranges = ARRAY_SIZE(da9063_bb_volatile_ranges),
};
static const struct regmap_range da9063l_bb_readable_ranges[] = {
regmap_reg_range(DA9063_REG_PAGE_CON, DA9063_REG_MON_A10_RES),
regmap_reg_range(DA9063_REG_SEQ, DA9063_REG_ID_32_31),
regmap_reg_range(DA9063_REG_SEQ_A, DA9063_REG_AUTO3_LOW),
regmap_reg_range(DA9063_REG_T_OFFSET, DA9063_BB_REG_GP_ID_19),
regmap_reg_range(DA9063_REG_CHIP_ID, DA9063_REG_CHIP_VARIANT),
};
static const struct regmap_range da9063l_bb_writeable_ranges[] = {
regmap_reg_range(DA9063_REG_PAGE_CON, DA9063_REG_PAGE_CON),
regmap_reg_range(DA9063_REG_FAULT_LOG, DA9063_REG_VSYS_MON),
regmap_reg_range(DA9063_REG_SEQ, DA9063_REG_ID_32_31),
regmap_reg_range(DA9063_REG_SEQ_A, DA9063_REG_AUTO3_LOW),
regmap_reg_range(DA9063_REG_CONFIG_I, DA9063_BB_REG_MON_REG_4),
regmap_reg_range(DA9063_BB_REG_GP_ID_0, DA9063_BB_REG_GP_ID_19),
};
static const struct regmap_range da9063l_bb_volatile_ranges[] = {
regmap_reg_range(DA9063_REG_PAGE_CON, DA9063_REG_EVENT_D),
regmap_reg_range(DA9063_REG_CONTROL_A, DA9063_REG_CONTROL_B),
regmap_reg_range(DA9063_REG_CONTROL_E, DA9063_REG_CONTROL_F),
regmap_reg_range(DA9063_REG_BCORE2_CONT, DA9063_REG_LDO11_CONT),
regmap_reg_range(DA9063_REG_DVC_1, DA9063_REG_ADC_MAN),
regmap_reg_range(DA9063_REG_ADC_RES_L, DA9063_REG_MON_A10_RES),
regmap_reg_range(DA9063_REG_SEQ, DA9063_REG_SEQ),
regmap_reg_range(DA9063_REG_EN_32K, DA9063_REG_EN_32K),
regmap_reg_range(DA9063_BB_REG_MON_REG_5, DA9063_BB_REG_MON_REG_6),
};
static const struct regmap_access_table da9063l_bb_readable_table = {
.yes_ranges = da9063l_bb_readable_ranges,
.n_yes_ranges = ARRAY_SIZE(da9063l_bb_readable_ranges),
};
static const struct regmap_access_table da9063l_bb_writeable_table = {
.yes_ranges = da9063l_bb_writeable_ranges,
.n_yes_ranges = ARRAY_SIZE(da9063l_bb_writeable_ranges),
};
static const struct regmap_access_table da9063l_bb_volatile_table = {
.yes_ranges = da9063l_bb_volatile_ranges,
.n_yes_ranges = ARRAY_SIZE(da9063l_bb_volatile_ranges),
};
static const struct regmap_range_cfg da9063_range_cfg[] = {
{
.range_min = DA9063_REG_PAGE_CON,
......@@ -232,11 +186,12 @@ static struct regmap_config da9063_regmap_config = {
static const struct of_device_id da9063_dt_ids[] = {
{ .compatible = "dlg,da9063", },
{ .compatible = "dlg,da9063l", },
{ }
};
MODULE_DEVICE_TABLE(of, da9063_dt_ids);
static int da9063_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
const struct i2c_device_id *id)
{
struct da9063 *da9063;
int ret;
......@@ -248,11 +203,16 @@ static int da9063_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, da9063);
da9063->dev = &i2c->dev;
da9063->chip_irq = i2c->irq;
da9063->type = id->driver_data;
if (da9063->variant_code == PMIC_DA9063_AD) {
da9063_regmap_config.rd_table = &da9063_ad_readable_table;
da9063_regmap_config.wr_table = &da9063_ad_writeable_table;
da9063_regmap_config.volatile_table = &da9063_ad_volatile_table;
} else if (da9063->type == PMIC_TYPE_DA9063L) {
da9063_regmap_config.rd_table = &da9063l_bb_readable_table;
da9063_regmap_config.wr_table = &da9063l_bb_writeable_table;
da9063_regmap_config.volatile_table = &da9063l_bb_volatile_table;
} else {
da9063_regmap_config.rd_table = &da9063_bb_readable_table;
da9063_regmap_config.wr_table = &da9063_bb_writeable_table;
......@@ -270,17 +230,9 @@ static int da9063_i2c_probe(struct i2c_client *i2c,
return da9063_device_init(da9063, i2c->irq);
}
static int da9063_i2c_remove(struct i2c_client *i2c)
{
struct da9063 *da9063 = i2c_get_clientdata(i2c);
da9063_device_exit(da9063);
return 0;
}
static const struct i2c_device_id da9063_i2c_id[] = {
{"da9063", PMIC_DA9063},
{ "da9063", PMIC_TYPE_DA9063 },
{ "da9063l", PMIC_TYPE_DA9063L },
{},
};
MODULE_DEVICE_TABLE(i2c, da9063_i2c_id);
......@@ -291,7 +243,6 @@ static struct i2c_driver da9063_i2c_driver = {
.of_match_table = of_match_ptr(da9063_dt_ids),
},
.probe = da9063_i2c_probe,
.remove = da9063_i2c_remove,
.id_table = da9063_i2c_id,
};
......
......@@ -28,132 +28,145 @@
static const struct regmap_irq da9063_irqs[] = {
/* DA9063 event A register */
[DA9063_IRQ_ONKEY] = {
.reg_offset = DA9063_REG_EVENT_A_OFFSET,
.mask = DA9063_M_ONKEY,
},
[DA9063_IRQ_ALARM] = {
.reg_offset = DA9063_REG_EVENT_A_OFFSET,
.mask = DA9063_M_ALARM,
},
[DA9063_IRQ_TICK] = {
.reg_offset = DA9063_REG_EVENT_A_OFFSET,
.mask = DA9063_M_TICK,
},
[DA9063_IRQ_ADC_RDY] = {
.reg_offset = DA9063_REG_EVENT_A_OFFSET,
.mask = DA9063_M_ADC_RDY,
},
[DA9063_IRQ_SEQ_RDY] = {
.reg_offset = DA9063_REG_EVENT_A_OFFSET,
.mask = DA9063_M_SEQ_RDY,
},
REGMAP_IRQ_REG(DA9063_IRQ_ONKEY,
DA9063_REG_EVENT_A_OFFSET, DA9063_M_ONKEY),
REGMAP_IRQ_REG(DA9063_IRQ_ALARM,
DA9063_REG_EVENT_A_OFFSET, DA9063_M_ALARM),
REGMAP_IRQ_REG(DA9063_IRQ_TICK,
DA9063_REG_EVENT_A_OFFSET, DA9063_M_TICK),
REGMAP_IRQ_REG(DA9063_IRQ_ADC_RDY,
DA9063_REG_EVENT_A_OFFSET, DA9063_M_ADC_RDY),
REGMAP_IRQ_REG(DA9063_IRQ_SEQ_RDY,
DA9063_REG_EVENT_A_OFFSET, DA9063_M_SEQ_RDY),
/* DA9063 event B register */
[DA9063_IRQ_WAKE] = {
.reg_offset = DA9063_REG_EVENT_B_OFFSET,
.mask = DA9063_M_WAKE,
},
[DA9063_IRQ_TEMP] = {
.reg_offset = DA9063_REG_EVENT_B_OFFSET,
.mask = DA9063_M_TEMP,
},
[DA9063_IRQ_COMP_1V2] = {
.reg_offset = DA9063_REG_EVENT_B_OFFSET,
.mask = DA9063_M_COMP_1V2,
},
[DA9063_IRQ_LDO_LIM] = {
.reg_offset = DA9063_REG_EVENT_B_OFFSET,
.mask = DA9063_M_LDO_LIM,
},
[DA9063_IRQ_REG_UVOV] = {
.reg_offset = DA9063_REG_EVENT_B_OFFSET,
.mask = DA9063_M_UVOV,
},
[DA9063_IRQ_DVC_RDY] = {
.reg_offset = DA9063_REG_EVENT_B_OFFSET,
.mask = DA9063_M_DVC_RDY,
},
[DA9063_IRQ_VDD_MON] = {
.reg_offset = DA9063_REG_EVENT_B_OFFSET,
.mask = DA9063_M_VDD_MON,
},
[DA9063_IRQ_WARN] = {
.reg_offset = DA9063_REG_EVENT_B_OFFSET,
.mask = DA9063_M_VDD_WARN,
},
REGMAP_IRQ_REG(DA9063_IRQ_WAKE,
DA9063_REG_EVENT_B_OFFSET, DA9063_M_WAKE),
REGMAP_IRQ_REG(DA9063_IRQ_TEMP,
DA9063_REG_EVENT_B_OFFSET, DA9063_M_TEMP),
REGMAP_IRQ_REG(DA9063_IRQ_COMP_1V2,
DA9063_REG_EVENT_B_OFFSET, DA9063_M_COMP_1V2),
REGMAP_IRQ_REG(DA9063_IRQ_LDO_LIM,
DA9063_REG_EVENT_B_OFFSET, DA9063_M_LDO_LIM),
REGMAP_IRQ_REG(DA9063_IRQ_REG_UVOV,
DA9063_REG_EVENT_B_OFFSET, DA9063_M_UVOV),
REGMAP_IRQ_REG(DA9063_IRQ_DVC_RDY,
DA9063_REG_EVENT_B_OFFSET, DA9063_M_DVC_RDY),
REGMAP_IRQ_REG(DA9063_IRQ_VDD_MON,
DA9063_REG_EVENT_B_OFFSET, DA9063_M_VDD_MON),
REGMAP_IRQ_REG(DA9063_IRQ_WARN,
DA9063_REG_EVENT_B_OFFSET, DA9063_M_VDD_WARN),
/* DA9063 event C register */
[DA9063_IRQ_GPI0] = {
.reg_offset = DA9063_REG_EVENT_C_OFFSET,
.mask = DA9063_M_GPI0,
},
[DA9063_IRQ_GPI1] = {
.reg_offset = DA9063_REG_EVENT_C_OFFSET,
.mask = DA9063_M_GPI1,
},
[DA9063_IRQ_GPI2] = {
.reg_offset = DA9063_REG_EVENT_C_OFFSET,
.mask = DA9063_M_GPI2,
},
[DA9063_IRQ_GPI3] = {
.reg_offset = DA9063_REG_EVENT_C_OFFSET,
.mask = DA9063_M_GPI3,
},
[DA9063_IRQ_GPI4] = {
.reg_offset = DA9063_REG_EVENT_C_OFFSET,
.mask = DA9063_M_GPI4,
},
[DA9063_IRQ_GPI5] = {
.reg_offset = DA9063_REG_EVENT_C_OFFSET,
.mask = DA9063_M_GPI5,
},
[DA9063_IRQ_GPI6] = {
.reg_offset = DA9063_REG_EVENT_C_OFFSET,
.mask = DA9063_M_GPI6,
},
[DA9063_IRQ_GPI7] = {
.reg_offset = DA9063_REG_EVENT_C_OFFSET,
.mask = DA9063_M_GPI7,
},
REGMAP_IRQ_REG(DA9063_IRQ_GPI0,
DA9063_REG_EVENT_C_OFFSET, DA9063_M_GPI0),
REGMAP_IRQ_REG(DA9063_IRQ_GPI1,
DA9063_REG_EVENT_C_OFFSET, DA9063_M_GPI1),
REGMAP_IRQ_REG(DA9063_IRQ_GPI2,
DA9063_REG_EVENT_C_OFFSET, DA9063_M_GPI2),
REGMAP_IRQ_REG(DA9063_IRQ_GPI3,
DA9063_REG_EVENT_C_OFFSET, DA9063_M_GPI3),
REGMAP_IRQ_REG(DA9063_IRQ_GPI4,
DA9063_REG_EVENT_C_OFFSET, DA9063_M_GPI4),
REGMAP_IRQ_REG(DA9063_IRQ_GPI5,
DA9063_REG_EVENT_C_OFFSET, DA9063_M_GPI5),
REGMAP_IRQ_REG(DA9063_IRQ_GPI6,
DA9063_REG_EVENT_C_OFFSET, DA9063_M_GPI6),
REGMAP_IRQ_REG(DA9063_IRQ_GPI7,
DA9063_REG_EVENT_C_OFFSET, DA9063_M_GPI7),
/* DA9063 event D register */
[DA9063_IRQ_GPI8] = {
.reg_offset = DA9063_REG_EVENT_D_OFFSET,
.mask = DA9063_M_GPI8,
},
[DA9063_IRQ_GPI9] = {
.reg_offset = DA9063_REG_EVENT_D_OFFSET,
.mask = DA9063_M_GPI9,
},
[DA9063_IRQ_GPI10] = {
.reg_offset = DA9063_REG_EVENT_D_OFFSET,
.mask = DA9063_M_GPI10,
},
[DA9063_IRQ_GPI11] = {
.reg_offset = DA9063_REG_EVENT_D_OFFSET,
.mask = DA9063_M_GPI11,
},
[DA9063_IRQ_GPI12] = {
.reg_offset = DA9063_REG_EVENT_D_OFFSET,
.mask = DA9063_M_GPI12,
},
[DA9063_IRQ_GPI13] = {
.reg_offset = DA9063_REG_EVENT_D_OFFSET,
.mask = DA9063_M_GPI13,
},
[DA9063_IRQ_GPI14] = {
.reg_offset = DA9063_REG_EVENT_D_OFFSET,
.mask = DA9063_M_GPI14,
},
[DA9063_IRQ_GPI15] = {
.reg_offset = DA9063_REG_EVENT_D_OFFSET,
.mask = DA9063_M_GPI15,
},
REGMAP_IRQ_REG(DA9063_IRQ_GPI8,
DA9063_REG_EVENT_D_OFFSET, DA9063_M_GPI8),
REGMAP_IRQ_REG(DA9063_IRQ_GPI9,
DA9063_REG_EVENT_D_OFFSET, DA9063_M_GPI9),
REGMAP_IRQ_REG(DA9063_IRQ_GPI10,
DA9063_REG_EVENT_D_OFFSET, DA9063_M_GPI10),
REGMAP_IRQ_REG(DA9063_IRQ_GPI11,
DA9063_REG_EVENT_D_OFFSET, DA9063_M_GPI11),
REGMAP_IRQ_REG(DA9063_IRQ_GPI12,
DA9063_REG_EVENT_D_OFFSET, DA9063_M_GPI12),
REGMAP_IRQ_REG(DA9063_IRQ_GPI13,
DA9063_REG_EVENT_D_OFFSET, DA9063_M_GPI13),
REGMAP_IRQ_REG(DA9063_IRQ_GPI14,
DA9063_REG_EVENT_D_OFFSET, DA9063_M_GPI14),
REGMAP_IRQ_REG(DA9063_IRQ_GPI15,
DA9063_REG_EVENT_D_OFFSET, DA9063_M_GPI15),
};
static const struct regmap_irq_chip da9063_irq_chip = {
.name = "da9063-irq",
.irqs = da9063_irqs,
.num_irqs = DA9063_NUM_IRQ,
.num_irqs = ARRAY_SIZE(da9063_irqs),
.num_regs = 4,
.status_base = DA9063_REG_EVENT_A,
.mask_base = DA9063_REG_IRQ_MASK_A,
.ack_base = DA9063_REG_EVENT_A,
.init_ack_masked = true,
};
static const struct regmap_irq da9063l_irqs[] = {
/* DA9063 event A register */
REGMAP_IRQ_REG(DA9063_IRQ_ONKEY,
DA9063_REG_EVENT_A_OFFSET, DA9063_M_ONKEY),
REGMAP_IRQ_REG(DA9063_IRQ_ADC_RDY,
DA9063_REG_EVENT_A_OFFSET, DA9063_M_ADC_RDY),
REGMAP_IRQ_REG(DA9063_IRQ_SEQ_RDY,
DA9063_REG_EVENT_A_OFFSET, DA9063_M_SEQ_RDY),
/* DA9063 event B register */
REGMAP_IRQ_REG(DA9063_IRQ_WAKE,
DA9063_REG_EVENT_B_OFFSET, DA9063_M_WAKE),
REGMAP_IRQ_REG(DA9063_IRQ_TEMP,
DA9063_REG_EVENT_B_OFFSET, DA9063_M_TEMP),
REGMAP_IRQ_REG(DA9063_IRQ_COMP_1V2,
DA9063_REG_EVENT_B_OFFSET, DA9063_M_COMP_1V2),
REGMAP_IRQ_REG(DA9063_IRQ_LDO_LIM,
DA9063_REG_EVENT_B_OFFSET, DA9063_M_LDO_LIM),
REGMAP_IRQ_REG(DA9063_IRQ_REG_UVOV,
DA9063_REG_EVENT_B_OFFSET, DA9063_M_UVOV),
REGMAP_IRQ_REG(DA9063_IRQ_DVC_RDY,
DA9063_REG_EVENT_B_OFFSET, DA9063_M_DVC_RDY),
REGMAP_IRQ_REG(DA9063_IRQ_VDD_MON,
DA9063_REG_EVENT_B_OFFSET, DA9063_M_VDD_MON),
REGMAP_IRQ_REG(DA9063_IRQ_WARN,
DA9063_REG_EVENT_B_OFFSET, DA9063_M_VDD_WARN),
/* DA9063 event C register */
REGMAP_IRQ_REG(DA9063_IRQ_GPI0,
DA9063_REG_EVENT_C_OFFSET, DA9063_M_GPI0),
REGMAP_IRQ_REG(DA9063_IRQ_GPI1,
DA9063_REG_EVENT_C_OFFSET, DA9063_M_GPI1),
REGMAP_IRQ_REG(DA9063_IRQ_GPI2,
DA9063_REG_EVENT_C_OFFSET, DA9063_M_GPI2),
REGMAP_IRQ_REG(DA9063_IRQ_GPI3,
DA9063_REG_EVENT_C_OFFSET, DA9063_M_GPI3),
REGMAP_IRQ_REG(DA9063_IRQ_GPI4,
DA9063_REG_EVENT_C_OFFSET, DA9063_M_GPI4),
REGMAP_IRQ_REG(DA9063_IRQ_GPI5,
DA9063_REG_EVENT_C_OFFSET, DA9063_M_GPI5),
REGMAP_IRQ_REG(DA9063_IRQ_GPI6,
DA9063_REG_EVENT_C_OFFSET, DA9063_M_GPI6),
REGMAP_IRQ_REG(DA9063_IRQ_GPI7,
DA9063_REG_EVENT_C_OFFSET, DA9063_M_GPI7),
/* DA9063 event D register */
REGMAP_IRQ_REG(DA9063_IRQ_GPI8,
DA9063_REG_EVENT_D_OFFSET, DA9063_M_GPI8),
REGMAP_IRQ_REG(DA9063_IRQ_GPI9,
DA9063_REG_EVENT_D_OFFSET, DA9063_M_GPI9),
REGMAP_IRQ_REG(DA9063_IRQ_GPI10,
DA9063_REG_EVENT_D_OFFSET, DA9063_M_GPI10),
REGMAP_IRQ_REG(DA9063_IRQ_GPI11,
DA9063_REG_EVENT_D_OFFSET, DA9063_M_GPI11),
REGMAP_IRQ_REG(DA9063_IRQ_GPI12,
DA9063_REG_EVENT_D_OFFSET, DA9063_M_GPI12),
REGMAP_IRQ_REG(DA9063_IRQ_GPI13,
DA9063_REG_EVENT_D_OFFSET, DA9063_M_GPI13),
REGMAP_IRQ_REG(DA9063_IRQ_GPI14,
DA9063_REG_EVENT_D_OFFSET, DA9063_M_GPI14),
REGMAP_IRQ_REG(DA9063_IRQ_GPI15,
DA9063_REG_EVENT_D_OFFSET, DA9063_M_GPI15),
};
static const struct regmap_irq_chip da9063l_irq_chip = {
.name = "da9063l-irq",
.irqs = da9063l_irqs,
.num_irqs = ARRAY_SIZE(da9063l_irqs),
.num_regs = 4,
.status_base = DA9063_REG_EVENT_A,
.mask_base = DA9063_REG_IRQ_MASK_A,
......@@ -163,6 +176,7 @@ static const struct regmap_irq_chip da9063_irq_chip = {
int da9063_irq_init(struct da9063 *da9063)
{
const struct regmap_irq_chip *irq_chip;
int ret;
if (!da9063->chip_irq) {
......@@ -170,10 +184,15 @@ int da9063_irq_init(struct da9063 *da9063)
return -EINVAL;
}
ret = regmap_add_irq_chip(da9063->regmap, da9063->chip_irq,
if (da9063->type == PMIC_TYPE_DA9063)
irq_chip = &da9063_irq_chip;
else
irq_chip = &da9063l_irq_chip;
ret = devm_regmap_add_irq_chip(da9063->dev, da9063->regmap,
da9063->chip_irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED,
da9063->irq_base, &da9063_irq_chip,
&da9063->regmap_irq);
da9063->irq_base, irq_chip, &da9063->regmap_irq);
if (ret) {
dev_err(da9063->dev, "Failed to reguest IRQ %d: %d\n",
da9063->chip_irq, ret);
......@@ -182,8 +201,3 @@ int da9063_irq_init(struct da9063 *da9063)
return 0;
}
void da9063_irq_exit(struct da9063 *da9063)
{
regmap_del_irq_chip(da9063->chip_irq, da9063->regmap_irq);
}
......@@ -194,6 +194,7 @@ static bool dln2_transfer_complete(struct dln2_dev *dln2, struct urb *urb,
struct device *dev = &dln2->interface->dev;
struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[handle];
struct dln2_rx_context *rxc;
unsigned long flags;
bool valid_slot = false;
if (rx_slot >= DLN2_MAX_RX_SLOTS)
......@@ -201,18 +202,13 @@ static bool dln2_transfer_complete(struct dln2_dev *dln2, struct urb *urb,
rxc = &rxs->slots[rx_slot];
/*
* No need to disable interrupts as this lock is not taken in interrupt
* context elsewhere in this driver. This function (or its callers) are
* also not exported to other modules.
*/
spin_lock(&rxs->lock);
spin_lock_irqsave(&rxs->lock, flags);
if (rxc->in_use && !rxc->urb) {
rxc->urb = urb;
complete(&rxc->done);
valid_slot = true;
}
spin_unlock(&rxs->lock);
spin_unlock_irqrestore(&rxs->lock, flags);
out:
if (!valid_slot)
......
......@@ -49,7 +49,7 @@ static struct regmap_config hi655x_regmap_config = {
.reg_bits = 32,
.reg_stride = HI655X_STRIDE,
.val_bits = 8,
.max_register = HI655X_BUS_ADDR(0xFFF),
.max_register = HI655X_BUS_ADDR(0x400) - HI655X_STRIDE,
};
static struct resource pwrkey_resources[] = {
......
......@@ -178,6 +178,19 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x31c2), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x31c4), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x31c6), (kernel_ulong_t)&bxt_info },
/* ICL-LP */
{ PCI_VDEVICE(INTEL, 0x34a8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x34a9), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x34aa), (kernel_ulong_t)&spt_info },
{ PCI_VDEVICE(INTEL, 0x34ab), (kernel_ulong_t)&spt_info },
{ PCI_VDEVICE(INTEL, 0x34c5), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34c6), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34c7), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x34e8), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34e9), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34ea), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34eb), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34fb), (kernel_ulong_t)&spt_info },
/* APL */
{ PCI_VDEVICE(INTEL, 0x5aac), (kernel_ulong_t)&apl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x5aae), (kernel_ulong_t)&apl_i2c_info },
......
......@@ -143,7 +143,7 @@ static struct platform_device *kempld_pdev;
static int kempld_create_platform_device(const struct dmi_system_id *id)
{
struct kempld_platform_data *pdata = id->driver_data;
const struct kempld_platform_data *pdata = id->driver_data;
int ret;
kempld_pdev = platform_device_alloc("kempld", -1);
......@@ -259,7 +259,7 @@ EXPORT_SYMBOL_GPL(kempld_write32);
*/
void kempld_get_mutex(struct kempld_device_data *pld)
{
struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
mutex_lock(&pld->lock);
pdata->get_hardware_mutex(pld);
......@@ -272,7 +272,7 @@ EXPORT_SYMBOL_GPL(kempld_get_mutex);
*/
void kempld_release_mutex(struct kempld_device_data *pld)
{
struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
pdata->release_hardware_mutex(pld);
mutex_unlock(&pld->lock);
......@@ -290,7 +290,7 @@ EXPORT_SYMBOL_GPL(kempld_release_mutex);
static int kempld_get_info(struct kempld_device_data *pld)
{
int ret;
struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
char major, minor;
ret = pdata->get_info(pld);
......@@ -332,7 +332,7 @@ static int kempld_get_info(struct kempld_device_data *pld)
*/
static int kempld_register_cells(struct kempld_device_data *pld)
{
struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
return pdata->register_cells(pld);
}
......@@ -444,7 +444,8 @@ static int kempld_detect_device(struct kempld_device_data *pld)
static int kempld_probe(struct platform_device *pdev)
{
struct kempld_platform_data *pdata = dev_get_platdata(&pdev->dev);
const struct kempld_platform_data *pdata =
dev_get_platdata(&pdev->dev);
struct device *dev = &pdev->dev;
struct kempld_device_data *pld;
struct resource *ioport;
......@@ -476,7 +477,7 @@ static int kempld_probe(struct platform_device *pdev)
static int kempld_remove(struct platform_device *pdev)
{
struct kempld_device_data *pld = platform_get_drvdata(pdev);
struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
sysfs_remove_group(&pld->dev->kobj, &pld_attr_group);
......
// SPDX-License-Identifier: GPL-2.0
/*
* Core MFD support for Cirrus Logic Madera codecs
*
* Copyright (C) 2015-2018 Cirrus Logic
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation; version 2.
*/
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/mfd/madera/core.h>
#include <linux/mfd/madera/registers.h>
#include "madera.h"
#define CS47L35_SILICON_ID 0x6360
#define CS47L85_SILICON_ID 0x6338
#define CS47L90_SILICON_ID 0x6364
#define MADERA_32KZ_MCLK2 1
static const char * const madera_core_supplies[] = {
"AVDD",
"DBVDD1",
};
static const struct mfd_cell madera_ldo1_devs[] = {
{ .name = "madera-ldo1" },
};
static const char * const cs47l35_supplies[] = {
"MICVDD",
"DBVDD2",
"CPVDD1",
"CPVDD2",
"SPKVDD",
};
static const struct mfd_cell cs47l35_devs[] = {
{ .name = "madera-pinctrl", },
{ .name = "madera-irq", },
{ .name = "madera-micsupp", },
{ .name = "madera-gpio", },
{ .name = "madera-extcon", },
{
.name = "cs47l35-codec",
.parent_supplies = cs47l35_supplies,
.num_parent_supplies = ARRAY_SIZE(cs47l35_supplies),
},
};
static const char * const cs47l85_supplies[] = {
"MICVDD",
"DBVDD2",
"DBVDD3",
"DBVDD4",
"CPVDD1",
"CPVDD2",
"SPKVDDL",
"SPKVDDR",
};
static const struct mfd_cell cs47l85_devs[] = {
{ .name = "madera-pinctrl", },
{ .name = "madera-irq", },
{ .name = "madera-micsupp" },
{ .name = "madera-gpio", },
{ .name = "madera-extcon", },
{
.name = "cs47l85-codec",
.parent_supplies = cs47l85_supplies,
.num_parent_supplies = ARRAY_SIZE(cs47l85_supplies),
},
};
static const char * const cs47l90_supplies[] = {
"MICVDD",
"DBVDD2",
"DBVDD3",
"DBVDD4",
"CPVDD1",
"CPVDD2",
};
static const struct mfd_cell cs47l90_devs[] = {
{ .name = "madera-pinctrl", },
{ .name = "madera-irq", },
{ .name = "madera-micsupp", },
{ .name = "madera-gpio", },
{ .name = "madera-extcon", },
{
.name = "cs47l90-codec",
.parent_supplies = cs47l90_supplies,
.num_parent_supplies = ARRAY_SIZE(cs47l90_supplies),
},
};
/* Used by madera-i2c and madera-spi drivers */
const char *madera_name_from_type(enum madera_type type)
{
switch (type) {
case CS47L35:
return "CS47L35";
case CS47L85:
return "CS47L85";
case CS47L90:
return "CS47L90";
case CS47L91:
return "CS47L91";
case WM1840:
return "WM1840";
default:
return "Unknown";
}
}
EXPORT_SYMBOL_GPL(madera_name_from_type);
#define MADERA_BOOT_POLL_MAX_INTERVAL_US 5000
#define MADERA_BOOT_POLL_TIMEOUT_US 25000
static int madera_wait_for_boot(struct madera *madera)
{
unsigned int val;
int ret;
/*
* We can't use an interrupt as we need to runtime resume to do so,
* so we poll the status bit. This won't race with the interrupt
* handler because it will be blocked on runtime resume.
*/
ret = regmap_read_poll_timeout(madera->regmap,
MADERA_IRQ1_RAW_STATUS_1,
val,
(val & MADERA_BOOT_DONE_STS1),
MADERA_BOOT_POLL_MAX_INTERVAL_US,
MADERA_BOOT_POLL_TIMEOUT_US);
if (ret)
dev_err(madera->dev, "Polling BOOT_DONE_STS failed: %d\n", ret);
/*
* BOOT_DONE defaults to unmasked on boot so we must ack it.
* Do this unconditionally to avoid interrupt storms.
*/
regmap_write(madera->regmap, MADERA_IRQ1_STATUS_1,
MADERA_BOOT_DONE_EINT1);
pm_runtime_mark_last_busy(madera->dev);
return ret;
}
static int madera_soft_reset(struct madera *madera)
{
int ret;
ret = regmap_write(madera->regmap, MADERA_SOFTWARE_RESET, 0);
if (ret != 0) {
dev_err(madera->dev, "Failed to soft reset device: %d\n", ret);
return ret;
}
/* Allow time for internal clocks to startup after reset */
usleep_range(1000, 2000);
return 0;
}
static void madera_enable_hard_reset(struct madera *madera)
{
if (!madera->pdata.reset)
return;
/*
* There are many existing out-of-tree users of these codecs that we
* can't break so preserve the expected behaviour of setting the line
* low to assert reset.
*/
gpiod_set_raw_value_cansleep(madera->pdata.reset, 0);
}
static void madera_disable_hard_reset(struct madera *madera)
{
if (!madera->pdata.reset)
return;
gpiod_set_raw_value_cansleep(madera->pdata.reset, 1);
usleep_range(1000, 2000);
}
static int __maybe_unused madera_runtime_resume(struct device *dev)
{
struct madera *madera = dev_get_drvdata(dev);
int ret;
dev_dbg(dev, "Leaving sleep mode\n");
ret = regulator_enable(madera->dcvdd);
if (ret) {
dev_err(dev, "Failed to enable DCVDD: %d\n", ret);
return ret;
}
regcache_cache_only(madera->regmap, false);
regcache_cache_only(madera->regmap_32bit, false);
ret = madera_wait_for_boot(madera);
if (ret)
goto err;
ret = regcache_sync(madera->regmap);
if (ret) {
dev_err(dev, "Failed to restore 16-bit register cache\n");
goto err;
}
ret = regcache_sync(madera->regmap_32bit);
if (ret) {
dev_err(dev, "Failed to restore 32-bit register cache\n");
goto err;
}
return 0;
err:
regcache_cache_only(madera->regmap_32bit, true);
regcache_cache_only(madera->regmap, true);
regulator_disable(madera->dcvdd);
return ret;
}
static int __maybe_unused madera_runtime_suspend(struct device *dev)
{
struct madera *madera = dev_get_drvdata(dev);
dev_dbg(madera->dev, "Entering sleep mode\n");
regcache_cache_only(madera->regmap, true);
regcache_mark_dirty(madera->regmap);
regcache_cache_only(madera->regmap_32bit, true);
regcache_mark_dirty(madera->regmap_32bit);
regulator_disable(madera->dcvdd);
return 0;
}
const struct dev_pm_ops madera_pm_ops = {
SET_RUNTIME_PM_OPS(madera_runtime_suspend,
madera_runtime_resume,
NULL)
};
EXPORT_SYMBOL_GPL(madera_pm_ops);
const struct of_device_id madera_of_match[] = {
{ .compatible = "cirrus,cs47l35", .data = (void *)CS47L35 },
{ .compatible = "cirrus,cs47l85", .data = (void *)CS47L85 },
{ .compatible = "cirrus,cs47l90", .data = (void *)CS47L90 },
{ .compatible = "cirrus,cs47l91", .data = (void *)CS47L91 },
{ .compatible = "cirrus,wm1840", .data = (void *)WM1840 },
{}
};
EXPORT_SYMBOL_GPL(madera_of_match);
static int madera_get_reset_gpio(struct madera *madera)
{
struct gpio_desc *reset;
int ret;
if (madera->pdata.reset)
return 0;
reset = devm_gpiod_get_optional(madera->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(reset)) {
ret = PTR_ERR(reset);
if (ret != -EPROBE_DEFER)
dev_err(madera->dev, "Failed to request /RESET: %d\n",
ret);
return ret;
}
/*
* A hard reset is needed for full reset of the chip. We allow running
* without hard reset only because it can be useful for early
* prototyping and some debugging, but we need to warn it's not ideal.
*/
if (!reset)
dev_warn(madera->dev,
"Running without reset GPIO is not recommended\n");
madera->pdata.reset = reset;
return 0;
}
static void madera_set_micbias_info(struct madera *madera)
{
/*
* num_childbias is an array because future codecs can have different
* childbiases for each micbias. Unspecified values default to 0.
*/
switch (madera->type) {
case CS47L35:
madera->num_micbias = 2;
madera->num_childbias[0] = 2;
madera->num_childbias[1] = 2;
return;
case CS47L85:
case WM1840:
madera->num_micbias = 4;
/* no child biases */
return;
case CS47L90:
case CS47L91:
madera->num_micbias = 2;
madera->num_childbias[0] = 4;
madera->num_childbias[1] = 4;
return;
default:
return;
}
}
int madera_dev_init(struct madera *madera)
{
struct device *dev = madera->dev;
unsigned int hwid;
int (*patch_fn)(struct madera *) = NULL;
const struct mfd_cell *mfd_devs;
int n_devs = 0;
int i, ret;
dev_set_drvdata(madera->dev, madera);
BLOCKING_INIT_NOTIFIER_HEAD(&madera->notifier);
madera_set_micbias_info(madera);
/*
* We need writable hw config info that all children can share.
* Simplest to take one shared copy of pdata struct.
*/
if (dev_get_platdata(madera->dev)) {
memcpy(&madera->pdata, dev_get_platdata(madera->dev),
sizeof(madera->pdata));
}
ret = madera_get_reset_gpio(madera);
if (ret)
return ret;
regcache_cache_only(madera->regmap, true);
regcache_cache_only(madera->regmap_32bit, true);
for (i = 0; i < ARRAY_SIZE(madera_core_supplies); i++)
madera->core_supplies[i].supply = madera_core_supplies[i];
madera->num_core_supplies = ARRAY_SIZE(madera_core_supplies);
/*
* On some codecs DCVDD could be supplied by the internal LDO1.
* For those we must add the LDO1 driver before requesting DCVDD
* No devm_ because we need to control shutdown order of children.
*/
switch (madera->type) {
case CS47L35:
case CS47L90:
case CS47L91:
break;
case CS47L85:
case WM1840:
ret = mfd_add_devices(madera->dev, PLATFORM_DEVID_NONE,
madera_ldo1_devs,
ARRAY_SIZE(madera_ldo1_devs),
NULL, 0, NULL);
if (ret) {
dev_err(dev, "Failed to add LDO1 child: %d\n", ret);
return ret;
}
break;
default:
/* No point continuing if the type is unknown */
dev_err(madera->dev, "Unknown device type %d\n", madera->type);
return -ENODEV;
}
ret = devm_regulator_bulk_get(dev, madera->num_core_supplies,
madera->core_supplies);
if (ret) {
dev_err(dev, "Failed to request core supplies: %d\n", ret);
goto err_devs;
}
/*
* Don't use devres here. If the regulator is one of our children it
* will already have been removed before devres cleanup on this mfd
* driver tries to call put() on it. We need control of shutdown order.
*/
madera->dcvdd = regulator_get(madera->dev, "DCVDD");
if (IS_ERR(madera->dcvdd)) {
ret = PTR_ERR(madera->dcvdd);
dev_err(dev, "Failed to request DCVDD: %d\n", ret);
goto err_devs;
}
ret = regulator_bulk_enable(madera->num_core_supplies,
madera->core_supplies);
if (ret) {
dev_err(dev, "Failed to enable core supplies: %d\n", ret);
goto err_dcvdd;
}
ret = regulator_enable(madera->dcvdd);
if (ret) {
dev_err(dev, "Failed to enable DCVDD: %d\n", ret);
goto err_enable;
}
madera_disable_hard_reset(madera);
regcache_cache_only(madera->regmap, false);
regcache_cache_only(madera->regmap_32bit, false);
/*
* Now we can power up and verify that this is a chip we know about
* before we start doing any writes to its registers.
*/
ret = regmap_read(madera->regmap, MADERA_SOFTWARE_RESET, &hwid);
if (ret) {
dev_err(dev, "Failed to read ID register: %d\n", ret);
goto err_reset;
}
switch (hwid) {
case CS47L35_SILICON_ID:
if (IS_ENABLED(CONFIG_MFD_CS47L35)) {
switch (madera->type) {
case CS47L35:
patch_fn = cs47l35_patch;
mfd_devs = cs47l35_devs;
n_devs = ARRAY_SIZE(cs47l35_devs);
break;
default:
break;
}
}
break;
case CS47L85_SILICON_ID:
if (IS_ENABLED(CONFIG_MFD_CS47L85)) {
switch (madera->type) {
case CS47L85:
case WM1840:
patch_fn = cs47l85_patch;
mfd_devs = cs47l85_devs;
n_devs = ARRAY_SIZE(cs47l85_devs);
break;
default:
break;
}
}
break;
case CS47L90_SILICON_ID:
if (IS_ENABLED(CONFIG_MFD_CS47L90)) {
switch (madera->type) {
case CS47L90:
case CS47L91:
patch_fn = cs47l90_patch;
mfd_devs = cs47l90_devs;
n_devs = ARRAY_SIZE(cs47l90_devs);
break;
default:
break;
}
}
break;
default:
dev_err(madera->dev, "Unknown device ID: %x\n", hwid);
ret = -EINVAL;
goto err_reset;
}
if (!n_devs) {
dev_err(madera->dev, "Device ID 0x%x not a %s\n", hwid,
madera->type_name);
ret = -ENODEV;
goto err_reset;
}
/*
* It looks like a device we support. If we don't have a hard reset
* we can now attempt a soft reset.
*/
if (!madera->pdata.reset) {
ret = madera_soft_reset(madera);
if (ret)
goto err_reset;
}
ret = madera_wait_for_boot(madera);
if (ret) {
dev_err(madera->dev, "Device failed initial boot: %d\n", ret);
goto err_reset;
}
ret = regmap_read(madera->regmap, MADERA_HARDWARE_REVISION,
&madera->rev);
if (ret) {
dev_err(dev, "Failed to read revision register: %d\n", ret);
goto err_reset;
}
madera->rev &= MADERA_HW_REVISION_MASK;
dev_info(dev, "%s silicon revision %d\n", madera->type_name,
madera->rev);
/* Apply hardware patch */
if (patch_fn) {
ret = patch_fn(madera);
if (ret) {
dev_err(madera->dev, "Failed to apply patch %d\n", ret);
goto err_reset;
}
}
/* Init 32k clock sourced from MCLK2 */
ret = regmap_update_bits(madera->regmap,
MADERA_CLOCK_32K_1,
MADERA_CLK_32K_ENA_MASK | MADERA_CLK_32K_SRC_MASK,
MADERA_CLK_32K_ENA | MADERA_32KZ_MCLK2);
if (ret) {
dev_err(madera->dev, "Failed to init 32k clock: %d\n", ret);
goto err_reset;
}
pm_runtime_set_active(madera->dev);
pm_runtime_enable(madera->dev);
pm_runtime_set_autosuspend_delay(madera->dev, 100);
pm_runtime_use_autosuspend(madera->dev);
/* No devm_ because we need to control shutdown order of children */
ret = mfd_add_devices(madera->dev, PLATFORM_DEVID_NONE,
mfd_devs, n_devs,
NULL, 0, NULL);
if (ret) {
dev_err(madera->dev, "Failed to add subdevices: %d\n", ret);
goto err_pm_runtime;
}
return 0;
err_pm_runtime:
pm_runtime_disable(madera->dev);
err_reset:
madera_enable_hard_reset(madera);
regulator_disable(madera->dcvdd);
err_enable:
regulator_bulk_disable(madera->num_core_supplies,
madera->core_supplies);
err_dcvdd:
regulator_put(madera->dcvdd);
err_devs:
mfd_remove_devices(dev);
return ret;
}
EXPORT_SYMBOL_GPL(madera_dev_init);
int madera_dev_exit(struct madera *madera)
{
/* Prevent any IRQs being serviced while we clean up */
disable_irq(madera->irq);
/*
* DCVDD could be supplied by a child node, we must disable it before
* removing the children, and prevent PM runtime from turning it back on
*/
pm_runtime_disable(madera->dev);
regulator_disable(madera->dcvdd);
regulator_put(madera->dcvdd);
mfd_remove_devices(madera->dev);
madera_enable_hard_reset(madera);
regulator_bulk_disable(madera->num_core_supplies,
madera->core_supplies);
return 0;
}
EXPORT_SYMBOL_GPL(madera_dev_exit);
MODULE_DESCRIPTION("Madera core MFD driver");
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
MODULE_LICENSE("GPL v2");
// SPDX-License-Identifier: GPL-2.0
/*
* I2C bus interface to Cirrus Logic Madera codecs
*
* Copyright (C) 2015-2018 Cirrus Logic
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation; version 2.
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/mfd/madera/core.h>
#include "madera.h"
static int madera_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct madera *madera;
const struct regmap_config *regmap_16bit_config = NULL;
const struct regmap_config *regmap_32bit_config = NULL;
const void *of_data;
unsigned long type;
const char *name;
int ret;
of_data = of_device_get_match_data(&i2c->dev);
if (of_data)
type = (unsigned long)of_data;
else
type = id->driver_data;
switch (type) {
case CS47L35:
if (IS_ENABLED(CONFIG_MFD_CS47L35)) {
regmap_16bit_config = &cs47l35_16bit_i2c_regmap;
regmap_32bit_config = &cs47l35_32bit_i2c_regmap;
}
break;
case CS47L85:
case WM1840:
if (IS_ENABLED(CONFIG_MFD_CS47L85)) {
regmap_16bit_config = &cs47l85_16bit_i2c_regmap;
regmap_32bit_config = &cs47l85_32bit_i2c_regmap;
}
break;
case CS47L90:
case CS47L91:
if (IS_ENABLED(CONFIG_MFD_CS47L90)) {
regmap_16bit_config = &cs47l90_16bit_i2c_regmap;
regmap_32bit_config = &cs47l90_32bit_i2c_regmap;
}
break;
default:
dev_err(&i2c->dev,
"Unknown Madera I2C device type %ld\n", type);
return -EINVAL;
}
name = madera_name_from_type(type);
if (!regmap_16bit_config) {
/* it's polite to say which codec isn't built into the kernel */
dev_err(&i2c->dev,
"Kernel does not include support for %s\n", name);
return -EINVAL;
}
madera = devm_kzalloc(&i2c->dev, sizeof(*madera), GFP_KERNEL);
if (!madera)
return -ENOMEM;
madera->regmap = devm_regmap_init_i2c(i2c, regmap_16bit_config);
if (IS_ERR(madera->regmap)) {
ret = PTR_ERR(madera->regmap);
dev_err(&i2c->dev,
"Failed to allocate 16-bit register map: %d\n", ret);
return ret;
}
madera->regmap_32bit = devm_regmap_init_i2c(i2c, regmap_32bit_config);
if (IS_ERR(madera->regmap_32bit)) {
ret = PTR_ERR(madera->regmap_32bit);
dev_err(&i2c->dev,
"Failed to allocate 32-bit register map: %d\n", ret);
return ret;
}
madera->type = type;
madera->type_name = name;
madera->dev = &i2c->dev;
madera->irq = i2c->irq;
return madera_dev_init(madera);
}
static int madera_i2c_remove(struct i2c_client *i2c)
{
struct madera *madera = dev_get_drvdata(&i2c->dev);
madera_dev_exit(madera);
return 0;
}
static const struct i2c_device_id madera_i2c_id[] = {
{ "cs47l35", CS47L35 },
{ "cs47l85", CS47L85 },
{ "cs47l90", CS47L90 },
{ "cs47l91", CS47L91 },
{ "wm1840", WM1840 },
{ }
};
MODULE_DEVICE_TABLE(i2c, madera_i2c_id);
static struct i2c_driver madera_i2c_driver = {
.driver = {
.name = "madera",
.pm = &madera_pm_ops,
.of_match_table = of_match_ptr(madera_of_match),
},
.probe = madera_i2c_probe,
.remove = madera_i2c_remove,
.id_table = madera_i2c_id,
};
module_i2c_driver(madera_i2c_driver);
MODULE_DESCRIPTION("Madera I2C bus interface");
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
MODULE_LICENSE("GPL v2");
// SPDX-License-Identifier: GPL-2.0
/*
* SPI bus interface to Cirrus Logic Madera codecs
*
* Copyright (C) 2015-2018 Cirrus Logic
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation; version 2.
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/mfd/madera/core.h>
#include "madera.h"
static int madera_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct madera *madera;
const struct regmap_config *regmap_16bit_config = NULL;
const struct regmap_config *regmap_32bit_config = NULL;
const void *of_data;
unsigned long type;
const char *name;
int ret;
of_data = of_device_get_match_data(&spi->dev);
if (of_data)
type = (unsigned long)of_data;
else
type = id->driver_data;
switch (type) {
case CS47L35:
if (IS_ENABLED(CONFIG_MFD_CS47L35)) {
regmap_16bit_config = &cs47l35_16bit_spi_regmap;
regmap_32bit_config = &cs47l35_32bit_spi_regmap;
}
break;
case CS47L85:
case WM1840:
if (IS_ENABLED(CONFIG_MFD_CS47L85)) {
regmap_16bit_config = &cs47l85_16bit_spi_regmap;
regmap_32bit_config = &cs47l85_32bit_spi_regmap;
}
break;
case CS47L90:
case CS47L91:
if (IS_ENABLED(CONFIG_MFD_CS47L90)) {
regmap_16bit_config = &cs47l90_16bit_spi_regmap;
regmap_32bit_config = &cs47l90_32bit_spi_regmap;
}
break;
default:
dev_err(&spi->dev,
"Unknown Madera SPI device type %ld\n", type);
return -EINVAL;
}
name = madera_name_from_type(type);
if (!regmap_16bit_config) {
/* it's polite to say which codec isn't built into the kernel */
dev_err(&spi->dev,
"Kernel does not include support for %s\n", name);
return -EINVAL;
}
madera = devm_kzalloc(&spi->dev, sizeof(*madera), GFP_KERNEL);
if (!madera)
return -ENOMEM;
madera->regmap = devm_regmap_init_spi(spi, regmap_16bit_config);
if (IS_ERR(madera->regmap)) {
ret = PTR_ERR(madera->regmap);
dev_err(&spi->dev,
"Failed to allocate 16-bit register map: %d\n", ret);
return ret;
}
madera->regmap_32bit = devm_regmap_init_spi(spi, regmap_32bit_config);
if (IS_ERR(madera->regmap_32bit)) {
ret = PTR_ERR(madera->regmap_32bit);
dev_err(&spi->dev,
"Failed to allocate 32-bit register map: %d\n", ret);
return ret;
}
madera->type = type;
madera->type_name = name;
madera->dev = &spi->dev;
madera->irq = spi->irq;
return madera_dev_init(madera);
}
static int madera_spi_remove(struct spi_device *spi)
{
struct madera *madera = spi_get_drvdata(spi);
madera_dev_exit(madera);
return 0;
}
static const struct spi_device_id madera_spi_ids[] = {
{ "cs47l35", CS47L35 },
{ "cs47l85", CS47L85 },
{ "cs47l90", CS47L90 },
{ "cs47l91", CS47L91 },
{ "wm1840", WM1840 },
{ }
};
MODULE_DEVICE_TABLE(spi, madera_spi_ids);
static struct spi_driver madera_spi_driver = {
.driver = {
.name = "madera",
.pm = &madera_pm_ops,
.of_match_table = of_match_ptr(madera_of_match),
},
.probe = madera_spi_probe,
.remove = madera_spi_remove,
.id_table = madera_spi_ids,
};
module_spi_driver(madera_spi_driver);
MODULE_DESCRIPTION("Madera SPI bus interface");
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
MODULE_LICENSE("GPL v2");
/*
* MFD internals for Cirrus Logic Madera codecs
*
* Copyright 2015-2018 Cirrus Logic
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef MADERA_MFD_H
#define MADERA_MFD_H
#include <linux/of.h>
#include <linux/pm.h>
struct madera;
extern const struct dev_pm_ops madera_pm_ops;
extern const struct of_device_id madera_of_match[];
int madera_dev_init(struct madera *madera);
int madera_dev_exit(struct madera *madera);
const char *madera_name_from_type(enum madera_type type);
extern const struct regmap_config cs47l35_16bit_spi_regmap;
extern const struct regmap_config cs47l35_32bit_spi_regmap;
extern const struct regmap_config cs47l35_16bit_i2c_regmap;
extern const struct regmap_config cs47l35_32bit_i2c_regmap;
int cs47l35_patch(struct madera *madera);
extern const struct regmap_config cs47l85_16bit_spi_regmap;
extern const struct regmap_config cs47l85_32bit_spi_regmap;
extern const struct regmap_config cs47l85_16bit_i2c_regmap;
extern const struct regmap_config cs47l85_32bit_i2c_regmap;
int cs47l85_patch(struct madera *madera);
extern const struct regmap_config cs47l90_16bit_spi_regmap;
extern const struct regmap_config cs47l90_32bit_spi_regmap;
extern const struct regmap_config cs47l90_16bit_i2c_regmap;
extern const struct regmap_config cs47l90_32bit_i2c_regmap;
int cs47l90_patch(struct madera *madera);
#endif
......@@ -63,16 +63,6 @@
#define RAVE_SP_TX_BUFFER_SIZE \
(RAVE_SP_STX_ETX_SIZE + 2 * RAVE_SP_RX_BUFFER_SIZE)
#define RAVE_SP_BOOT_SOURCE_GET 0
#define RAVE_SP_BOOT_SOURCE_SET 1
#define RAVE_SP_RDU2_BOARD_TYPE_RMB 0
#define RAVE_SP_RDU2_BOARD_TYPE_DEB 1
#define RAVE_SP_BOOT_SOURCE_SD 0
#define RAVE_SP_BOOT_SOURCE_EMMC 1
#define RAVE_SP_BOOT_SOURCE_NOR 2
/**
* enum rave_sp_deframer_state - Possible state for de-framer
*
......@@ -127,14 +117,44 @@ struct rave_sp_checksum {
void (*subroutine)(const u8 *, size_t, u8 *);
};
struct rave_sp_version {
u8 hardware;
__le16 major;
u8 minor;
u8 letter[2];
} __packed;
struct rave_sp_status {
struct rave_sp_version bootloader_version;
struct rave_sp_version firmware_version;
u16 rdu_eeprom_flag;
u16 dds_eeprom_flag;
u8 pic_flag;
u8 orientation;
u32 etc;
s16 temp[2];
u8 backlight_current[3];
u8 dip_switch;
u8 host_interrupt;
u16 voltage_28;
u8 i2c_device_status;
u8 power_status;
u8 general_status;
u8 deprecated1;
u8 power_led_status;
u8 deprecated2;
u8 periph_power_shutoff;
} __packed;
/**
* struct rave_sp_variant_cmds - Variant specific command routines
*
* @translate: Generic to variant specific command mapping routine
*
* @get_status: Variant specific implementation of CMD_GET_STATUS
*/
struct rave_sp_variant_cmds {
int (*translate)(enum rave_sp_command);
int (*get_status)(struct rave_sp *sp, struct rave_sp_status *);
};
/**
......@@ -180,35 +200,6 @@ struct rave_sp {
const char *part_number_bootloader;
};
struct rave_sp_version {
u8 hardware;
__le16 major;
u8 minor;
u8 letter[2];
} __packed;
struct rave_sp_status {
struct rave_sp_version bootloader_version;
struct rave_sp_version firmware_version;
u16 rdu_eeprom_flag;
u16 dds_eeprom_flag;
u8 pic_flag;
u8 orientation;
u32 etc;
s16 temp[2];
u8 backlight_current[3];
u8 dip_switch;
u8 host_interrupt;
u16 voltage_28;
u8 i2c_device_status;
u8 power_status;
u8 general_status;
u8 deprecated1;
u8 power_led_status;
u8 deprecated2;
u8 periph_power_shutoff;
} __packed;
static bool rave_sp_id_is_event(u8 code)
{
return (code & 0xF0) == RAVE_SP_EVNT_BASE;
......@@ -641,10 +632,14 @@ static int rave_sp_default_cmd_translate(enum rave_sp_command command)
return 0x14;
case RAVE_SP_CMD_SW_WDT:
return 0x1C;
case RAVE_SP_CMD_PET_WDT:
return 0x1D;
case RAVE_SP_CMD_RESET:
return 0x1E;
case RAVE_SP_CMD_RESET_REASON:
return 0x1F;
case RAVE_SP_CMD_RMB_EEPROM:
return 0x20;
default:
return -EINVAL;
}
......@@ -666,18 +661,44 @@ static const char *devm_rave_sp_version(struct device *dev,
version->letter[1]);
}
static int rave_sp_get_status(struct rave_sp *sp)
static int rave_sp_rdu1_get_status(struct rave_sp *sp,
struct rave_sp_status *status)
{
struct device *dev = &sp->serdev->dev;
u8 cmd[] = {
[0] = RAVE_SP_CMD_STATUS,
[1] = 0
};
return rave_sp_exec(sp, cmd, sizeof(cmd), status, sizeof(*status));
}
static int rave_sp_emulated_get_status(struct rave_sp *sp,
struct rave_sp_status *status)
{
u8 cmd[] = {
[0] = RAVE_SP_CMD_GET_FIRMWARE_VERSION,
[1] = 0,
};
int ret;
ret = rave_sp_exec(sp, cmd, sizeof(cmd), &status->firmware_version,
sizeof(status->firmware_version));
if (ret)
return ret;
cmd[0] = RAVE_SP_CMD_GET_BOOTLOADER_VERSION;
return rave_sp_exec(sp, cmd, sizeof(cmd), &status->bootloader_version,
sizeof(status->bootloader_version));
}
static int rave_sp_get_status(struct rave_sp *sp)
{
struct device *dev = &sp->serdev->dev;
struct rave_sp_status status;
const char *version;
int ret;
ret = rave_sp_exec(sp, cmd, sizeof(cmd), &status, sizeof(status));
ret = sp->variant->cmd.get_status(sp, &status);
if (ret)
return ret;
......@@ -707,9 +728,10 @@ static const struct rave_sp_checksum rave_sp_checksum_ccitt = {
};
static const struct rave_sp_variant rave_sp_legacy = {
.checksum = &rave_sp_checksum_8b2c,
.checksum = &rave_sp_checksum_ccitt,
.cmd = {
.translate = rave_sp_default_cmd_translate,
.get_status = rave_sp_emulated_get_status,
},
};
......@@ -717,6 +739,7 @@ static const struct rave_sp_variant rave_sp_rdu1 = {
.checksum = &rave_sp_checksum_8b2c,
.cmd = {
.translate = rave_sp_rdu1_cmd_translate,
.get_status = rave_sp_rdu1_get_status,
},
};
......@@ -724,6 +747,7 @@ static const struct rave_sp_variant rave_sp_rdu2 = {
.checksum = &rave_sp_checksum_ccitt,
.cmd = {
.translate = rave_sp_rdu2_cmd_translate,
.get_status = rave_sp_emulated_get_status,
},
};
......@@ -776,6 +800,13 @@ static int rave_sp_probe(struct serdev_device *serdev)
return ret;
serdev_device_set_baudrate(serdev, baud);
serdev_device_set_flow_control(serdev, false);
ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
if (ret) {
dev_err(dev, "Failed to set parity\n");
return ret;
}
ret = rave_sp_get_status(sp);
if (ret) {
......
此差异已折叠。
......@@ -146,6 +146,7 @@ static const struct of_device_id sec_dt_match[] = {
/* Sentinel */
},
};
MODULE_DEVICE_TABLE(of, sec_dt_match);
#endif
static bool s2mpa01_volatile(struct device *dev, unsigned int reg)
......
......@@ -715,6 +715,7 @@ sm501_create_subdev(struct sm501_devdata *sm, char *name,
smdev->pdev.name = name;
smdev->pdev.id = sm->pdev_id;
smdev->pdev.dev.parent = sm->dev;
smdev->pdev.dev.coherent_dma_mask = 0xffffffff;
if (res_count) {
smdev->pdev.resource = (struct resource *)(smdev+1);
......
......@@ -209,14 +209,13 @@ static int ti_tscadc_probe(struct platform_device *pdev)
* The TSC_ADC_SS controller design assumes the OCP clock is
* at least 6x faster than the ADC clock.
*/
clk = clk_get(&pdev->dev, "adc_tsc_fck");
clk = devm_clk_get(&pdev->dev, "adc_tsc_fck");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "failed to get TSC fck\n");
err = PTR_ERR(clk);
goto err_disable_clk;
}
clock_rate = clk_get_rate(clk);
clk_put(clk);
tscadc->clk_div = clock_rate / ADC_CLK;
/* TSCADC_CLKDIV needs to be configured to the value minus 1 */
......
此差异已折叠。
......@@ -361,6 +361,7 @@ source "drivers/pinctrl/vt8500/Kconfig"
source "drivers/pinctrl/mediatek/Kconfig"
source "drivers/pinctrl/zte/Kconfig"
source "drivers/pinctrl/meson/Kconfig"
source "drivers/pinctrl/cirrus/Kconfig"
config PINCTRL_XWAY
bool
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册