mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 03:27:44 -04:00
- 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-ups
- 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
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAlt6fm8ACgkQUa+KL4f8
d2H8qA/8CLIKKEyc3CiQIF4LqmOPn+P20gSlnNdDFGHV+ZpVryL6qKItDy76nbQS
+GrPCmhADDn7N224ENlv4TYsB1txr7iZvyNr6W7F0fbdTLq5Fl2+6WIB3NnopPPK
MGhzR2jikfuYGwEG+BG3efvTnrjoi+NPbNPHDdoegOiy/Q1R8PhjMKh10xWo3pgG
0TeZVg61ztfewAiebieMixNz+ehzFKK959Lsa7neV2ZsstBbM8B44UpXmLF5JLYe
SR1ai5etSPBLxEeTXxlE+oG+fnDWk9Eyile7z1Nwg4et9OiXdyqAXBoA+BNDj2N2
N8vHuU79pSra+XGbka/7Kyz8sXjb4u92mJlc1wDcsCF+hX+wGEmwkEUb0S4YEtOH
1POi4YuIq0WF1oLSgCla/Pui0OQPIZaT31XhInYLV7rAX+MPgH0cUuLDsmzcg4ut
C6RvoSzWiGA2JVqlALExSDtAkWUoZLjYkqnPYmZwGs25xsGUqJmiw1E73HI0OWr/
EJuKQbeq/bOmRPqbbwO7z7peZJHOt6j344VCXBdP/sB7d/lR1sEGpxC03MIogHrA
BuiPyfDXTF1NB/+ZPBFbSG2Xhcg51yL4ddRa1sMntP6J4Ht1dW7TOpLI21Yu3eRp
jzKnA9DRa/LPUvoR1y+C2LxDeBcT5qFMg4mZedOgio/C0vCXvus=
=4Yj4
-----END PGP SIGNATURE-----
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
...
This commit is contained in:
commit
532c2b926d
77 changed files with 16124 additions and 460 deletions
|
|
@ -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 @@ error:
|
|||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,386 +0,0 @@
|
|||
/*
|
||||
* ChromeOS EC multi-function device (I2C)
|
||||
*
|
||||
* Copyright (C) 2012 Google, Inc
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/cros_ec.h>
|
||||
#include <linux/mfd/cros_ec_commands.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/**
|
||||
* Request format for protocol v3
|
||||
* byte 0 0xda (EC_COMMAND_PROTOCOL_3)
|
||||
* byte 1-8 struct ec_host_request
|
||||
* byte 10- response data
|
||||
*/
|
||||
struct ec_host_request_i2c {
|
||||
/* Always 0xda to backward compatible with v2 struct */
|
||||
uint8_t command_protocol;
|
||||
struct ec_host_request ec_request;
|
||||
} __packed;
|
||||
|
||||
|
||||
/*
|
||||
* Response format for protocol v3
|
||||
* byte 0 result code
|
||||
* byte 1 packet_length
|
||||
* byte 2-9 struct ec_host_response
|
||||
* byte 10- response data
|
||||
*/
|
||||
struct ec_host_response_i2c {
|
||||
uint8_t result;
|
||||
uint8_t packet_length;
|
||||
struct ec_host_response ec_response;
|
||||
} __packed;
|
||||
|
||||
static inline struct cros_ec_device *to_ec_dev(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
return i2c_get_clientdata(client);
|
||||
}
|
||||
|
||||
static int cros_ec_pkt_xfer_i2c(struct cros_ec_device *ec_dev,
|
||||
struct cros_ec_command *msg)
|
||||
{
|
||||
struct i2c_client *client = ec_dev->priv;
|
||||
int ret = -ENOMEM;
|
||||
int i;
|
||||
int packet_len;
|
||||
u8 *out_buf = NULL;
|
||||
u8 *in_buf = NULL;
|
||||
u8 sum;
|
||||
struct i2c_msg i2c_msg[2];
|
||||
struct ec_host_response *ec_response;
|
||||
struct ec_host_request_i2c *ec_request_i2c;
|
||||
struct ec_host_response_i2c *ec_response_i2c;
|
||||
int request_header_size = sizeof(struct ec_host_request_i2c);
|
||||
int response_header_size = sizeof(struct ec_host_response_i2c);
|
||||
|
||||
i2c_msg[0].addr = client->addr;
|
||||
i2c_msg[0].flags = 0;
|
||||
i2c_msg[1].addr = client->addr;
|
||||
i2c_msg[1].flags = I2C_M_RD;
|
||||
|
||||
packet_len = msg->insize + response_header_size;
|
||||
BUG_ON(packet_len > ec_dev->din_size);
|
||||
in_buf = ec_dev->din;
|
||||
i2c_msg[1].len = packet_len;
|
||||
i2c_msg[1].buf = (char *) in_buf;
|
||||
|
||||
packet_len = msg->outsize + request_header_size;
|
||||
BUG_ON(packet_len > ec_dev->dout_size);
|
||||
out_buf = ec_dev->dout;
|
||||
i2c_msg[0].len = packet_len;
|
||||
i2c_msg[0].buf = (char *) out_buf;
|
||||
|
||||
/* create request data */
|
||||
ec_request_i2c = (struct ec_host_request_i2c *) out_buf;
|
||||
ec_request_i2c->command_protocol = EC_COMMAND_PROTOCOL_3;
|
||||
|
||||
ec_dev->dout++;
|
||||
ret = cros_ec_prepare_tx(ec_dev, msg);
|
||||
ec_dev->dout--;
|
||||
|
||||
/* send command to EC and read answer */
|
||||
ret = i2c_transfer(client->adapter, i2c_msg, 2);
|
||||
if (ret < 0) {
|
||||
dev_dbg(ec_dev->dev, "i2c transfer failed: %d\n", ret);
|
||||
goto done;
|
||||
} else if (ret != 2) {
|
||||
dev_err(ec_dev->dev, "failed to get response: %d\n", ret);
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ec_response_i2c = (struct ec_host_response_i2c *) in_buf;
|
||||
msg->result = ec_response_i2c->result;
|
||||
ec_response = &ec_response_i2c->ec_response;
|
||||
|
||||
switch (msg->result) {
|
||||
case EC_RES_SUCCESS:
|
||||
break;
|
||||
case EC_RES_IN_PROGRESS:
|
||||
ret = -EAGAIN;
|
||||
dev_dbg(ec_dev->dev, "command 0x%02x in progress\n",
|
||||
msg->command);
|
||||
goto done;
|
||||
|
||||
default:
|
||||
dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n",
|
||||
msg->command, msg->result);
|
||||
/*
|
||||
* When we send v3 request to v2 ec, ec won't recognize the
|
||||
* 0xda (EC_COMMAND_PROTOCOL_3) and will return with status
|
||||
* EC_RES_INVALID_COMMAND with zero data length.
|
||||
*
|
||||
* In case of invalid command for v3 protocol the data length
|
||||
* will be at least sizeof(struct ec_host_response)
|
||||
*/
|
||||
if (ec_response_i2c->result == EC_RES_INVALID_COMMAND &&
|
||||
ec_response_i2c->packet_length == 0) {
|
||||
ret = -EPROTONOSUPPORT;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (ec_response_i2c->packet_length < sizeof(struct ec_host_response)) {
|
||||
dev_err(ec_dev->dev,
|
||||
"response of %u bytes too short; not a full header\n",
|
||||
ec_response_i2c->packet_length);
|
||||
ret = -EBADMSG;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (msg->insize < ec_response->data_len) {
|
||||
dev_err(ec_dev->dev,
|
||||
"response data size is too large: expected %u, got %u\n",
|
||||
msg->insize,
|
||||
ec_response->data_len);
|
||||
ret = -EMSGSIZE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* copy response packet payload and compute checksum */
|
||||
sum = 0;
|
||||
for (i = 0; i < sizeof(struct ec_host_response); i++)
|
||||
sum += ((u8 *)ec_response)[i];
|
||||
|
||||
memcpy(msg->data,
|
||||
in_buf + response_header_size,
|
||||
ec_response->data_len);
|
||||
for (i = 0; i < ec_response->data_len; i++)
|
||||
sum += msg->data[i];
|
||||
|
||||
/* All bytes should sum to zero */
|
||||
if (sum) {
|
||||
dev_err(ec_dev->dev, "bad packet checksum\n");
|
||||
ret = -EBADMSG;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = ec_response->data_len;
|
||||
|
||||
done:
|
||||
if (msg->command == EC_CMD_REBOOT_EC)
|
||||
msleep(EC_REBOOT_DELAY_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev,
|
||||
struct cros_ec_command *msg)
|
||||
{
|
||||
struct i2c_client *client = ec_dev->priv;
|
||||
int ret = -ENOMEM;
|
||||
int i;
|
||||
int len;
|
||||
int packet_len;
|
||||
u8 *out_buf = NULL;
|
||||
u8 *in_buf = NULL;
|
||||
u8 sum;
|
||||
struct i2c_msg i2c_msg[2];
|
||||
|
||||
i2c_msg[0].addr = client->addr;
|
||||
i2c_msg[0].flags = 0;
|
||||
i2c_msg[1].addr = client->addr;
|
||||
i2c_msg[1].flags = I2C_M_RD;
|
||||
|
||||
/*
|
||||
* allocate larger packet (one byte for checksum, one byte for
|
||||
* length, and one for result code)
|
||||
*/
|
||||
packet_len = msg->insize + 3;
|
||||
in_buf = kzalloc(packet_len, GFP_KERNEL);
|
||||
if (!in_buf)
|
||||
goto done;
|
||||
i2c_msg[1].len = packet_len;
|
||||
i2c_msg[1].buf = (char *)in_buf;
|
||||
|
||||
/*
|
||||
* allocate larger packet (one byte for checksum, one for
|
||||
* command code, one for length, and one for command version)
|
||||
*/
|
||||
packet_len = msg->outsize + 4;
|
||||
out_buf = kzalloc(packet_len, GFP_KERNEL);
|
||||
if (!out_buf)
|
||||
goto done;
|
||||
i2c_msg[0].len = packet_len;
|
||||
i2c_msg[0].buf = (char *)out_buf;
|
||||
|
||||
out_buf[0] = EC_CMD_VERSION0 + msg->version;
|
||||
out_buf[1] = msg->command;
|
||||
out_buf[2] = msg->outsize;
|
||||
|
||||
/* copy message payload and compute checksum */
|
||||
sum = out_buf[0] + out_buf[1] + out_buf[2];
|
||||
for (i = 0; i < msg->outsize; i++) {
|
||||
out_buf[3 + i] = msg->data[i];
|
||||
sum += out_buf[3 + i];
|
||||
}
|
||||
out_buf[3 + msg->outsize] = sum;
|
||||
|
||||
/* send command to EC and read answer */
|
||||
ret = i2c_transfer(client->adapter, i2c_msg, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(ec_dev->dev, "i2c transfer failed: %d\n", ret);
|
||||
goto done;
|
||||
} else if (ret != 2) {
|
||||
dev_err(ec_dev->dev, "failed to get response: %d\n", ret);
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* check response error code */
|
||||
msg->result = i2c_msg[1].buf[0];
|
||||
ret = cros_ec_check_result(ec_dev, msg);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
len = in_buf[1];
|
||||
if (len > msg->insize) {
|
||||
dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
|
||||
len, msg->insize);
|
||||
ret = -ENOSPC;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* copy response packet payload and compute checksum */
|
||||
sum = in_buf[0] + in_buf[1];
|
||||
for (i = 0; i < len; i++) {
|
||||
msg->data[i] = in_buf[2 + i];
|
||||
sum += in_buf[2 + i];
|
||||
}
|
||||
dev_dbg(ec_dev->dev, "packet: %*ph, sum = %02x\n",
|
||||
i2c_msg[1].len, in_buf, sum);
|
||||
if (sum != in_buf[2 + len]) {
|
||||
dev_err(ec_dev->dev, "bad packet checksum\n");
|
||||
ret = -EBADMSG;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = len;
|
||||
done:
|
||||
kfree(in_buf);
|
||||
kfree(out_buf);
|
||||
if (msg->command == EC_CMD_REBOOT_EC)
|
||||
msleep(EC_REBOOT_DELAY_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_ec_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *dev_id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct cros_ec_device *ec_dev = NULL;
|
||||
int err;
|
||||
|
||||
ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
|
||||
if (!ec_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, ec_dev);
|
||||
ec_dev->dev = dev;
|
||||
ec_dev->priv = client;
|
||||
ec_dev->irq = client->irq;
|
||||
ec_dev->cmd_xfer = cros_ec_cmd_xfer_i2c;
|
||||
ec_dev->pkt_xfer = cros_ec_pkt_xfer_i2c;
|
||||
ec_dev->phys_name = client->adapter->name;
|
||||
ec_dev->din_size = sizeof(struct ec_host_response_i2c) +
|
||||
sizeof(struct ec_response_get_protocol_info);
|
||||
ec_dev->dout_size = sizeof(struct ec_host_request_i2c);
|
||||
|
||||
err = cros_ec_register(ec_dev);
|
||||
if (err) {
|
||||
dev_err(dev, "cannot register EC\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_ec_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct cros_ec_device *ec_dev = i2c_get_clientdata(client);
|
||||
|
||||
cros_ec_remove(ec_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int cros_ec_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct cros_ec_device *ec_dev = to_ec_dev(dev);
|
||||
|
||||
return cros_ec_suspend(ec_dev);
|
||||
}
|
||||
|
||||
static int cros_ec_i2c_resume(struct device *dev)
|
||||
{
|
||||
struct cros_ec_device *ec_dev = to_ec_dev(dev);
|
||||
|
||||
return cros_ec_resume(ec_dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops cros_ec_i2c_pm_ops = {
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(cros_ec_i2c_suspend, cros_ec_i2c_resume)
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id cros_ec_i2c_of_match[] = {
|
||||
{ .compatible = "google,cros-ec-i2c", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cros_ec_i2c_of_match);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id cros_ec_i2c_id[] = {
|
||||
{ "cros-ec-i2c", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cros_ec_i2c_id);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id cros_ec_i2c_acpi_id[] = {
|
||||
{ "GOOG0008", 0 },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, cros_ec_i2c_acpi_id);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver cros_ec_driver = {
|
||||
.driver = {
|
||||
.name = "cros-ec-i2c",
|
||||
.acpi_match_table = ACPI_PTR(cros_ec_i2c_acpi_id),
|
||||
.of_match_table = of_match_ptr(cros_ec_i2c_of_match),
|
||||
.pm = &cros_ec_i2c_pm_ops,
|
||||
},
|
||||
.probe = cros_ec_i2c_probe,
|
||||
.remove = cros_ec_i2c_remove,
|
||||
.id_table = cros_ec_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(cros_ec_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("ChromeOS EC multi function device");
|
||||
|
|
@ -1,743 +0,0 @@
|
|||
/*
|
||||
* ChromeOS EC multi-function device (SPI)
|
||||
*
|
||||
* Copyright (C) 2012 Google, Inc
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/cros_ec.h>
|
||||
#include <linux/mfd/cros_ec_commands.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
|
||||
/* The header byte, which follows the preamble */
|
||||
#define EC_MSG_HEADER 0xec
|
||||
|
||||
/*
|
||||
* Number of EC preamble bytes we read at a time. Since it takes
|
||||
* about 400-500us for the EC to respond there is not a lot of
|
||||
* point in tuning this. If the EC could respond faster then
|
||||
* we could increase this so that might expect the preamble and
|
||||
* message to occur in a single transaction. However, the maximum
|
||||
* SPI transfer size is 256 bytes, so at 5MHz we need a response
|
||||
* time of perhaps <320us (200 bytes / 1600 bits).
|
||||
*/
|
||||
#define EC_MSG_PREAMBLE_COUNT 32
|
||||
|
||||
/*
|
||||
* Allow for a long time for the EC to respond. We support i2c
|
||||
* tunneling and support fairly long messages for the tunnel (249
|
||||
* bytes long at the moment). If we're talking to a 100 kHz device
|
||||
* on the other end and need to transfer ~256 bytes, then we need:
|
||||
* 10 us/bit * ~10 bits/byte * ~256 bytes = ~25ms
|
||||
*
|
||||
* We'll wait 8 times that to handle clock stretching and other
|
||||
* paranoia. Note that some battery gas gauge ICs claim to have a
|
||||
* clock stretch of 144ms in rare situations. That's incentive for
|
||||
* not directly passing i2c through, but it's too late for that for
|
||||
* existing hardware.
|
||||
*
|
||||
* It's pretty unlikely that we'll really see a 249 byte tunnel in
|
||||
* anything other than testing. If this was more common we might
|
||||
* consider having slow commands like this require a GET_STATUS
|
||||
* wait loop. The 'flash write' command would be another candidate
|
||||
* for this, clocking in at 2-3ms.
|
||||
*/
|
||||
#define EC_MSG_DEADLINE_MS 200
|
||||
|
||||
/*
|
||||
* Time between raising the SPI chip select (for the end of a
|
||||
* transaction) and dropping it again (for the next transaction).
|
||||
* If we go too fast, the EC will miss the transaction. We know that we
|
||||
* need at least 70 us with the 16 MHz STM32 EC, so go with 200 us to be
|
||||
* safe.
|
||||
*/
|
||||
#define EC_SPI_RECOVERY_TIME_NS (200 * 1000)
|
||||
|
||||
/**
|
||||
* struct cros_ec_spi - information about a SPI-connected EC
|
||||
*
|
||||
* @spi: SPI device we are connected to
|
||||
* @last_transfer_ns: time that we last finished a transfer.
|
||||
* @start_of_msg_delay: used to set the delay_usecs on the spi_transfer that
|
||||
* is sent when we want to turn on CS at the start of a transaction.
|
||||
* @end_of_msg_delay: used to set the delay_usecs on the spi_transfer that
|
||||
* is sent when we want to turn off CS at the end of a transaction.
|
||||
*/
|
||||
struct cros_ec_spi {
|
||||
struct spi_device *spi;
|
||||
s64 last_transfer_ns;
|
||||
unsigned int start_of_msg_delay;
|
||||
unsigned int end_of_msg_delay;
|
||||
};
|
||||
|
||||
static void debug_packet(struct device *dev, const char *name, u8 *ptr,
|
||||
int len)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
int i;
|
||||
|
||||
dev_dbg(dev, "%s: ", name);
|
||||
for (i = 0; i < len; i++)
|
||||
pr_cont(" %02x", ptr[i]);
|
||||
|
||||
pr_cont("\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static int terminate_request(struct cros_ec_device *ec_dev)
|
||||
{
|
||||
struct cros_ec_spi *ec_spi = ec_dev->priv;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer trans;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Turn off CS, possibly adding a delay to ensure the rising edge
|
||||
* doesn't come too soon after the end of the data.
|
||||
*/
|
||||
spi_message_init(&msg);
|
||||
memset(&trans, 0, sizeof(trans));
|
||||
trans.delay_usecs = ec_spi->end_of_msg_delay;
|
||||
spi_message_add_tail(&trans, &msg);
|
||||
|
||||
ret = spi_sync_locked(ec_spi->spi, &msg);
|
||||
|
||||
/* Reset end-of-response timer */
|
||||
ec_spi->last_transfer_ns = ktime_get_ns();
|
||||
if (ret < 0) {
|
||||
dev_err(ec_dev->dev,
|
||||
"cs-deassert spi transfer failed: %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* receive_n_bytes - receive n bytes from the EC.
|
||||
*
|
||||
* Assumes buf is a pointer into the ec_dev->din buffer
|
||||
*/
|
||||
static int receive_n_bytes(struct cros_ec_device *ec_dev, u8 *buf, int n)
|
||||
{
|
||||
struct cros_ec_spi *ec_spi = ec_dev->priv;
|
||||
struct spi_transfer trans;
|
||||
struct spi_message msg;
|
||||
int ret;
|
||||
|
||||
BUG_ON(buf - ec_dev->din + n > ec_dev->din_size);
|
||||
|
||||
memset(&trans, 0, sizeof(trans));
|
||||
trans.cs_change = 1;
|
||||
trans.rx_buf = buf;
|
||||
trans.len = n;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&trans, &msg);
|
||||
ret = spi_sync_locked(ec_spi->spi, &msg);
|
||||
if (ret < 0)
|
||||
dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cros_ec_spi_receive_packet - Receive a packet from the EC.
|
||||
*
|
||||
* This function has two phases: reading the preamble bytes (since if we read
|
||||
* data from the EC before it is ready to send, we just get preamble) and
|
||||
* reading the actual message.
|
||||
*
|
||||
* The received data is placed into ec_dev->din.
|
||||
*
|
||||
* @ec_dev: ChromeOS EC device
|
||||
* @need_len: Number of message bytes we need to read
|
||||
*/
|
||||
static int cros_ec_spi_receive_packet(struct cros_ec_device *ec_dev,
|
||||
int need_len)
|
||||
{
|
||||
struct ec_host_response *response;
|
||||
u8 *ptr, *end;
|
||||
int ret;
|
||||
unsigned long deadline;
|
||||
int todo;
|
||||
|
||||
BUG_ON(ec_dev->din_size < EC_MSG_PREAMBLE_COUNT);
|
||||
|
||||
/* Receive data until we see the header byte */
|
||||
deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS);
|
||||
while (true) {
|
||||
unsigned long start_jiffies = jiffies;
|
||||
|
||||
ret = receive_n_bytes(ec_dev,
|
||||
ec_dev->din,
|
||||
EC_MSG_PREAMBLE_COUNT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ptr = ec_dev->din;
|
||||
for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) {
|
||||
if (*ptr == EC_SPI_FRAME_START) {
|
||||
dev_dbg(ec_dev->dev, "msg found at %zd\n",
|
||||
ptr - ec_dev->din);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ptr != end)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Use the time at the start of the loop as a timeout. This
|
||||
* gives us one last shot at getting the transfer and is useful
|
||||
* in case we got context switched out for a while.
|
||||
*/
|
||||
if (time_after(start_jiffies, deadline)) {
|
||||
dev_warn(ec_dev->dev, "EC failed to respond in time\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ptr now points to the header byte. Copy any valid data to the
|
||||
* start of our buffer
|
||||
*/
|
||||
todo = end - ++ptr;
|
||||
BUG_ON(todo < 0 || todo > ec_dev->din_size);
|
||||
todo = min(todo, need_len);
|
||||
memmove(ec_dev->din, ptr, todo);
|
||||
ptr = ec_dev->din + todo;
|
||||
dev_dbg(ec_dev->dev, "need %d, got %d bytes from preamble\n",
|
||||
need_len, todo);
|
||||
need_len -= todo;
|
||||
|
||||
/* If the entire response struct wasn't read, get the rest of it. */
|
||||
if (todo < sizeof(*response)) {
|
||||
ret = receive_n_bytes(ec_dev, ptr, sizeof(*response) - todo);
|
||||
if (ret < 0)
|
||||
return -EBADMSG;
|
||||
ptr += (sizeof(*response) - todo);
|
||||
todo = sizeof(*response);
|
||||
}
|
||||
|
||||
response = (struct ec_host_response *)ec_dev->din;
|
||||
|
||||
/* Abort if data_len is too large. */
|
||||
if (response->data_len > ec_dev->din_size)
|
||||
return -EMSGSIZE;
|
||||
|
||||
/* Receive data until we have it all */
|
||||
while (need_len > 0) {
|
||||
/*
|
||||
* We can't support transfers larger than the SPI FIFO size
|
||||
* unless we have DMA. We don't have DMA on the ISP SPI ports
|
||||
* for Exynos. We need a way of asking SPI driver for
|
||||
* maximum-supported transfer size.
|
||||
*/
|
||||
todo = min(need_len, 256);
|
||||
dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%zd\n",
|
||||
todo, need_len, ptr - ec_dev->din);
|
||||
|
||||
ret = receive_n_bytes(ec_dev, ptr, todo);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ptr += todo;
|
||||
need_len -= todo;
|
||||
}
|
||||
|
||||
dev_dbg(ec_dev->dev, "loop done, ptr=%zd\n", ptr - ec_dev->din);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cros_ec_spi_receive_response - Receive a response from the EC.
|
||||
*
|
||||
* This function has two phases: reading the preamble bytes (since if we read
|
||||
* data from the EC before it is ready to send, we just get preamble) and
|
||||
* reading the actual message.
|
||||
*
|
||||
* The received data is placed into ec_dev->din.
|
||||
*
|
||||
* @ec_dev: ChromeOS EC device
|
||||
* @need_len: Number of message bytes we need to read
|
||||
*/
|
||||
static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
|
||||
int need_len)
|
||||
{
|
||||
u8 *ptr, *end;
|
||||
int ret;
|
||||
unsigned long deadline;
|
||||
int todo;
|
||||
|
||||
BUG_ON(ec_dev->din_size < EC_MSG_PREAMBLE_COUNT);
|
||||
|
||||
/* Receive data until we see the header byte */
|
||||
deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS);
|
||||
while (true) {
|
||||
unsigned long start_jiffies = jiffies;
|
||||
|
||||
ret = receive_n_bytes(ec_dev,
|
||||
ec_dev->din,
|
||||
EC_MSG_PREAMBLE_COUNT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ptr = ec_dev->din;
|
||||
for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) {
|
||||
if (*ptr == EC_SPI_FRAME_START) {
|
||||
dev_dbg(ec_dev->dev, "msg found at %zd\n",
|
||||
ptr - ec_dev->din);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ptr != end)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Use the time at the start of the loop as a timeout. This
|
||||
* gives us one last shot at getting the transfer and is useful
|
||||
* in case we got context switched out for a while.
|
||||
*/
|
||||
if (time_after(start_jiffies, deadline)) {
|
||||
dev_warn(ec_dev->dev, "EC failed to respond in time\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ptr now points to the header byte. Copy any valid data to the
|
||||
* start of our buffer
|
||||
*/
|
||||
todo = end - ++ptr;
|
||||
BUG_ON(todo < 0 || todo > ec_dev->din_size);
|
||||
todo = min(todo, need_len);
|
||||
memmove(ec_dev->din, ptr, todo);
|
||||
ptr = ec_dev->din + todo;
|
||||
dev_dbg(ec_dev->dev, "need %d, got %d bytes from preamble\n",
|
||||
need_len, todo);
|
||||
need_len -= todo;
|
||||
|
||||
/* Receive data until we have it all */
|
||||
while (need_len > 0) {
|
||||
/*
|
||||
* We can't support transfers larger than the SPI FIFO size
|
||||
* unless we have DMA. We don't have DMA on the ISP SPI ports
|
||||
* for Exynos. We need a way of asking SPI driver for
|
||||
* maximum-supported transfer size.
|
||||
*/
|
||||
todo = min(need_len, 256);
|
||||
dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%zd\n",
|
||||
todo, need_len, ptr - ec_dev->din);
|
||||
|
||||
ret = receive_n_bytes(ec_dev, ptr, todo);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
debug_packet(ec_dev->dev, "interim", ptr, todo);
|
||||
ptr += todo;
|
||||
need_len -= todo;
|
||||
}
|
||||
|
||||
dev_dbg(ec_dev->dev, "loop done, ptr=%zd\n", ptr - ec_dev->din);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cros_ec_pkt_xfer_spi - Transfer a packet over SPI and receive the reply
|
||||
*
|
||||
* @ec_dev: ChromeOS EC device
|
||||
* @ec_msg: Message to transfer
|
||||
*/
|
||||
static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
|
||||
struct cros_ec_command *ec_msg)
|
||||
{
|
||||
struct ec_host_response *response;
|
||||
struct cros_ec_spi *ec_spi = ec_dev->priv;
|
||||
struct spi_transfer trans, trans_delay;
|
||||
struct spi_message msg;
|
||||
int i, len;
|
||||
u8 *ptr;
|
||||
u8 *rx_buf;
|
||||
u8 sum;
|
||||
u8 rx_byte;
|
||||
int ret = 0, final_ret;
|
||||
unsigned long delay;
|
||||
|
||||
len = cros_ec_prepare_tx(ec_dev, ec_msg);
|
||||
dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);
|
||||
|
||||
/* If it's too soon to do another transaction, wait */
|
||||
delay = ktime_get_ns() - ec_spi->last_transfer_ns;
|
||||
if (delay < EC_SPI_RECOVERY_TIME_NS)
|
||||
ndelay(EC_SPI_RECOVERY_TIME_NS - delay);
|
||||
|
||||
rx_buf = kzalloc(len, GFP_KERNEL);
|
||||
if (!rx_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_bus_lock(ec_spi->spi->master);
|
||||
|
||||
/*
|
||||
* Leave a gap between CS assertion and clocking of data to allow the
|
||||
* EC time to wakeup.
|
||||
*/
|
||||
spi_message_init(&msg);
|
||||
if (ec_spi->start_of_msg_delay) {
|
||||
memset(&trans_delay, 0, sizeof(trans_delay));
|
||||
trans_delay.delay_usecs = ec_spi->start_of_msg_delay;
|
||||
spi_message_add_tail(&trans_delay, &msg);
|
||||
}
|
||||
|
||||
/* Transmit phase - send our message */
|
||||
memset(&trans, 0, sizeof(trans));
|
||||
trans.tx_buf = ec_dev->dout;
|
||||
trans.rx_buf = rx_buf;
|
||||
trans.len = len;
|
||||
trans.cs_change = 1;
|
||||
spi_message_add_tail(&trans, &msg);
|
||||
ret = spi_sync_locked(ec_spi->spi, &msg);
|
||||
|
||||
/* Get the response */
|
||||
if (!ret) {
|
||||
/* Verify that EC can process command */
|
||||
for (i = 0; i < len; i++) {
|
||||
rx_byte = rx_buf[i];
|
||||
/*
|
||||
* Seeing the PAST_END, RX_BAD_DATA, or NOT_READY
|
||||
* markers are all signs that the EC didn't fully
|
||||
* receive our command. e.g., if the EC is flashing
|
||||
* itself, it can't respond to any commands and instead
|
||||
* clocks out EC_SPI_PAST_END from its SPI hardware
|
||||
* buffer. Similar occurrences can happen if the AP is
|
||||
* too slow to clock out data after asserting CS -- the
|
||||
* EC will abort and fill its buffer with
|
||||
* EC_SPI_RX_BAD_DATA.
|
||||
*
|
||||
* In all cases, these errors should be safe to retry.
|
||||
* Report -EAGAIN and let the caller decide what to do
|
||||
* about that.
|
||||
*/
|
||||
if (rx_byte == EC_SPI_PAST_END ||
|
||||
rx_byte == EC_SPI_RX_BAD_DATA ||
|
||||
rx_byte == EC_SPI_NOT_READY) {
|
||||
ret = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
ret = cros_ec_spi_receive_packet(ec_dev,
|
||||
ec_msg->insize + sizeof(*response));
|
||||
else if (ret != -EAGAIN)
|
||||
dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
|
||||
|
||||
final_ret = terminate_request(ec_dev);
|
||||
|
||||
spi_bus_unlock(ec_spi->spi->master);
|
||||
|
||||
if (!ret)
|
||||
ret = final_ret;
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
ptr = ec_dev->din;
|
||||
|
||||
/* check response error code */
|
||||
response = (struct ec_host_response *)ptr;
|
||||
ec_msg->result = response->result;
|
||||
|
||||
ret = cros_ec_check_result(ec_dev, ec_msg);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
len = response->data_len;
|
||||
sum = 0;
|
||||
if (len > ec_msg->insize) {
|
||||
dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
|
||||
len, ec_msg->insize);
|
||||
ret = -EMSGSIZE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(*response); i++)
|
||||
sum += ptr[i];
|
||||
|
||||
/* copy response packet payload and compute checksum */
|
||||
memcpy(ec_msg->data, ptr + sizeof(*response), len);
|
||||
for (i = 0; i < len; i++)
|
||||
sum += ec_msg->data[i];
|
||||
|
||||
if (sum) {
|
||||
dev_err(ec_dev->dev,
|
||||
"bad packet checksum, calculated %x\n",
|
||||
sum);
|
||||
ret = -EBADMSG;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = len;
|
||||
exit:
|
||||
kfree(rx_buf);
|
||||
if (ec_msg->command == EC_CMD_REBOOT_EC)
|
||||
msleep(EC_REBOOT_DELAY_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cros_ec_cmd_xfer_spi - Transfer a message over SPI and receive the reply
|
||||
*
|
||||
* @ec_dev: ChromeOS EC device
|
||||
* @ec_msg: Message to transfer
|
||||
*/
|
||||
static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
|
||||
struct cros_ec_command *ec_msg)
|
||||
{
|
||||
struct cros_ec_spi *ec_spi = ec_dev->priv;
|
||||
struct spi_transfer trans;
|
||||
struct spi_message msg;
|
||||
int i, len;
|
||||
u8 *ptr;
|
||||
u8 *rx_buf;
|
||||
u8 rx_byte;
|
||||
int sum;
|
||||
int ret = 0, final_ret;
|
||||
unsigned long delay;
|
||||
|
||||
len = cros_ec_prepare_tx(ec_dev, ec_msg);
|
||||
dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);
|
||||
|
||||
/* If it's too soon to do another transaction, wait */
|
||||
delay = ktime_get_ns() - ec_spi->last_transfer_ns;
|
||||
if (delay < EC_SPI_RECOVERY_TIME_NS)
|
||||
ndelay(EC_SPI_RECOVERY_TIME_NS - delay);
|
||||
|
||||
rx_buf = kzalloc(len, GFP_KERNEL);
|
||||
if (!rx_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_bus_lock(ec_spi->spi->master);
|
||||
|
||||
/* Transmit phase - send our message */
|
||||
debug_packet(ec_dev->dev, "out", ec_dev->dout, len);
|
||||
memset(&trans, 0, sizeof(trans));
|
||||
trans.tx_buf = ec_dev->dout;
|
||||
trans.rx_buf = rx_buf;
|
||||
trans.len = len;
|
||||
trans.cs_change = 1;
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&trans, &msg);
|
||||
ret = spi_sync_locked(ec_spi->spi, &msg);
|
||||
|
||||
/* Get the response */
|
||||
if (!ret) {
|
||||
/* Verify that EC can process command */
|
||||
for (i = 0; i < len; i++) {
|
||||
rx_byte = rx_buf[i];
|
||||
/* See comments in cros_ec_pkt_xfer_spi() */
|
||||
if (rx_byte == EC_SPI_PAST_END ||
|
||||
rx_byte == EC_SPI_RX_BAD_DATA ||
|
||||
rx_byte == EC_SPI_NOT_READY) {
|
||||
ret = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
ret = cros_ec_spi_receive_response(ec_dev,
|
||||
ec_msg->insize + EC_MSG_TX_PROTO_BYTES);
|
||||
else if (ret != -EAGAIN)
|
||||
dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
|
||||
|
||||
final_ret = terminate_request(ec_dev);
|
||||
|
||||
spi_bus_unlock(ec_spi->spi->master);
|
||||
|
||||
if (!ret)
|
||||
ret = final_ret;
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
ptr = ec_dev->din;
|
||||
|
||||
/* check response error code */
|
||||
ec_msg->result = ptr[0];
|
||||
ret = cros_ec_check_result(ec_dev, ec_msg);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
len = ptr[1];
|
||||
sum = ptr[0] + ptr[1];
|
||||
if (len > ec_msg->insize) {
|
||||
dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
|
||||
len, ec_msg->insize);
|
||||
ret = -ENOSPC;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* copy response packet payload and compute checksum */
|
||||
for (i = 0; i < len; i++) {
|
||||
sum += ptr[i + 2];
|
||||
if (ec_msg->insize)
|
||||
ec_msg->data[i] = ptr[i + 2];
|
||||
}
|
||||
sum &= 0xff;
|
||||
|
||||
debug_packet(ec_dev->dev, "in", ptr, len + 3);
|
||||
|
||||
if (sum != ptr[len + 2]) {
|
||||
dev_err(ec_dev->dev,
|
||||
"bad packet checksum, expected %02x, got %02x\n",
|
||||
sum, ptr[len + 2]);
|
||||
ret = -EBADMSG;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = len;
|
||||
exit:
|
||||
kfree(rx_buf);
|
||||
if (ec_msg->command == EC_CMD_REBOOT_EC)
|
||||
msleep(EC_REBOOT_DELAY_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cros_ec_spi_dt_probe(struct cros_ec_spi *ec_spi, struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(np, "google,cros-ec-spi-pre-delay", &val);
|
||||
if (!ret)
|
||||
ec_spi->start_of_msg_delay = val;
|
||||
|
||||
ret = of_property_read_u32(np, "google,cros-ec-spi-msg-delay", &val);
|
||||
if (!ret)
|
||||
ec_spi->end_of_msg_delay = val;
|
||||
}
|
||||
|
||||
static int cros_ec_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct device *dev = &spi->dev;
|
||||
struct cros_ec_device *ec_dev;
|
||||
struct cros_ec_spi *ec_spi;
|
||||
int err;
|
||||
|
||||
spi->bits_per_word = 8;
|
||||
spi->mode = SPI_MODE_0;
|
||||
err = spi_setup(spi);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
ec_spi = devm_kzalloc(dev, sizeof(*ec_spi), GFP_KERNEL);
|
||||
if (ec_spi == NULL)
|
||||
return -ENOMEM;
|
||||
ec_spi->spi = spi;
|
||||
ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
|
||||
if (!ec_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Check for any DT properties */
|
||||
cros_ec_spi_dt_probe(ec_spi, dev);
|
||||
|
||||
spi_set_drvdata(spi, ec_dev);
|
||||
ec_dev->dev = dev;
|
||||
ec_dev->priv = ec_spi;
|
||||
ec_dev->irq = spi->irq;
|
||||
ec_dev->cmd_xfer = cros_ec_cmd_xfer_spi;
|
||||
ec_dev->pkt_xfer = cros_ec_pkt_xfer_spi;
|
||||
ec_dev->phys_name = dev_name(&ec_spi->spi->dev);
|
||||
ec_dev->din_size = EC_MSG_PREAMBLE_COUNT +
|
||||
sizeof(struct ec_host_response) +
|
||||
sizeof(struct ec_response_get_protocol_info);
|
||||
ec_dev->dout_size = sizeof(struct ec_host_request);
|
||||
|
||||
ec_spi->last_transfer_ns = ktime_get_ns();
|
||||
|
||||
err = cros_ec_register(ec_dev);
|
||||
if (err) {
|
||||
dev_err(dev, "cannot register EC\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
device_init_wakeup(&spi->dev, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_ec_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct cros_ec_device *ec_dev;
|
||||
|
||||
ec_dev = spi_get_drvdata(spi);
|
||||
cros_ec_remove(ec_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int cros_ec_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct cros_ec_device *ec_dev = dev_get_drvdata(dev);
|
||||
|
||||
return cros_ec_suspend(ec_dev);
|
||||
}
|
||||
|
||||
static int cros_ec_spi_resume(struct device *dev)
|
||||
{
|
||||
struct cros_ec_device *ec_dev = dev_get_drvdata(dev);
|
||||
|
||||
return cros_ec_resume(ec_dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(cros_ec_spi_pm_ops, cros_ec_spi_suspend,
|
||||
cros_ec_spi_resume);
|
||||
|
||||
static const struct of_device_id cros_ec_spi_of_match[] = {
|
||||
{ .compatible = "google,cros-ec-spi", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cros_ec_spi_of_match);
|
||||
|
||||
static const struct spi_device_id cros_ec_spi_id[] = {
|
||||
{ "cros-ec-spi", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, cros_ec_spi_id);
|
||||
|
||||
static struct spi_driver cros_ec_driver_spi = {
|
||||
.driver = {
|
||||
.name = "cros-ec-spi",
|
||||
.of_match_table = of_match_ptr(cros_ec_spi_of_match),
|
||||
.pm = &cros_ec_spi_pm_ops,
|
||||
},
|
||||
.probe = cros_ec_spi_probe,
|
||||
.remove = cros_ec_spi_remove,
|
||||
.id_table = cros_ec_spi_id,
|
||||
};
|
||||
|
||||
module_spi_driver(cros_ec_driver_spi);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("ChromeOS EC multi function device (SPI)");
|
||||
1609
drivers/mfd/cs47l35-tables.c
Normal file
1609
drivers/mfd/cs47l35-tables.c
Normal file
File diff suppressed because it is too large
Load diff
3009
drivers/mfd/cs47l85-tables.c
Normal file
3009
drivers/mfd/cs47l85-tables.c
Normal file
File diff suppressed because it is too large
Load diff
2674
drivers/mfd/cs47l90-tables.c
Normal file
2674
drivers/mfd/cs47l90-tables.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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,21 +229,28 @@ 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void da9063_device_exit(struct da9063 *da9063)
|
||||
{
|
||||
mfd_remove_devices(da9063->dev);
|
||||
da9063_irq_exit(da9063);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("PMIC driver for Dialog DA9063");
|
||||
MODULE_AUTHOR("Krystian Garbaciak");
|
||||
MODULE_AUTHOR("Michal Hajduk");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
609
drivers/mfd/madera-core.c
Normal file
609
drivers/mfd/madera-core.c
Normal file
|
|
@ -0,0 +1,609 @@
|
|||
// 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 <[email protected]>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
140
drivers/mfd/madera-i2c.c
Normal file
140
drivers/mfd/madera-i2c.c
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
// 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 <[email protected]>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
139
drivers/mfd/madera-spi.c
Normal file
139
drivers/mfd/madera-spi.c
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
// 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 <[email protected]>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
44
drivers/mfd/madera.h
Normal file
44
drivers/mfd/madera.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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) {
|
||||
|
|
|
|||
211
drivers/mfd/rohm-bd718x7.c
Normal file
211
drivers/mfd/rohm-bd718x7.c
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
//
|
||||
// Copyright (C) 2018 ROHM Semiconductors
|
||||
//
|
||||
// ROHM BD71837MWV PMIC driver
|
||||
//
|
||||
// Datasheet available from
|
||||
// https://www.rohm.com/datasheet/BD71837MWV/bd71837mwv-e
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/rohm-bd718x7.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/*
|
||||
* gpio_keys.h requires definiton of bool. It is brought in
|
||||
* by above includes. Keep this as last until gpio_keys.h gets fixed.
|
||||
*/
|
||||
#include <linux/gpio_keys.h>
|
||||
|
||||
static const u8 supported_revisions[] = { 0xA2 /* BD71837 */ };
|
||||
|
||||
static struct gpio_keys_button button = {
|
||||
.code = KEY_POWER,
|
||||
.gpio = -1,
|
||||
.type = EV_KEY,
|
||||
};
|
||||
|
||||
static struct gpio_keys_platform_data bd718xx_powerkey_data = {
|
||||
.buttons = &button,
|
||||
.nbuttons = 1,
|
||||
.name = "bd718xx-pwrkey",
|
||||
};
|
||||
|
||||
static struct mfd_cell bd71837_mfd_cells[] = {
|
||||
{
|
||||
.name = "gpio-keys",
|
||||
.platform_data = &bd718xx_powerkey_data,
|
||||
.pdata_size = sizeof(bd718xx_powerkey_data),
|
||||
},
|
||||
{ .name = "bd71837-clk", },
|
||||
{ .name = "bd71837-pmic", },
|
||||
};
|
||||
|
||||
static const struct regmap_irq bd71837_irqs[] = {
|
||||
REGMAP_IRQ_REG(BD71837_INT_SWRST, 0, BD71837_INT_SWRST_MASK),
|
||||
REGMAP_IRQ_REG(BD71837_INT_PWRBTN_S, 0, BD71837_INT_PWRBTN_S_MASK),
|
||||
REGMAP_IRQ_REG(BD71837_INT_PWRBTN_L, 0, BD71837_INT_PWRBTN_L_MASK),
|
||||
REGMAP_IRQ_REG(BD71837_INT_PWRBTN, 0, BD71837_INT_PWRBTN_MASK),
|
||||
REGMAP_IRQ_REG(BD71837_INT_WDOG, 0, BD71837_INT_WDOG_MASK),
|
||||
REGMAP_IRQ_REG(BD71837_INT_ON_REQ, 0, BD71837_INT_ON_REQ_MASK),
|
||||
REGMAP_IRQ_REG(BD71837_INT_STBY_REQ, 0, BD71837_INT_STBY_REQ_MASK),
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip bd71837_irq_chip = {
|
||||
.name = "bd71837-irq",
|
||||
.irqs = bd71837_irqs,
|
||||
.num_irqs = ARRAY_SIZE(bd71837_irqs),
|
||||
.num_regs = 1,
|
||||
.irq_reg_stride = 1,
|
||||
.status_base = BD71837_REG_IRQ,
|
||||
.mask_base = BD71837_REG_MIRQ,
|
||||
.ack_base = BD71837_REG_IRQ,
|
||||
.init_ack_masked = true,
|
||||
.mask_invert = false,
|
||||
};
|
||||
|
||||
static const struct regmap_range pmic_status_range = {
|
||||
.range_min = BD71837_REG_IRQ,
|
||||
.range_max = BD71837_REG_POW_STATE,
|
||||
};
|
||||
|
||||
static const struct regmap_access_table volatile_regs = {
|
||||
.yes_ranges = &pmic_status_range,
|
||||
.n_yes_ranges = 1,
|
||||
};
|
||||
|
||||
static const struct regmap_config bd71837_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.volatile_table = &volatile_regs,
|
||||
.max_register = BD71837_MAX_REGISTER - 1,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static int bd71837_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct bd71837 *bd71837;
|
||||
int ret, i;
|
||||
unsigned int val;
|
||||
|
||||
bd71837 = devm_kzalloc(&i2c->dev, sizeof(struct bd71837), GFP_KERNEL);
|
||||
|
||||
if (!bd71837)
|
||||
return -ENOMEM;
|
||||
|
||||
bd71837->chip_irq = i2c->irq;
|
||||
|
||||
if (!bd71837->chip_irq) {
|
||||
dev_err(&i2c->dev, "No IRQ configured\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bd71837->dev = &i2c->dev;
|
||||
dev_set_drvdata(&i2c->dev, bd71837);
|
||||
|
||||
bd71837->regmap = devm_regmap_init_i2c(i2c, &bd71837_regmap_config);
|
||||
if (IS_ERR(bd71837->regmap)) {
|
||||
dev_err(&i2c->dev, "regmap initialization failed\n");
|
||||
return PTR_ERR(bd71837->regmap);
|
||||
}
|
||||
|
||||
ret = regmap_read(bd71837->regmap, BD71837_REG_REV, &val);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "Read BD71837_REG_DEVICE failed\n");
|
||||
return ret;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(supported_revisions); i++)
|
||||
if (supported_revisions[i] == val)
|
||||
break;
|
||||
|
||||
if (i == ARRAY_SIZE(supported_revisions)) {
|
||||
dev_err(&i2c->dev, "Unsupported chip revision\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = devm_regmap_add_irq_chip(&i2c->dev, bd71837->regmap,
|
||||
bd71837->chip_irq, IRQF_ONESHOT, 0,
|
||||
&bd71837_irq_chip, &bd71837->irq_data);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "Failed to add irq_chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configure short press to 10 milliseconds */
|
||||
ret = regmap_update_bits(bd71837->regmap,
|
||||
BD71837_REG_PWRONCONFIG0,
|
||||
BD718XX_PWRBTN_PRESS_DURATION_MASK,
|
||||
BD718XX_PWRBTN_SHORT_PRESS_10MS);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev,
|
||||
"Failed to configure button short press timeout\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configure long press to 10 seconds */
|
||||
ret = regmap_update_bits(bd71837->regmap,
|
||||
BD71837_REG_PWRONCONFIG1,
|
||||
BD718XX_PWRBTN_PRESS_DURATION_MASK,
|
||||
BD718XX_PWRBTN_LONG_PRESS_10S);
|
||||
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev,
|
||||
"Failed to configure button long press timeout\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_irq_get_virq(bd71837->irq_data, BD71837_INT_PWRBTN_S);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "Failed to get the IRQ\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
button.irq = ret;
|
||||
|
||||
ret = devm_mfd_add_devices(bd71837->dev, PLATFORM_DEVID_AUTO,
|
||||
bd71837_mfd_cells,
|
||||
ARRAY_SIZE(bd71837_mfd_cells), NULL, 0,
|
||||
regmap_irq_get_domain(bd71837->irq_data));
|
||||
if (ret)
|
||||
dev_err(&i2c->dev, "Failed to create subdevices\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id bd71837_of_match[] = {
|
||||
{ .compatible = "rohm,bd71837", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bd71837_of_match);
|
||||
|
||||
static struct i2c_driver bd71837_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "rohm-bd718x7",
|
||||
.of_match_table = bd71837_of_match,
|
||||
},
|
||||
.probe = bd71837_i2c_probe,
|
||||
};
|
||||
|
||||
static int __init bd71837_i2c_init(void)
|
||||
{
|
||||
return i2c_add_driver(&bd71837_i2c_driver);
|
||||
}
|
||||
|
||||
/* Initialise early so consumer devices can complete system boot */
|
||||
subsys_initcall(bd71837_i2c_init);
|
||||
|
||||
static void __exit bd71837_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&bd71837_i2c_driver);
|
||||
}
|
||||
module_exit(bd71837_i2c_exit);
|
||||
|
||||
MODULE_AUTHOR("Matti Vaittinen <[email protected]>");
|
||||
MODULE_DESCRIPTION("ROHM BD71837 Power Management IC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -302,6 +302,10 @@ static int wm8994_set_pdata_from_of(struct wm8994 *wm8994)
|
|||
if (of_find_property(np, "wlf,ldoena-always-driven", NULL))
|
||||
pdata->lineout2fb = true;
|
||||
|
||||
pdata->spkmode_pu = of_property_read_bool(np, "wlf,spkmode-pu");
|
||||
|
||||
pdata->csnaddr_pd = of_property_read_bool(np, "wlf,csnaddr-pd");
|
||||
|
||||
pdata->ldo[0].enable = of_get_named_gpio(np, "wlf,ldo1ena", 0);
|
||||
if (pdata->ldo[0].enable < 0)
|
||||
pdata->ldo[0].enable = 0;
|
||||
|
|
@ -513,14 +517,15 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
|
|||
break;
|
||||
default:
|
||||
dev_err(wm8994->dev, "Unknown device type %d\n", wm8994->type);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto err_enable;
|
||||
}
|
||||
|
||||
ret = regmap_reinit_cache(wm8994->regmap, regmap_config);
|
||||
if (ret != 0) {
|
||||
dev_err(wm8994->dev, "Failed to reinit register cache: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
goto err_enable;
|
||||
}
|
||||
|
||||
/* Explicitly put the device into reset in case regulators
|
||||
|
|
@ -531,7 +536,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
|
|||
wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET));
|
||||
if (ret != 0) {
|
||||
dev_err(wm8994->dev, "Failed to reset device: %d\n", ret);
|
||||
return ret;
|
||||
goto err_enable;
|
||||
}
|
||||
|
||||
if (regmap_patch) {
|
||||
|
|
@ -540,7 +545,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
|
|||
if (ret != 0) {
|
||||
dev_err(wm8994->dev, "Failed to register patch: %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
goto err_enable;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -559,6 +564,8 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
|
|||
|
||||
if (pdata->spkmode_pu)
|
||||
pulls |= WM8994_SPKMODE_PU;
|
||||
if (pdata->csnaddr_pd)
|
||||
pulls |= WM8994_CSNADDR_PD;
|
||||
|
||||
/* Disable unneeded pulls */
|
||||
wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
|
||||
|
|
|
|||
Loading…
Reference in a new issue