Changes to existing drivers:

- Increase DT coverage - arizona, mc13xxx, stmpe-i2c, syscon, sun6i-prcm
  - Regmap use of and/or clean-up - tps65090, twl6040
  - Basic renaming - max14577
  - Use new cpufreq helpers -  db8500-prcmu
  - Increase regulator support - stmpe, arizona, wm5102
  - Reduce legacy GPIO overhead - stmpe
  - Provide necessary remove path - bcm590xx
  - Expand sysfs presence - kempld
  - Move driver specific code out to drivers - rtc-s5m, arizona
  - Clk handling - twl6040
  - Use managed (devm_*) resources - ipaq-micro
  - Clean-up/remove unused/duplicated code - tps65218, sec, pm8921, abx500-core
    		   		     	    db8500-prcmu, menelaus
  - Build/boot/sematic bug fixes - rtsx_usb, stmpe, bcm590xx, abx500, mc13xxx
                                   rdc321x-southbridge, mfd-core, sec, max14577
 				  syscon, cros_ec_spi
  - Constify stuff 		- sm501, tps65910, tps6507x, tps6586x, max77686,
    	    	  		  max8997, kempld, max77693, max8907, rtsx_usb
 				  db8500-prcmu, max8998, wm8400, sec, lp3943,
 				  max14577, as3711, omap-usb-host, ipaq-micro
 Support for new devices:
  - Add support for max77836 into max14577
  - Add support for tps658640 into tps6586x
  - Add support for cros-ec-i2c-tunnel into cros_ec
  - Add new driver for rtsx_usb_sdmmc and rtsx_usb_ms
  - Add new driver for axp20x
  - Add new driver for sun6i-prcm
  - Add new driver for ipaq-micro
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.14 (GNU/Linux)
 
 iQIcBAABAgAGBQJTkEAyAAoJEFGvii+H/HdhYdMP/j0MCmbORM9mr84Uuhi0PfBZ
 yE1zlhLQHtqQEcp4Ih6vWxIbgdhyHy3CqIiCKkhSTH0TWfreBX7DmlSc1QAKazpy
 sXeD/pB5TSIIfAHVh4NXF51WMzZ8OvQrmJQwdjUY1Sal2tlDCUdc84qtnn+3/J9N
 JIKpf4E1IeWojE8F3koKBcyE6ZzkAthIzWNDU9/y+sIZZEqPVRu9Y3mpdhPo6P3I
 9TboZ/s2cAwad56iArFMOCvtg1xpn0WyS0HgAxpSa9X5qLRjYPX5GnBBz8zKJYm3
 xHCiD6SgN29xX9W+MkcvtgEghhMfOkPgwF69u2/eagbtNEOm50cyLrvAe+SKjRvE
 pODs5yvHJap29cbVafHSdzV+zLZ51J/Oi/TFsG8/VBbd4DyW7oSM4juT20TFSiNt
 Edwzd4gicg/NxA7TupFCRQLgwAa3fnpPeCtsIims7LU7SclPuwNgS31isOGAkOvd
 mMaBa1clZb50Dy2iL8m1ugyqdOZXs9S24j3u+B0TTJyabMbNhDTGUwBG4/PrlJHq
 fIysx6CdNUGLikG2PybFvhCf3+FkEoPPtkloM1sblkhPHVunVlhXPRYAE90W2jUA
 dDQ2Aco/idWSGRZ5t7kA8OBN3+PwiIpxTt/D4eDmS7Qe/v3KXvhUYuKl2mD5+IfC
 StFiP5cd3zyJvWTeexuW
 =GYTU
 -----END PGP SIGNATURE-----

Merge tag 'mfd-for-linus-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd into next

Pull MFD updates from Lee Jones:
 "Changes to existing drivers:
   - increase DT coverage: arizona, mc13xxx, stmpe-i2c, syscon,
     sun6i-prcm
   - regmap use of and/or clean-up: tps65090, twl6040
   - basic renaming: max14577
   - use new cpufreq helpers: db8500-prcmu
   - increase regulator support: stmpe, arizona, wm5102
   - reduce legacy GPIO overhead: stmpe
   - provide necessary remove path: bcm590xx
   - expand sysfs presence: kempld
   - move driver specific code out to drivers: rtc-s5m, arizona
   - clk handling: twl6040
   - use managed (devm_*) resources: ipaq-micro
   - clean-up/remove unused/duplicated code: tps65218, sec, pm8921,
     abx500-core, db8500-prcmu, menelaus
   - build/boot/sematic bug fixes: rtsx_usb, stmpe, bcm590xx, abx500,
     mc13xxx, rdc321x-southbridge, mfd-core, sec, max14577, syscon,
     cros_ec_spi
   - constify stuff: sm501, tps65910, tps6507x, tps6586x, max77686,
     max8997, kempld, max77693, max8907, rtsx_usb, db8500-prcmu,
     max8998, wm8400, sec, lp3943, max14577, as3711, omap-usb-host,
     ipaq-micro

  Support for new devices:
   - add support for max77836 into max14577
   - add support for tps658640 into tps6586x
   - add support for cros-ec-i2c-tunnel into cros_ec
   - add new driver for rtsx_usb_sdmmc and rtsx_usb_ms
   - add new driver for axp20x
   - add new driver for sun6i-prcm
   - add new driver for ipaq-micro"

* tag 'mfd-for-linus-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (77 commits)
  mfd: wm5102: Correct default for LDO Control 2 register
  mfd: menelaus: Use module_i2c_driver
  mfd: tps65218: Terminate of match table
  mfd: db8500-prcmu: Remove check for CONFIG_DBX500_PRCMU_DEBUG
  mfd: ti-keystone-devctrl: Add bindings for device state control
  mfd: palmas: Format the header file
  mfd: abx500-core: Remove unused function abx500_dump_all_banks()
  mfd: arizona: Correct addresses of always-on trigger registers
  mfd: max14577: Cast to architecture agnostic data type
  i2c: ChromeOS EC tunnel driver
  mfd: cros_ec: Sync to the latest cros_ec_commands.h from EC sources
  mfd: cros_ec: spi: Increase cros_ec_spi deadline from 5ms to 100ms
  mfd: cros_ec: spi: Make the cros_ec_spi timeout more reliable
  mfd: cros_ec: spi: Add mutex to cros_ec_spi
  mfd: cros_ec: spi: Calculate delay between transfers correctly
  mfd: arizona: Correct error message for addition of main IRQ chip
  mfd: wm8997: Add registers for high power mode
  mfd: arizona: Add MICVDD to mapped regulators
  mfd: ipaq-micro: Make mfd_cell array const
  mfd: ipaq-micro: Use devm_ioremap_resource()
  ...
This commit is contained in:
Linus Torvalds 2014-06-06 12:08:39 -07:00
commit 1fe9eb1847
76 changed files with 6640 additions and 1672 deletions

View File

@ -0,0 +1,39 @@
I2C bus that tunnels through the ChromeOS EC (cros-ec)
======================================================
On some ChromeOS board designs we've got a connection to the EC (embedded
controller) but no direct connection to some devices on the other side of
the EC (like a battery and PMIC). To get access to those devices we need
to tunnel our i2c commands through the EC.
The node for this device should be under a cros-ec node like google,cros-ec-spi
or google,cros-ec-i2c.
Required properties:
- compatible: google,cros-ec-i2c-tunnel
- google,remote-bus: The EC bus we'd like to talk to.
Optional child nodes:
- One node per I2C device connected to the tunnelled I2C bus.
Example:
cros-ec@0 {
compatible = "google,cros-ec-spi";
...
i2c-tunnel {
compatible = "google,cros-ec-i2c-tunnel";
#address-cells = <1>;
#size-cells = <0>;
google,remote-bus = <0>;
battery: sbs-battery@b {
compatible = "sbs,sbs-battery";
reg = <0xb>;
sbs,poll-retry-count = <1>;
};
};
}

View File

@ -0,0 +1,59 @@
* Allwinner PRCM (Power/Reset/Clock Management) Multi-Functional Device
PRCM is an MFD device exposing several Power Management related devices
(like clks and reset controllers).
Required properties:
- compatible: "allwinner,sun6i-a31-prcm"
- reg: The PRCM registers range
The prcm node may contain several subdevices definitions:
- see Documentation/devicetree/clk/sunxi.txt for clock devices
- see Documentation/devicetree/reset/allwinner,sunxi-clock-reset.txt for reset
controller devices
Example:
prcm: prcm@01f01400 {
compatible = "allwinner,sun6i-a31-prcm";
reg = <0x01f01400 0x200>;
/* Put subdevices here */
ar100: ar100_clk {
compatible = "allwinner,sun6i-a31-ar100-clk";
#clock-cells = <0>;
clocks = <&osc32k>, <&osc24M>, <&pll6>, <&pll6>;
};
ahb0: ahb0_clk {
compatible = "fixed-factor-clock";
#clock-cells = <0>;
clock-div = <1>;
clock-mult = <1>;
clocks = <&ar100_div>;
clock-output-names = "ahb0";
};
apb0: apb0_clk {
compatible = "allwinner,sun6i-a31-apb0-clk";
#clock-cells = <0>;
clocks = <&ahb0>;
clock-output-names = "apb0";
};
apb0_gates: apb0_gates_clk {
compatible = "allwinner,sun6i-a31-apb0-gates-clk";
#clock-cells = <1>;
clocks = <&apb0>;
clock-output-names = "apb0_pio", "apb0_ir",
"apb0_timer01", "apb0_p2wi",
"apb0_uart", "apb0_1wire",
"apb0_i2c";
};
apb0_rst: apb0_rst {
compatible = "allwinner,sun6i-a31-clock-reset";
#reset-cells = <1>;
};
};

View File

@ -0,0 +1,19 @@
* Device tree bindings for Texas Instruments keystone device state control
The Keystone II devices have a set of registers that are used to control
the status of its peripherals. This node is intended to allow access to
this functionality.
Required properties:
- compatible: "ti,keystone-devctrl", "syscon"
- reg: contains offset/length value for device state control
registers space.
Example:
devctrl: device-state-control@0x02620000 {
compatible = "ti,keystone-devctrl", "syscon";
reg = <0x02620000 0x1000>;
};

View File

@ -19,6 +19,8 @@ Required properties:
Optional properties, nodes: Optional properties, nodes:
- enable-active-high: To power on the twl6040 during boot. - enable-active-high: To power on the twl6040 during boot.
- clocks: phandle to the clk32k clock provider
- clock-names: Must be "clk32k"
Vibra functionality Vibra functionality
Required properties: Required properties:

View File

@ -23,7 +23,8 @@
enum { REG_RE, REG_FE, REG_IE }; enum { REG_RE, REG_FE, REG_IE };
#define CACHE_NR_REGS 3 #define CACHE_NR_REGS 3
#define CACHE_NR_BANKS (STMPE_NR_GPIOS / 8) /* No variant has more than 24 GPIOs */
#define CACHE_NR_BANKS (24 / 8)
struct stmpe_gpio { struct stmpe_gpio {
struct gpio_chip chip; struct gpio_chip chip;
@ -31,8 +32,6 @@ struct stmpe_gpio {
struct device *dev; struct device *dev;
struct mutex irq_lock; struct mutex irq_lock;
struct irq_domain *domain; struct irq_domain *domain;
int irq_base;
unsigned norequest_mask; unsigned norequest_mask;
/* Caches of interrupt control registers for bus_lock */ /* Caches of interrupt control registers for bus_lock */
@ -311,13 +310,8 @@ static const struct irq_domain_ops stmpe_gpio_irq_simple_ops = {
static int stmpe_gpio_irq_init(struct stmpe_gpio *stmpe_gpio, static int stmpe_gpio_irq_init(struct stmpe_gpio *stmpe_gpio,
struct device_node *np) struct device_node *np)
{ {
int base = 0;
if (!np)
base = stmpe_gpio->irq_base;
stmpe_gpio->domain = irq_domain_add_simple(np, stmpe_gpio->domain = irq_domain_add_simple(np,
stmpe_gpio->chip.ngpio, base, stmpe_gpio->chip.ngpio, 0,
&stmpe_gpio_irq_simple_ops, stmpe_gpio); &stmpe_gpio_irq_simple_ops, stmpe_gpio);
if (!stmpe_gpio->domain) { if (!stmpe_gpio->domain) {
dev_err(stmpe_gpio->dev, "failed to create irqdomain\n"); dev_err(stmpe_gpio->dev, "failed to create irqdomain\n");
@ -354,7 +348,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
#ifdef CONFIG_OF #ifdef CONFIG_OF
stmpe_gpio->chip.of_node = np; stmpe_gpio->chip.of_node = np;
#endif #endif
stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1; stmpe_gpio->chip.base = -1;
if (pdata) if (pdata)
stmpe_gpio->norequest_mask = pdata->norequest_mask; stmpe_gpio->norequest_mask = pdata->norequest_mask;
@ -362,9 +356,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
of_property_read_u32(np, "st,norequest-mask", of_property_read_u32(np, "st,norequest-mask",
&stmpe_gpio->norequest_mask); &stmpe_gpio->norequest_mask);
if (irq >= 0) if (irq < 0)
stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0);
else
dev_info(&pdev->dev, dev_info(&pdev->dev,
"device configured in no-irq mode; " "device configured in no-irq mode; "
"irqs are not available\n"); "irqs are not available\n");

View File

@ -993,6 +993,15 @@ config I2C_SIBYTE
help help
Supports the SiByte SOC on-chip I2C interfaces (2 channels). Supports the SiByte SOC on-chip I2C interfaces (2 channels).
config I2C_CROS_EC_TUNNEL
tristate "ChromeOS EC tunnel I2C bus"
depends on MFD_CROS_EC
help
If you say yes here you get an I2C bus that will tunnel i2c commands
through to the other side of the ChromeOS EC to the i2c bus
connected there. This will work whatever the interface used to
talk to the EC (SPI, I2C or LPC).
config SCx200_I2C config SCx200_I2C
tristate "NatSemi SCx200 I2C using GPIO pins (DEPRECATED)" tristate "NatSemi SCx200 I2C using GPIO pins (DEPRECATED)"
depends on SCx200_GPIO depends on SCx200_GPIO

View File

@ -95,6 +95,7 @@ obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o
# Other I2C/SMBus bus drivers # Other I2C/SMBus bus drivers
obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o
obj-$(CONFIG_I2C_BCM_KONA) += i2c-bcm-kona.o obj-$(CONFIG_I2C_BCM_KONA) += i2c-bcm-kona.o
obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o

View File

@ -0,0 +1,318 @@
/*
* Copyright (C) 2013 Google, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Expose an I2C passthrough to the ChromeOS EC.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/mfd/cros_ec.h>
#include <linux/mfd/cros_ec_commands.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
/**
* struct ec_i2c_device - Driver data for I2C tunnel
*
* @dev: Device node
* @adap: I2C adapter
* @ec: Pointer to EC device
* @remote_bus: The EC bus number we tunnel to on the other side.
* @request_buf: Buffer for transmitting data; we expect most transfers to fit.
* @response_buf: Buffer for receiving data; we expect most transfers to fit.
*/
struct ec_i2c_device {
struct device *dev;
struct i2c_adapter adap;
struct cros_ec_device *ec;
u16 remote_bus;
u8 request_buf[256];
u8 response_buf[256];
};
/**
* ec_i2c_count_message - Count bytes needed for ec_i2c_construct_message
*
* @i2c_msgs: The i2c messages to read
* @num: The number of i2c messages.
*
* Returns the number of bytes the messages will take up.
*/
static int ec_i2c_count_message(const struct i2c_msg i2c_msgs[], int num)
{
int i;
int size;
size = sizeof(struct ec_params_i2c_passthru);
size += num * sizeof(struct ec_params_i2c_passthru_msg);
for (i = 0; i < num; i++)
if (!(i2c_msgs[i].flags & I2C_M_RD))
size += i2c_msgs[i].len;
return size;
}
/**
* ec_i2c_construct_message - construct a message to go to the EC
*
* This function effectively stuffs the standard i2c_msg format of Linux into
* a format that the EC understands.
*
* @buf: The buffer to fill. We assume that the buffer is big enough.
* @i2c_msgs: The i2c messages to read.
* @num: The number of i2c messages.
* @bus_num: The remote bus number we want to talk to.
*
* Returns 0 or a negative error number.
*/
static int ec_i2c_construct_message(u8 *buf, const struct i2c_msg i2c_msgs[],
int num, u16 bus_num)
{
struct ec_params_i2c_passthru *params;
u8 *out_data;
int i;
out_data = buf + sizeof(struct ec_params_i2c_passthru) +
num * sizeof(struct ec_params_i2c_passthru_msg);
params = (struct ec_params_i2c_passthru *)buf;
params->port = bus_num;
params->num_msgs = num;
for (i = 0; i < num; i++) {
const struct i2c_msg *i2c_msg = &i2c_msgs[i];
struct ec_params_i2c_passthru_msg *msg = &params->msg[i];
msg->len = i2c_msg->len;
msg->addr_flags = i2c_msg->addr;
if (i2c_msg->flags & I2C_M_TEN)
msg->addr_flags |= EC_I2C_FLAG_10BIT;
if (i2c_msg->flags & I2C_M_RD) {
msg->addr_flags |= EC_I2C_FLAG_READ;
} else {
memcpy(out_data, i2c_msg->buf, msg->len);
out_data += msg->len;
}
}
return 0;
}
/**
* ec_i2c_count_response - Count bytes needed for ec_i2c_parse_response
*
* @i2c_msgs: The i2c messages to to fill up.
* @num: The number of i2c messages expected.
*
* Returns the number of response bytes expeced.
*/
static int ec_i2c_count_response(struct i2c_msg i2c_msgs[], int num)
{
int size;
int i;
size = sizeof(struct ec_response_i2c_passthru);
for (i = 0; i < num; i++)
if (i2c_msgs[i].flags & I2C_M_RD)
size += i2c_msgs[i].len;
return size;
}
/**
* ec_i2c_parse_response - Parse a response from the EC
*
* We'll take the EC's response and copy it back into msgs.
*
* @buf: The buffer to parse.
* @i2c_msgs: The i2c messages to to fill up.
* @num: The number of i2c messages; will be modified to include the actual
* number received.
*
* Returns 0 or a negative error number.
*/
static int ec_i2c_parse_response(const u8 *buf, struct i2c_msg i2c_msgs[],
int *num)
{
const struct ec_response_i2c_passthru *resp;
const u8 *in_data;
int i;
in_data = buf + sizeof(struct ec_response_i2c_passthru);
resp = (const struct ec_response_i2c_passthru *)buf;
if (resp->i2c_status & EC_I2C_STATUS_TIMEOUT)
return -ETIMEDOUT;
else if (resp->i2c_status & EC_I2C_STATUS_ERROR)
return -EREMOTEIO;
/* Other side could send us back fewer messages, but not more */
if (resp->num_msgs > *num)
return -EPROTO;
*num = resp->num_msgs;
for (i = 0; i < *num; i++) {
struct i2c_msg *i2c_msg = &i2c_msgs[i];
if (i2c_msgs[i].flags & I2C_M_RD) {
memcpy(i2c_msg->buf, in_data, i2c_msg->len);
in_data += i2c_msg->len;
}
}
return 0;
}
static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[],
int num)
{
struct ec_i2c_device *bus = adap->algo_data;
struct device *dev = bus->dev;
const u16 bus_num = bus->remote_bus;
int request_len;
int response_len;
u8 *request = NULL;
u8 *response = NULL;
int result;
request_len = ec_i2c_count_message(i2c_msgs, num);
if (request_len < 0) {
dev_warn(dev, "Error constructing message %d\n", request_len);
result = request_len;
goto exit;
}
response_len = ec_i2c_count_response(i2c_msgs, num);
if (response_len < 0) {
/* Unexpected; no errors should come when NULL response */
dev_warn(dev, "Error preparing response %d\n", response_len);
result = response_len;
goto exit;
}
if (request_len <= ARRAY_SIZE(bus->request_buf)) {
request = bus->request_buf;
} else {
request = kzalloc(request_len, GFP_KERNEL);
if (request == NULL) {
result = -ENOMEM;
goto exit;
}
}
if (response_len <= ARRAY_SIZE(bus->response_buf)) {
response = bus->response_buf;
} else {
response = kzalloc(response_len, GFP_KERNEL);
if (response == NULL) {
result = -ENOMEM;
goto exit;
}
}
ec_i2c_construct_message(request, i2c_msgs, num, bus_num);
result = bus->ec->command_sendrecv(bus->ec, EC_CMD_I2C_PASSTHRU,
request, request_len,
response, response_len);
if (result)
goto exit;
result = ec_i2c_parse_response(response, i2c_msgs, &num);
if (result < 0)
goto exit;
/* Indicate success by saying how many messages were sent */
result = num;
exit:
if (request != bus->request_buf)
kfree(request);
if (response != bus->response_buf)
kfree(response);
return result;
}
static u32 ec_i2c_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm ec_i2c_algorithm = {
.master_xfer = ec_i2c_xfer,
.functionality = ec_i2c_functionality,
};
static int ec_i2c_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
struct device *dev = &pdev->dev;
struct ec_i2c_device *bus = NULL;
u32 remote_bus;
int err;
if (!ec->command_sendrecv) {
dev_err(dev, "Missing sendrecv\n");
return -EINVAL;
}
bus = devm_kzalloc(dev, sizeof(*bus), GFP_KERNEL);
if (bus == NULL)
return -ENOMEM;
err = of_property_read_u32(np, "google,remote-bus", &remote_bus);
if (err) {
dev_err(dev, "Couldn't read remote-bus property\n");
return err;
}
bus->remote_bus = remote_bus;
bus->ec = ec;
bus->dev = dev;
bus->adap.owner = THIS_MODULE;
strlcpy(bus->adap.name, "cros-ec-i2c-tunnel", sizeof(bus->adap.name));
bus->adap.algo = &ec_i2c_algorithm;
bus->adap.algo_data = bus;
bus->adap.dev.parent = &pdev->dev;
bus->adap.dev.of_node = np;
err = i2c_add_adapter(&bus->adap);
if (err) {
dev_err(dev, "cannot register i2c adapter\n");
return err;
}
platform_set_drvdata(pdev, bus);
return err;
}
static int ec_i2c_remove(struct platform_device *dev)
{
struct ec_i2c_device *bus = platform_get_drvdata(dev);
i2c_del_adapter(&bus->adap);
return 0;
}
static struct platform_driver ec_i2c_tunnel_driver = {
.probe = ec_i2c_probe,
.remove = ec_i2c_remove,
.driver = {
.name = "cros-ec-i2c-tunnel",
},
};
module_platform_driver(ec_i2c_tunnel_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EC I2C tunnel driver");
MODULE_ALIAS("platform:cros-ec-i2c-tunnel");

View File

@ -52,3 +52,13 @@ config MEMSTICK_REALTEK_PCI
To compile this driver as a module, choose M here: the module will To compile this driver as a module, choose M here: the module will
be called rtsx_pci_ms. be called rtsx_pci_ms.
config MEMSTICK_REALTEK_USB
tristate "Realtek USB Memstick Card Interface Driver"
depends on MFD_RTSX_USB
help
Say Y here to include driver code to support Memstick card interface
of Realtek RTS5129/39 series USB card reader
To compile this driver as a module, choose M here: the module will
be called rts5139_ms.

View File

@ -6,3 +6,4 @@ obj-$(CONFIG_MEMSTICK_TIFM_MS) += tifm_ms.o
obj-$(CONFIG_MEMSTICK_JMICRON_38X) += jmb38x_ms.o obj-$(CONFIG_MEMSTICK_JMICRON_38X) += jmb38x_ms.o
obj-$(CONFIG_MEMSTICK_R592) += r592.o obj-$(CONFIG_MEMSTICK_R592) += r592.o
obj-$(CONFIG_MEMSTICK_REALTEK_PCI) += rtsx_pci_ms.o obj-$(CONFIG_MEMSTICK_REALTEK_PCI) += rtsx_pci_ms.o
obj-$(CONFIG_MEMSTICK_REALTEK_USB) += rtsx_usb_ms.o

View File

@ -0,0 +1,839 @@
/* Realtek USB Memstick Card Interface driver
*
* Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Author:
* Roger Tseng <rogerable@realtek.com>
*/
#include <linux/module.h>
#include <linux/highmem.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/memstick.h>
#include <linux/kthread.h>
#include <linux/mfd/rtsx_usb.h>
#include <linux/pm_runtime.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/completion.h>
#include <asm/unaligned.h>
struct rtsx_usb_ms {
struct platform_device *pdev;
struct rtsx_ucr *ucr;
struct memstick_host *msh;
struct memstick_request *req;
struct mutex host_mutex;
struct work_struct handle_req;
struct task_struct *detect_ms;
struct completion detect_ms_exit;
u8 ssc_depth;
unsigned int clock;
int power_mode;
unsigned char ifmode;
bool eject;
};
static inline struct device *ms_dev(struct rtsx_usb_ms *host)
{
return &(host->pdev->dev);
}
static inline void ms_clear_error(struct rtsx_usb_ms *host)
{
struct rtsx_ucr *ucr = host->ucr;
rtsx_usb_ep0_write_register(ucr, CARD_STOP,
MS_STOP | MS_CLR_ERR,
MS_STOP | MS_CLR_ERR);
rtsx_usb_clear_dma_err(ucr);
rtsx_usb_clear_fsm_err(ucr);
}
#ifdef DEBUG
static void ms_print_debug_regs(struct rtsx_usb_ms *host)
{
struct rtsx_ucr *ucr = host->ucr;
u16 i;
u8 *ptr;
/* Print MS host internal registers */
rtsx_usb_init_cmd(ucr);
/* MS_CFG to MS_INT_REG */
for (i = 0xFD40; i <= 0xFD44; i++)
rtsx_usb_add_cmd(ucr, READ_REG_CMD, i, 0, 0);
/* CARD_SHARE_MODE to CARD_GPIO */
for (i = 0xFD51; i <= 0xFD56; i++)
rtsx_usb_add_cmd(ucr, READ_REG_CMD, i, 0, 0);
/* CARD_PULL_CTLx */
for (i = 0xFD60; i <= 0xFD65; i++)
rtsx_usb_add_cmd(ucr, READ_REG_CMD, i, 0, 0);
/* CARD_DATA_SOURCE, CARD_SELECT, CARD_CLK_EN, CARD_PWR_CTL */
rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_DATA_SOURCE, 0, 0);
rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_SELECT, 0, 0);
rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_CLK_EN, 0, 0);
rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_PWR_CTL, 0, 0);
rtsx_usb_send_cmd(ucr, MODE_CR, 100);
rtsx_usb_get_rsp(ucr, 21, 100);
ptr = ucr->rsp_buf;
for (i = 0xFD40; i <= 0xFD44; i++)
dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
for (i = 0xFD51; i <= 0xFD56; i++)
dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
for (i = 0xFD60; i <= 0xFD65; i++)
dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_DATA_SOURCE, *(ptr++));
dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_SELECT, *(ptr++));
dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_CLK_EN, *(ptr++));
dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_PWR_CTL, *(ptr++));
}
#else
static void ms_print_debug_regs(struct rtsx_usb_ms *host)
{
}
#endif
static int ms_pull_ctl_disable_lqfp48(struct rtsx_ucr *ucr)
{
rtsx_usb_init_cmd(ucr);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0xA5);
return rtsx_usb_send_cmd(ucr, MODE_C, 100);
}
static int ms_pull_ctl_disable_qfn24(struct rtsx_ucr *ucr)
{
rtsx_usb_init_cmd(ucr);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x65);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x56);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x59);
return rtsx_usb_send_cmd(ucr, MODE_C, 100);
}
static int ms_pull_ctl_enable_lqfp48(struct rtsx_ucr *ucr)
{
rtsx_usb_init_cmd(ucr);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0xA5);
return rtsx_usb_send_cmd(ucr, MODE_C, 100);
}
static int ms_pull_ctl_enable_qfn24(struct rtsx_ucr *ucr)
{
rtsx_usb_init_cmd(ucr);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x65);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x59);
return rtsx_usb_send_cmd(ucr, MODE_C, 100);
}
static int ms_power_on(struct rtsx_usb_ms *host)
{
struct rtsx_ucr *ucr = host->ucr;
int err;
dev_dbg(ms_dev(host), "%s\n", __func__);
rtsx_usb_init_cmd(ucr);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SELECT, 0x07, MS_MOD_SEL);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SHARE_MODE,
CARD_SHARE_MASK, CARD_SHARE_MS);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_EN,
MS_CLK_EN, MS_CLK_EN);
err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
if (err < 0)
return err;
if (CHECK_PKG(ucr, LQFP48))
err = ms_pull_ctl_enable_lqfp48(ucr);
else
err = ms_pull_ctl_enable_qfn24(ucr);
if (err < 0)
return err;
err = rtsx_usb_write_register(ucr, CARD_PWR_CTL,
POWER_MASK, PARTIAL_POWER_ON);
if (err)
return err;
usleep_range(800, 1000);
rtsx_usb_init_cmd(ucr);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
POWER_MASK, POWER_ON);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE,
MS_OUTPUT_EN, MS_OUTPUT_EN);
return rtsx_usb_send_cmd(ucr, MODE_C, 100);
}
static int ms_power_off(struct rtsx_usb_ms *host)
{
struct rtsx_ucr *ucr = host->ucr;
int err;
dev_dbg(ms_dev(host), "%s\n", __func__);
rtsx_usb_init_cmd(ucr);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_EN, MS_CLK_EN, 0);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE, MS_OUTPUT_EN, 0);
err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
if (err < 0)
return err;
if (CHECK_PKG(ucr, LQFP48))
return ms_pull_ctl_disable_lqfp48(ucr);
return ms_pull_ctl_disable_qfn24(ucr);
}
static int ms_transfer_data(struct rtsx_usb_ms *host, unsigned char data_dir,
u8 tpc, u8 cfg, struct scatterlist *sg)
{
struct rtsx_ucr *ucr = host->ucr;
int err;
unsigned int length = sg->length;
u16 sec_cnt = (u16)(length / 512);
u8 trans_mode, dma_dir, flag;
unsigned int pipe;
struct memstick_dev *card = host->msh->card;
dev_dbg(ms_dev(host), "%s: tpc = 0x%02x, data_dir = %s, length = %d\n",
__func__, tpc, (data_dir == READ) ? "READ" : "WRITE",
length);
if (data_dir == READ) {
flag = MODE_CDIR;
dma_dir = DMA_DIR_FROM_CARD;
if (card->id.type != MEMSTICK_TYPE_PRO)
trans_mode = MS_TM_NORMAL_READ;
else
trans_mode = MS_TM_AUTO_READ;
pipe = usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN);
} else {
flag = MODE_CDOR;
dma_dir = DMA_DIR_TO_CARD;
if (card->id.type != MEMSTICK_TYPE_PRO)
trans_mode = MS_TM_NORMAL_WRITE;
else
trans_mode = MS_TM_AUTO_WRITE;
pipe = usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT);
}
rtsx_usb_init_cmd(ucr);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
if (card->id.type == MEMSTICK_TYPE_PRO) {
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_SECTOR_CNT_H,
0xFF, (u8)(sec_cnt >> 8));
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_SECTOR_CNT_L,
0xFF, (u8)sec_cnt);
}
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC3,
0xFF, (u8)(length >> 24));
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC2,
0xFF, (u8)(length >> 16));
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC1,
0xFF, (u8)(length >> 8));
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC0, 0xFF,
(u8)length);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_CTL,
0x03 | DMA_PACK_SIZE_MASK, dma_dir | DMA_EN | DMA_512);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE,
0x01, RING_BUFFER);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER,
0xFF, MS_TRANSFER_START | trans_mode);
rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, MS_TRANSFER,
MS_TRANSFER_END, MS_TRANSFER_END);
err = rtsx_usb_send_cmd(ucr, flag | STAGE_MS_STATUS, 100);
if (err)
return err;
err = rtsx_usb_transfer_data(ucr, pipe, sg, length,
1, NULL, 10000);
if (err)
goto err_out;
err = rtsx_usb_get_rsp(ucr, 3, 15000);
if (err)
goto err_out;
if (ucr->rsp_buf[0] & MS_TRANSFER_ERR ||
ucr->rsp_buf[1] & (MS_CRC16_ERR | MS_RDY_TIMEOUT)) {
err = -EIO;
goto err_out;
}
return 0;
err_out:
ms_clear_error(host);
return err;
}
static int ms_write_bytes(struct rtsx_usb_ms *host, u8 tpc,
u8 cfg, u8 cnt, u8 *data, u8 *int_reg)
{
struct rtsx_ucr *ucr = host->ucr;
int err, i;
dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc);
rtsx_usb_init_cmd(ucr);
for (i = 0; i < cnt; i++)
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
PPBUF_BASE2 + i, 0xFF, data[i]);
if (cnt % 2)
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
PPBUF_BASE2 + i, 0xFF, 0xFF);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE,
0x01, PINGPONG_BUFFER);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER,
0xFF, MS_TRANSFER_START | MS_TM_WRITE_BYTES);
rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, MS_TRANSFER,
MS_TRANSFER_END, MS_TRANSFER_END);
rtsx_usb_add_cmd(ucr, READ_REG_CMD, MS_TRANS_CFG, 0, 0);
err = rtsx_usb_send_cmd(ucr, MODE_CR, 100);
if (err)
return err;
err = rtsx_usb_get_rsp(ucr, 2, 5000);
if (err || (ucr->rsp_buf[0] & MS_TRANSFER_ERR)) {
u8 val;
rtsx_usb_ep0_read_register(ucr, MS_TRANS_CFG, &val);
dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val);
if (int_reg)
*int_reg = val & 0x0F;
ms_print_debug_regs(host);
ms_clear_error(host);
if (!(tpc & 0x08)) {
if (val & MS_CRC16_ERR)
return -EIO;
} else {
if (!(val & 0x80)) {
if (val & (MS_INT_ERR | MS_INT_CMDNK))
return -EIO;
}
}
return -ETIMEDOUT;
}
if (int_reg)
*int_reg = ucr->rsp_buf[1] & 0x0F;
return 0;
}
static int ms_read_bytes(struct rtsx_usb_ms *host, u8 tpc,
u8 cfg, u8 cnt, u8 *data, u8 *int_reg)
{
struct rtsx_ucr *ucr = host->ucr;
int err, i;
u8 *ptr;
dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc);
rtsx_usb_init_cmd(ucr);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE,
0x01, PINGPONG_BUFFER);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER,
0xFF, MS_TRANSFER_START | MS_TM_READ_BYTES);
rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, MS_TRANSFER,
MS_TRANSFER_END, MS_TRANSFER_END);
for (i = 0; i < cnt - 1; i++)
rtsx_usb_add_cmd(ucr, READ_REG_CMD, PPBUF_BASE2 + i, 0, 0);
if (cnt % 2)
rtsx_usb_add_cmd(ucr, READ_REG_CMD, PPBUF_BASE2 + cnt, 0, 0);
else
rtsx_usb_add_cmd(ucr, READ_REG_CMD,
PPBUF_BASE2 + cnt - 1, 0, 0);
rtsx_usb_add_cmd(ucr, READ_REG_CMD, MS_TRANS_CFG, 0, 0);
err = rtsx_usb_send_cmd(ucr, MODE_CR, 100);
if (err)
return err;
err = rtsx_usb_get_rsp(ucr, cnt + 2, 5000);
if (err || (ucr->rsp_buf[0] & MS_TRANSFER_ERR)) {
u8 val;
rtsx_usb_ep0_read_register(ucr, MS_TRANS_CFG, &val);
dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val);
if (int_reg && (host->ifmode != MEMSTICK_SERIAL))
*int_reg = val & 0x0F;
ms_print_debug_regs(host);
ms_clear_error(host);
if (!(tpc & 0x08)) {
if (val & MS_CRC16_ERR)
return -EIO;
} else {
if (!(val & 0x80)) {
if (val & (MS_INT_ERR | MS_INT_CMDNK))
return -EIO;
}
}
return -ETIMEDOUT;
}
ptr = ucr->rsp_buf + 1;
for (i = 0; i < cnt; i++)
data[i] = *ptr++;
if (int_reg && (host->ifmode != MEMSTICK_SERIAL))
*int_reg = *ptr & 0x0F;
return 0;
}
static int rtsx_usb_ms_issue_cmd(struct rtsx_usb_ms *host)
{
struct memstick_request *req = host->req;
int err = 0;
u8 cfg = 0, int_reg;
dev_dbg(ms_dev(host), "%s\n", __func__);
if (req->need_card_int) {
if (host->ifmode != MEMSTICK_SERIAL)
cfg = WAIT_INT;
}
if (req->long_data) {
err = ms_transfer_data(host, req->data_dir,
req->tpc, cfg, &(req->sg));
} else {
if (req->data_dir == READ)
err = ms_read_bytes(host, req->tpc, cfg,
req->data_len, req->data, &int_reg);
else
err = ms_write_bytes(host, req->tpc, cfg,
req->data_len, req->data, &int_reg);
}
if (err < 0)
return err;
if (req->need_card_int) {
if (host->ifmode == MEMSTICK_SERIAL) {
err = ms_read_bytes(host, MS_TPC_GET_INT,
NO_WAIT_INT, 1, &req->int_reg, NULL);
if (err < 0)
return err;
} else {
if (int_reg & MS_INT_CMDNK)
req->int_reg |= MEMSTICK_INT_CMDNAK;
if (int_reg & MS_INT_BREQ)
req->int_reg |= MEMSTICK_INT_BREQ;
if (int_reg & MS_INT_ERR)
req->int_reg |= MEMSTICK_INT_ERR;
if (int_reg & MS_INT_CED)
req->int_reg |= MEMSTICK_INT_CED;
}
dev_dbg(ms_dev(host), "int_reg: 0x%02x\n", req->int_reg);
}
return 0;
}
static void rtsx_usb_ms_handle_req(struct work_struct *work)
{
struct rtsx_usb_ms *host = container_of(work,
struct rtsx_usb_ms, handle_req);
struct rtsx_ucr *ucr = host->ucr;
struct memstick_host *msh = host->msh;
int rc;
if (!host->req) {
do {
rc = memstick_next_req(msh, &host->req);
dev_dbg(ms_dev(host), "next req %d\n", rc);
if (!rc) {
mutex_lock(&ucr->dev_mutex);
if (rtsx_usb_card_exclusive_check(ucr,
RTSX_USB_MS_CARD))
host->req->error = -EIO;
else
host->req->error =
rtsx_usb_ms_issue_cmd(host);
mutex_unlock(&ucr->dev_mutex);
dev_dbg(ms_dev(host), "req result %d\n",
host->req->error);
}
} while (!rc);
}
}
static void rtsx_usb_ms_request(struct memstick_host *msh)
{
struct rtsx_usb_ms *host = memstick_priv(msh);
dev_dbg(ms_dev(host), "--> %s\n", __func__);
if (!host->eject)
schedule_work(&host->handle_req);
}
static int rtsx_usb_ms_set_param(struct memstick_host *msh,
enum memstick_param param, int value)
{
struct rtsx_usb_ms *host = memstick_priv(msh);
struct rtsx_ucr *ucr = host->ucr;
unsigned int clock = 0;
u8 ssc_depth = 0;
int err;
dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n",
__func__, param, value);
mutex_lock(&ucr->dev_mutex);
err = rtsx_usb_card_exclusive_check(ucr, RTSX_USB_MS_CARD);
if (err)
goto out;
switch (param) {
case MEMSTICK_POWER:
if (value == host->power_mode)
break;
if (value == MEMSTICK_POWER_ON) {
pm_runtime_get_sync(ms_dev(host));
err = ms_power_on(host);
} else if (value == MEMSTICK_POWER_OFF) {
err = ms_power_off(host);
if (host->msh->card)
pm_runtime_put_noidle(ms_dev(host));
else
pm_runtime_put(ms_dev(host));
} else
err = -EINVAL;
if (!err)
host->power_mode = value;
break;
case MEMSTICK_INTERFACE:
if (value == MEMSTICK_SERIAL) {
clock = 19000000;
ssc_depth = SSC_DEPTH_512K;
err = rtsx_usb_write_register(ucr, MS_CFG, 0x5A,
MS_BUS_WIDTH_1 | PUSH_TIME_DEFAULT);
if (err < 0)
break;
} else if (value == MEMSTICK_PAR4) {
clock = 39000000;
ssc_depth = SSC_DEPTH_1M;
err = rtsx_usb_write_register(ucr, MS_CFG, 0x5A,
MS_BUS_WIDTH_4 | PUSH_TIME_ODD |
MS_NO_CHECK_INT);
if (err < 0)
break;
} else {
err = -EINVAL;
break;
}
err = rtsx_usb_switch_clock(ucr, clock,
ssc_depth, false, true, false);
if (err < 0) {
dev_dbg(ms_dev(host), "switch clock failed\n");
break;
}
host->ssc_depth = ssc_depth;
host->clock = clock;
host->ifmode = value;
break;
default:
err = -EINVAL;
break;
}
out:
mutex_unlock(&ucr->dev_mutex);
/* power-on delay */
if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON)
usleep_range(10000, 12000);
dev_dbg(ms_dev(host), "%s: return = %d\n", __func__, err);
return err;
}
#ifdef CONFIG_PM_SLEEP
static int rtsx_usb_ms_suspend(struct device *dev)
{
struct rtsx_usb_ms *host = dev_get_drvdata(dev);
struct memstick_host *msh = host->msh;
dev_dbg(ms_dev(host), "--> %s\n", __func__);
memstick_suspend_host(msh);
return 0;
}
static int rtsx_usb_ms_resume(struct device *dev)
{
struct rtsx_usb_ms *host = dev_get_drvdata(dev);
struct memstick_host *msh = host->msh;
dev_dbg(ms_dev(host), "--> %s\n", __func__);
memstick_resume_host(msh);
return 0;
}
#endif /* CONFIG_PM_SLEEP */
/*
* Thread function of ms card slot detection. The thread starts right after
* successful host addition. It stops while the driver removal function sets
* host->eject true.
*/
static int rtsx_usb_detect_ms_card(void *__host)
{
struct rtsx_usb_ms *host = (struct rtsx_usb_ms *)__host;
struct rtsx_ucr *ucr = host->ucr;
u8 val = 0;
int err;
for (;;) {
mutex_lock(&ucr->dev_mutex);
/* Check pending MS card changes */
err = rtsx_usb_read_register(ucr, CARD_INT_PEND, &val);
if (err) {
mutex_unlock(&ucr->dev_mutex);
goto poll_again;
}
/* Clear the pending */
rtsx_usb_write_register(ucr, CARD_INT_PEND,
XD_INT | MS_INT | SD_INT,
XD_INT | MS_INT | SD_INT);
mutex_unlock(&ucr->dev_mutex);
if (val & MS_INT) {
dev_dbg(ms_dev(host), "MS slot change detected\n");
memstick_detect_change(host->msh);
}
poll_again:
if (host->eject)
break;
msleep(1000);
}
complete(&host->detect_ms_exit);
return 0;
}
static int rtsx_usb_ms_drv_probe(struct platform_device *pdev)
{
struct memstick_host *msh;
struct rtsx_usb_ms *host;
struct rtsx_ucr *ucr;
int err;
ucr = usb_get_intfdata(to_usb_interface(pdev->dev.parent));
if (!ucr)
return -ENXIO;
dev_dbg(&(pdev->dev),
"Realtek USB Memstick controller found\n");
msh = memstick_alloc_host(sizeof(*host), &pdev->dev);
if (!msh)
return -ENOMEM;
host = memstick_priv(msh);
host->ucr = ucr;
host->msh = msh;
host->pdev = pdev;
host->power_mode = MEMSTICK_POWER_OFF;
platform_set_drvdata(pdev, host);
mutex_init(&host->host_mutex);
INIT_WORK(&host->handle_req, rtsx_usb_ms_handle_req);
init_completion(&host->detect_ms_exit);
host->detect_ms = kthread_create(rtsx_usb_detect_ms_card, host,
"rtsx_usb_ms_%d", pdev->id);
if (IS_ERR(host->detect_ms)) {
dev_dbg(&(pdev->dev),
"Unable to create polling thread.\n");
err = PTR_ERR(host->detect_ms);
goto err_out;
}
msh->request = rtsx_usb_ms_request;
msh->set_param = rtsx_usb_ms_set_param;
msh->caps = MEMSTICK_CAP_PAR4;
pm_runtime_enable(&pdev->dev);
err = memstick_add_host(msh);
if (err)
goto err_out;
wake_up_process(host->detect_ms);
return 0;
err_out:
memstick_free_host(msh);
return err;
}
static int rtsx_usb_ms_drv_remove(struct platform_device *pdev)
{
struct rtsx_usb_ms *host = platform_get_drvdata(pdev);
struct memstick_host *msh;
int err;
msh = host->msh;
host->eject = true;
cancel_work_sync(&host->handle_req);
mutex_lock(&host->host_mutex);
if (host->req) {
dev_dbg(&(pdev->dev),
"%s: Controller removed during transfer\n",
dev_name(&msh->dev));
host->req->error = -ENOMEDIUM;
do {
err = memstick_next_req(msh, &host->req);
if (!err)
host->req->error = -ENOMEDIUM;
} while (!err);
}
mutex_unlock(&host->host_mutex);
wait_for_completion(&host->detect_ms_exit);
memstick_remove_host(msh);
memstick_free_host(msh);
/* Balance possible unbalanced usage count
* e.g. unconditional module removal
*/
if (pm_runtime_active(ms_dev(host)))
pm_runtime_put(ms_dev(host));
pm_runtime_disable(&pdev->dev);
platform_set_drvdata(pdev, NULL);
dev_dbg(&(pdev->dev),
": Realtek USB Memstick controller has been removed\n");
return 0;
}
static SIMPLE_DEV_PM_OPS(rtsx_usb_ms_pm_ops,
rtsx_usb_ms_suspend, rtsx_usb_ms_resume);
static struct platform_device_id rtsx_usb_ms_ids[] = {
{
.name = "rtsx_usb_ms",
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(platform, rtsx_usb_ms_ids);
static struct platform_driver rtsx_usb_ms_driver = {
.probe = rtsx_usb_ms_drv_probe,
.remove = rtsx_usb_ms_drv_remove,
.id_table = rtsx_usb_ms_ids,
.driver = {
.owner = THIS_MODULE,
.name = "rtsx_usb_ms",
.pm = &rtsx_usb_ms_pm_ops,
},
};
module_platform_driver(rtsx_usb_ms_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Roger Tseng <rogerable@realtek.com>");
MODULE_DESCRIPTION("Realtek USB Memstick Card Host Driver");

View File

@ -67,6 +67,18 @@ config MFD_BCM590XX
help help
Support for the BCM590xx PMUs from Broadcom Support for the BCM590xx PMUs from Broadcom
config MFD_AXP20X
bool "X-Powers AXP20X"
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
depends on I2C=y
help
If you say Y here you get support for the X-Powers AXP202 and AXP209.
This driver include only the core APIs. You have to select individual
components like regulators or the PEK (Power Enable Key) under the
corresponding menus.
config MFD_CROS_EC config MFD_CROS_EC
tristate "ChromeOS Embedded Controller" tristate "ChromeOS Embedded Controller"
select MFD_CORE select MFD_CORE
@ -250,6 +262,16 @@ config MFD_INTEL_MSIC
Passage) chip. This chip embeds audio, battery, GPIO, etc. Passage) chip. This chip embeds audio, battery, GPIO, etc.
devices used in Intel Medfield platforms. devices used in Intel Medfield platforms.
config MFD_IPAQ_MICRO
bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
depends on SA1100_H3100 || SA1100_H3600
select MFD_CORE
help
Select this to get support for the Microcontroller found in
the Compaq iPAQ handheld computers. This is an Atmel
AT90LS8535 microcontroller flashed with a special iPAQ
firmware using the custom protocol implemented in this driver.
config MFD_JANZ_CMODIO config MFD_JANZ_CMODIO
tristate "Janz CMOD-IO PCI MODULbus Carrier Board" tristate "Janz CMOD-IO PCI MODULbus Carrier Board"
select MFD_CORE select MFD_CORE
@ -675,6 +697,7 @@ config MFD_DB8500_PRCMU
config MFD_STMPE config MFD_STMPE
bool "STMicroelectronics STMPE" bool "STMicroelectronics STMPE"
depends on (I2C=y || SPI_MASTER=y) depends on (I2C=y || SPI_MASTER=y)
depends on OF
select MFD_CORE select MFD_CORE
help help
Support for the STMPE family of I/O Expanders from Support for the STMPE family of I/O Expanders from
@ -719,6 +742,14 @@ config MFD_STA2X11
select MFD_CORE select MFD_CORE
select REGMAP_MMIO select REGMAP_MMIO
config MFD_SUN6I_PRCM
bool "Allwinner A31 PRCM controller"
depends on ARCH_SUNXI
select MFD_CORE
help
Support for the PRCM (Power/Reset/Clock Management) unit available
in A31 SoC.
config MFD_SYSCON config MFD_SYSCON
bool "System Controller Register R/W Based on Regmap" bool "System Controller Register R/W Based on Regmap"
select REGMAP_MMIO select REGMAP_MMIO

View File

@ -29,6 +29,7 @@ obj-$(CONFIG_MFD_STA2X11) += sta2x11-mfd.o
obj-$(CONFIG_MFD_STMPE) += stmpe.o obj-$(CONFIG_MFD_STMPE) += stmpe.o
obj-$(CONFIG_STMPE_I2C) += stmpe-i2c.o obj-$(CONFIG_STMPE_I2C) += stmpe-i2c.o
obj-$(CONFIG_STMPE_SPI) += stmpe-spi.o obj-$(CONFIG_STMPE_SPI) += stmpe-spi.o
obj-$(CONFIG_MFD_SUN6I_PRCM) += sun6i-prcm.o
obj-$(CONFIG_MFD_TC3589X) += tc3589x.o obj-$(CONFIG_MFD_TC3589X) += tc3589x.o
obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o
obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o
@ -102,6 +103,7 @@ obj-$(CONFIG_PMIC_DA9052) += da9052-irq.o
obj-$(CONFIG_PMIC_DA9052) += da9052-core.o obj-$(CONFIG_PMIC_DA9052) += da9052-core.o
obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o
obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
obj-$(CONFIG_MFD_AXP20X) += axp20x.o
obj-$(CONFIG_MFD_LP3943) += lp3943.o obj-$(CONFIG_MFD_LP3943) += lp3943.o
obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o
@ -166,3 +168,4 @@ obj-$(CONFIG_MFD_RETU) += retu-mfd.o
obj-$(CONFIG_MFD_AS3711) += as3711.o obj-$(CONFIG_MFD_AS3711) += as3711.o
obj-$(CONFIG_MFD_AS3722) += as3722.o obj-$(CONFIG_MFD_AS3722) += as3722.o
obj-$(CONFIG_MFD_STW481X) += stw481x.o obj-$(CONFIG_MFD_STW481X) += stw481x.o
obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o

View File

@ -151,22 +151,6 @@ int abx500_startup_irq_enabled(struct device *dev, unsigned int irq)
} }
EXPORT_SYMBOL(abx500_startup_irq_enabled); EXPORT_SYMBOL(abx500_startup_irq_enabled);
void abx500_dump_all_banks(void)
{
struct abx500_ops *ops;
struct device dummy_child = {NULL};
struct abx500_device_entry *dev_entry;
list_for_each_entry(dev_entry, &abx500_list, list) {
dummy_child.parent = dev_entry->dev;
ops = &dev_entry->ops;
if ((ops != NULL) && (ops->dump_all_banks != NULL))
ops->dump_all_banks(&dummy_child);
}
}
EXPORT_SYMBOL(abx500_dump_all_banks);
MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>"); MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
MODULE_DESCRIPTION("ABX500 core driver"); MODULE_DESCRIPTION("ABX500 core driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -583,6 +583,7 @@ static const char *wm5102_supplies[] = {
"CPVDD", "CPVDD",
"SPKVDDL", "SPKVDDL",
"SPKVDDR", "SPKVDDR",
"MICVDD",
}; };
static const struct mfd_cell wm5102_devs[] = { static const struct mfd_cell wm5102_devs[] = {

View File

@ -285,7 +285,7 @@ int arizona_irq_init(struct arizona *arizona)
IRQF_ONESHOT, -1, irq, IRQF_ONESHOT, -1, irq,
&arizona->irq_chip); &arizona->irq_chip);
if (ret != 0) { if (ret != 0) {
dev_err(arizona->dev, "Failed to add AOD IRQs: %d\n", ret); dev_err(arizona->dev, "Failed to add main IRQs: %d\n", ret);
goto err_aod; goto err_aod;
} }

View File

@ -114,7 +114,7 @@ static const struct regmap_config as3711_regmap_config = {
}; };
#ifdef CONFIG_OF #ifdef CONFIG_OF
static struct of_device_id as3711_of_match[] = { static const struct of_device_id as3711_of_match[] = {
{.compatible = "ams,as3711",}, {.compatible = "ams,as3711",},
{} {}
}; };

258
drivers/mfd/axp20x.c Normal file
View File

@ -0,0 +1,258 @@
/*
* axp20x.c - MFD core driver for the X-Powers AXP202 and AXP209
*
* AXP20x comprises an adaptive USB-Compatible PWM charger, 2 BUCK DC-DC
* converters, 5 LDOs, multiple 12-bit ADCs of voltage, current and temperature
* as well as 4 configurable GPIOs.
*
* Author: Carlo Caione <carlo@caione.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
#include <linux/mfd/axp20x.h>
#include <linux/mfd/core.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#define AXP20X_OFF 0x80
static const struct regmap_range axp20x_writeable_ranges[] = {
regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE),
regmap_reg_range(AXP20X_DCDC_MODE, AXP20X_FG_RES),
};
static const struct regmap_range axp20x_volatile_ranges[] = {
regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE),
};
static const struct regmap_access_table axp20x_writeable_table = {
.yes_ranges = axp20x_writeable_ranges,
.n_yes_ranges = ARRAY_SIZE(axp20x_writeable_ranges),
};
static const struct regmap_access_table axp20x_volatile_table = {
.yes_ranges = axp20x_volatile_ranges,
.n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges),
};
static struct resource axp20x_pek_resources[] = {
{
.name = "PEK_DBR",
.start = AXP20X_IRQ_PEK_RIS_EDGE,
.end = AXP20X_IRQ_PEK_RIS_EDGE,
.flags = IORESOURCE_IRQ,
}, {
.name = "PEK_DBF",
.start = AXP20X_IRQ_PEK_FAL_EDGE,
.end = AXP20X_IRQ_PEK_FAL_EDGE,
.flags = IORESOURCE_IRQ,
},
};
static const struct regmap_config axp20x_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.wr_table = &axp20x_writeable_table,
.volatile_table = &axp20x_volatile_table,
.max_register = AXP20X_FG_RES,
.cache_type = REGCACHE_RBTREE,
};
#define AXP20X_IRQ(_irq, _off, _mask) \
[AXP20X_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) }
static const struct regmap_irq axp20x_regmap_irqs[] = {
AXP20X_IRQ(ACIN_OVER_V, 0, 7),
AXP20X_IRQ(ACIN_PLUGIN, 0, 6),
AXP20X_IRQ(ACIN_REMOVAL, 0, 5),
AXP20X_IRQ(VBUS_OVER_V, 0, 4),
AXP20X_IRQ(VBUS_PLUGIN, 0, 3),
AXP20X_IRQ(VBUS_REMOVAL, 0, 2),
AXP20X_IRQ(VBUS_V_LOW, 0, 1),
AXP20X_IRQ(BATT_PLUGIN, 1, 7),
AXP20X_IRQ(BATT_REMOVAL, 1, 6),
AXP20X_IRQ(BATT_ENT_ACT_MODE, 1, 5),
AXP20X_IRQ(BATT_EXIT_ACT_MODE, 1, 4),
AXP20X_IRQ(CHARG, 1, 3),
AXP20X_IRQ(CHARG_DONE, 1, 2),
AXP20X_IRQ(BATT_TEMP_HIGH, 1, 1),
AXP20X_IRQ(BATT_TEMP_LOW, 1, 0),
AXP20X_IRQ(DIE_TEMP_HIGH, 2, 7),
AXP20X_IRQ(CHARG_I_LOW, 2, 6),
AXP20X_IRQ(DCDC1_V_LONG, 2, 5),
AXP20X_IRQ(DCDC2_V_LONG, 2, 4),
AXP20X_IRQ(DCDC3_V_LONG, 2, 3),
AXP20X_IRQ(PEK_SHORT, 2, 1),
AXP20X_IRQ(PEK_LONG, 2, 0),
AXP20X_IRQ(N_OE_PWR_ON, 3, 7),
AXP20X_IRQ(N_OE_PWR_OFF, 3, 6),
AXP20X_IRQ(VBUS_VALID, 3, 5),
AXP20X_IRQ(VBUS_NOT_VALID, 3, 4),
AXP20X_IRQ(VBUS_SESS_VALID, 3, 3),
AXP20X_IRQ(VBUS_SESS_END, 3, 2),
AXP20X_IRQ(LOW_PWR_LVL1, 3, 1),
AXP20X_IRQ(LOW_PWR_LVL2, 3, 0),
AXP20X_IRQ(TIMER, 4, 7),
AXP20X_IRQ(PEK_RIS_EDGE, 4, 6),
AXP20X_IRQ(PEK_FAL_EDGE, 4, 5),
AXP20X_IRQ(GPIO3_INPUT, 4, 3),
AXP20X_IRQ(GPIO2_INPUT, 4, 2),
AXP20X_IRQ(GPIO1_INPUT, 4, 1),
AXP20X_IRQ(GPIO0_INPUT, 4, 0),
};
static const struct of_device_id axp20x_of_match[] = {
{ .compatible = "x-powers,axp202", .data = (void *) AXP202_ID },
{ .compatible = "x-powers,axp209", .data = (void *) AXP209_ID },
{ },
};
MODULE_DEVICE_TABLE(of, axp20x_of_match);
/*
* This is useless for OF-enabled devices, but it is needed by I2C subsystem
*/
static const struct i2c_device_id axp20x_i2c_id[] = {
{ },
};
MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id);
static const struct regmap_irq_chip axp20x_regmap_irq_chip = {
.name = "axp20x_irq_chip",
.status_base = AXP20X_IRQ1_STATE,
.ack_base = AXP20X_IRQ1_STATE,
.mask_base = AXP20X_IRQ1_EN,
.num_regs = 5,
.irqs = axp20x_regmap_irqs,
.num_irqs = ARRAY_SIZE(axp20x_regmap_irqs),
.mask_invert = true,
.init_ack_masked = true,
};
static const char * const axp20x_supplies[] = {
"acin",
"vin2",
"vin3",
"ldo24in",
"ldo3in",
"ldo5in",
};
static struct mfd_cell axp20x_cells[] = {
{
.name = "axp20x-pek",
.num_resources = ARRAY_SIZE(axp20x_pek_resources),
.resources = axp20x_pek_resources,
}, {
.name = "axp20x-regulator",
.parent_supplies = axp20x_supplies,
.num_parent_supplies = ARRAY_SIZE(axp20x_supplies),
},
};
static struct axp20x_dev *axp20x_pm_power_off;
static void axp20x_power_off(void)
{
regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL,
AXP20X_OFF);
}
static int axp20x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct axp20x_dev *axp20x;
const struct of_device_id *of_id;
int ret;
axp20x = devm_kzalloc(&i2c->dev, sizeof(*axp20x), GFP_KERNEL);
if (!axp20x)
return -ENOMEM;
of_id = of_match_device(axp20x_of_match, &i2c->dev);
if (!of_id) {
dev_err(&i2c->dev, "Unable to setup AXP20X data\n");
return -ENODEV;
}
axp20x->variant = (long) of_id->data;
axp20x->i2c_client = i2c;
axp20x->dev = &i2c->dev;
dev_set_drvdata(axp20x->dev, axp20x);
axp20x->regmap = devm_regmap_init_i2c(i2c, &axp20x_regmap_config);
if (IS_ERR(axp20x->regmap)) {
ret = PTR_ERR(axp20x->regmap);
dev_err(&i2c->dev, "regmap init failed: %d\n", ret);
return ret;
}
ret = regmap_add_irq_chip(axp20x->regmap, i2c->irq,
IRQF_ONESHOT | IRQF_SHARED, -1,
&axp20x_regmap_irq_chip,
&axp20x->regmap_irqc);
if (ret) {
dev_err(&i2c->dev, "failed to add irq chip: %d\n", ret);
return ret;
}
ret = mfd_add_devices(axp20x->dev, -1, axp20x_cells,
ARRAY_SIZE(axp20x_cells), NULL, 0, NULL);
if (ret) {
dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret);
regmap_del_irq_chip(i2c->irq, axp20x->regmap_irqc);
return ret;
}
if (!pm_power_off) {
axp20x_pm_power_off = axp20x;
pm_power_off = axp20x_power_off;
}
dev_info(&i2c->dev, "AXP20X driver loaded\n");
return 0;
}
static int axp20x_i2c_remove(struct i2c_client *i2c)
{
struct axp20x_dev *axp20x = i2c_get_clientdata(i2c);
if (axp20x == axp20x_pm_power_off) {
axp20x_pm_power_off = NULL;
pm_power_off = NULL;
}
mfd_remove_devices(axp20x->dev);
regmap_del_irq_chip(axp20x->i2c_client->irq, axp20x->regmap_irqc);
return 0;
}
static struct i2c_driver axp20x_i2c_driver = {
.driver = {
.name = "axp20x",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(axp20x_of_match),
},
.probe = axp20x_i2c_probe,
.remove = axp20x_i2c_remove,
.id_table = axp20x_i2c_id,
};
module_i2c_driver(axp20x_i2c_driver);
MODULE_DESCRIPTION("PMIC MFD core driver for AXP20X");
MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
MODULE_LICENSE("GPL");

View File

@ -96,6 +96,12 @@ err:
return ret; return ret;
} }
static int bcm590xx_i2c_remove(struct i2c_client *i2c)
{
mfd_remove_devices(&i2c->dev);
return 0;
}
static const struct of_device_id bcm590xx_of_match[] = { static const struct of_device_id bcm590xx_of_match[] = {
{ .compatible = "brcm,bcm59056" }, { .compatible = "brcm,bcm59056" },
{ } { }
@ -115,6 +121,7 @@ static struct i2c_driver bcm590xx_i2c_driver = {
.of_match_table = of_match_ptr(bcm590xx_of_match), .of_match_table = of_match_ptr(bcm590xx_of_match),
}, },
.probe = bcm590xx_i2c_probe, .probe = bcm590xx_i2c_probe,
.remove = bcm590xx_i2c_remove,
.id_table = bcm590xx_i2c_id, .id_table = bcm590xx_i2c_id,
}; };
module_i2c_driver(bcm590xx_i2c_driver); module_i2c_driver(bcm590xx_i2c_driver);
@ -122,4 +129,4 @@ module_i2c_driver(bcm590xx_i2c_driver);
MODULE_AUTHOR("Matt Porter <mporter@linaro.org>"); MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
MODULE_DESCRIPTION("BCM590xx multi-function driver"); MODULE_DESCRIPTION("BCM590xx multi-function driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:bcm590xx"); MODULE_ALIAS("i2c:bcm590xx");

View File

@ -30,7 +30,7 @@ int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
uint8_t *out; uint8_t *out;
int csum, i; int csum, i;
BUG_ON(msg->out_len > EC_HOST_PARAM_SIZE); BUG_ON(msg->out_len > EC_PROTO2_MAX_PARAM_SIZE);
out = ec_dev->dout; out = ec_dev->dout;
out[0] = EC_CMD_VERSION0 + msg->version; out[0] = EC_CMD_VERSION0 + msg->version;
out[1] = msg->cmd; out[1] = msg->cmd;
@ -90,6 +90,11 @@ static const struct mfd_cell cros_devs[] = {
.id = 1, .id = 1,
.of_compatible = "google,cros-ec-keyb", .of_compatible = "google,cros-ec-keyb",
}, },
{
.name = "cros-ec-i2c-tunnel",
.id = 2,
.of_compatible = "google,cros-ec-i2c-tunnel",
},
}; };
int cros_ec_register(struct cros_ec_device *ec_dev) int cros_ec_register(struct cros_ec_device *ec_dev)
@ -184,3 +189,6 @@ int cros_ec_resume(struct cros_ec_device *ec_dev)
EXPORT_SYMBOL(cros_ec_resume); EXPORT_SYMBOL(cros_ec_resume);
#endif #endif
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ChromeOS EC core driver");

View File

@ -39,14 +39,22 @@
#define EC_MSG_PREAMBLE_COUNT 32 #define EC_MSG_PREAMBLE_COUNT 32
/* /*
* We must get a response from the EC in 5ms. This is a very long * Allow for a long time for the EC to respond. We support i2c
* time, but the flash write command can take 2-3ms. The EC command * tunneling and support fairly long messages for the tunnel (249
* processing is currently not very fast (about 500us). We could * bytes long at the moment). If we're talking to a 100 kHz device
* look at speeding this up and making the flash write command a * on the other end and need to transfer ~256 bytes, then we need:
* 'slow' command, requiring a GET_STATUS wait loop, like flash * 10 us/bit * ~10 bits/byte * ~256 bytes = ~25ms
* erase. *
*/ * We'll wait 4 times that to handle clock stretching and other
#define EC_MSG_DEADLINE_MS 5 * paranoia.
*
* 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 100
/* /*
* Time between raising the SPI chip select (for the end of a * Time between raising the SPI chip select (for the end of a
@ -65,11 +73,13 @@
* if no record * if no record
* @end_of_msg_delay: used to set the delay_usecs on the spi_transfer that * @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. * is sent when we want to turn off CS at the end of a transaction.
* @lock: mutex to ensure only one user of cros_ec_command_spi_xfer at a time
*/ */
struct cros_ec_spi { struct cros_ec_spi {
struct spi_device *spi; struct spi_device *spi;
s64 last_transfer_ns; s64 last_transfer_ns;
unsigned int end_of_msg_delay; unsigned int end_of_msg_delay;
struct mutex lock;
}; };
static void debug_packet(struct device *dev, const char *name, u8 *ptr, static void debug_packet(struct device *dev, const char *name, u8 *ptr,
@ -111,7 +121,9 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
/* Receive data until we see the header byte */ /* Receive data until we see the header byte */
deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS); deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS);
do { while (true) {
unsigned long start_jiffies = jiffies;
memset(&trans, 0, sizeof(trans)); memset(&trans, 0, sizeof(trans));
trans.cs_change = 1; trans.cs_change = 1;
trans.rx_buf = ptr = ec_dev->din; trans.rx_buf = ptr = ec_dev->din;
@ -132,12 +144,19 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
break; break;
} }
} }
if (ptr != end)
break;
if (time_after(jiffies, deadline)) { /*
* 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"); dev_warn(ec_dev->dev, "EC failed to respond in time\n");
return -ETIMEDOUT; return -ETIMEDOUT;
} }
} while (ptr == end); }
/* /*
* ptr now points to the header byte. Copy any valid data to the * ptr now points to the header byte. Copy any valid data to the
@ -208,6 +227,13 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev,
int ret = 0, final_ret; int ret = 0, final_ret;
struct timespec ts; struct timespec ts;
/*
* We have the shared ec_dev buffer plus we do lots of separate spi_sync
* calls, so we need to make sure only one person is using this at a
* time.
*/
mutex_lock(&ec_spi->lock);
len = cros_ec_prepare_tx(ec_dev, ec_msg); len = cros_ec_prepare_tx(ec_dev, ec_msg);
dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);
@ -219,7 +245,7 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev,
ktime_get_ts(&ts); ktime_get_ts(&ts);
delay = timespec_to_ns(&ts) - ec_spi->last_transfer_ns; delay = timespec_to_ns(&ts) - ec_spi->last_transfer_ns;
if (delay < EC_SPI_RECOVERY_TIME_NS) if (delay < EC_SPI_RECOVERY_TIME_NS)
ndelay(delay); ndelay(EC_SPI_RECOVERY_TIME_NS - delay);
} }
/* Transmit phase - send our message */ /* Transmit phase - send our message */
@ -260,7 +286,7 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev,
ret = final_ret; ret = final_ret;
if (ret < 0) { if (ret < 0) {
dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
return ret; goto exit;
} }
/* check response error code */ /* check response error code */
@ -269,14 +295,16 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev,
dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n", dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n",
ec_msg->cmd, ptr[0]); ec_msg->cmd, ptr[0]);
debug_packet(ec_dev->dev, "in_err", ptr, len); debug_packet(ec_dev->dev, "in_err", ptr, len);
return -EINVAL; ret = -EINVAL;
goto exit;
} }
len = ptr[1]; len = ptr[1];
sum = ptr[0] + ptr[1]; sum = ptr[0] + ptr[1];
if (len > ec_msg->in_len) { if (len > ec_msg->in_len) {
dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)", dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
len, ec_msg->in_len); len, ec_msg->in_len);
return -ENOSPC; ret = -ENOSPC;
goto exit;
} }
/* copy response packet payload and compute checksum */ /* copy response packet payload and compute checksum */
@ -293,10 +321,14 @@ static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev,
dev_err(ec_dev->dev, dev_err(ec_dev->dev,
"bad packet checksum, expected %02x, got %02x\n", "bad packet checksum, expected %02x, got %02x\n",
sum, ptr[len + 2]); sum, ptr[len + 2]);
return -EBADMSG; ret = -EBADMSG;
goto exit;
} }
return 0; ret = 0;
exit:
mutex_unlock(&ec_spi->lock);
return ret;
} }
static void cros_ec_spi_dt_probe(struct cros_ec_spi *ec_spi, struct device *dev) static void cros_ec_spi_dt_probe(struct cros_ec_spi *ec_spi, struct device *dev)
@ -327,6 +359,7 @@ static int cros_ec_spi_probe(struct spi_device *spi)
if (ec_spi == NULL) if (ec_spi == NULL)
return -ENOMEM; return -ENOMEM;
ec_spi->spi = spi; ec_spi->spi = spi;
mutex_init(&ec_spi->lock);
ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
if (!ec_dev) if (!ec_dev)
return -ENOMEM; return -ENOMEM;

View File

@ -2300,9 +2300,6 @@ int prcmu_ac_wake_req(void)
if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work, if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
msecs_to_jiffies(5000))) { msecs_to_jiffies(5000))) {
#if defined(CONFIG_DBX500_PRCMU_DEBUG)
db8500_prcmu_debug_dump(__func__, true, true);
#endif
pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n", pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
__func__); __func__);
ret = -EFAULT; ret = -EFAULT;
@ -3112,7 +3109,7 @@ static int db8500_prcmu_register_ab8500(struct device *parent,
{ {
struct device_node *np; struct device_node *np;
struct resource ab8500_resource; struct resource ab8500_resource;
struct mfd_cell ab8500_cell = { const struct mfd_cell ab8500_cell = {
.name = "ab8500-core", .name = "ab8500-core",
.of_compatible = "stericsson,ab8500", .of_compatible = "stericsson,ab8500",
.id = AB8500_VERSION_AB8500, .id = AB8500_VERSION_AB8500,

482
drivers/mfd/ipaq-micro.c Normal file
View File

@ -0,0 +1,482 @@
/*
* Compaq iPAQ h3xxx Atmel microcontroller companion support
*
* This is an Atmel AT90LS8535 with a special flashed-in firmware that
* implements the special protocol used by this driver.
*
* based on previous kernel 2.4 version by Andrew Christian
* Author : Alessandro Gardich <gremlin@gremlin.it>
* Author : Dmitry Artamonow <mad_soft@inbox.ru>
* Author : Linus Walleij <linus.walleij@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/mfd/core.h>
#include <linux/mfd/ipaq-micro.h>
#include <linux/string.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <mach/hardware.h>
static void ipaq_micro_trigger_tx(struct ipaq_micro *micro)
{
struct ipaq_micro_txdev *tx = &micro->tx;
struct ipaq_micro_msg *msg = micro->msg;
int i, bp;
u8 checksum;
u32 val;
bp = 0;
tx->buf[bp++] = CHAR_SOF;
checksum = ((msg->id & 0x0f) << 4) | (msg->tx_len & 0x0f);
tx->buf[bp++] = checksum;
for (i = 0; i < msg->tx_len; i++) {
tx->buf[bp++] = msg->tx_data[i];
checksum += msg->tx_data[i];
}
tx->buf[bp++] = checksum;
tx->len = bp;
tx->index = 0;
print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_OFFSET, 16, 1,
tx->buf, tx->len, true);
/* Enable interrupt */
val = readl(micro->base + UTCR3);
val |= UTCR3_TIE;
writel(val, micro->base + UTCR3);
}
int ipaq_micro_tx_msg(struct ipaq_micro *micro, struct ipaq_micro_msg *msg)
{
unsigned long flags;
dev_dbg(micro->dev, "TX msg: %02x, %d bytes\n", msg->id, msg->tx_len);
spin_lock_irqsave(&micro->lock, flags);
if (micro->msg) {
list_add_tail(&msg->node, &micro->queue);
spin_unlock_irqrestore(&micro->lock, flags);
return 0;
}
micro->msg = msg;
ipaq_micro_trigger_tx(micro);
spin_unlock_irqrestore(&micro->lock, flags);
return 0;
}
EXPORT_SYMBOL(ipaq_micro_tx_msg);
static void micro_rx_msg(struct ipaq_micro *micro, u8 id, int len, u8 *data)
{
int i;
dev_dbg(micro->dev, "RX msg: %02x, %d bytes\n", id, len);
spin_lock(&micro->lock);
switch (id) {
case MSG_VERSION:
case MSG_EEPROM_READ:
case MSG_EEPROM_WRITE:
case MSG_BACKLIGHT:
case MSG_NOTIFY_LED:
case MSG_THERMAL_SENSOR:
case MSG_BATTERY:
/* Handle synchronous messages */
if (micro->msg && micro->msg->id == id) {
struct ipaq_micro_msg *msg = micro->msg;
memcpy(msg->rx_data, data, len);
msg->rx_len = len;
complete(&micro->msg->ack);
if (!list_empty(&micro->queue)) {
micro->msg = list_entry(micro->queue.next,
struct ipaq_micro_msg,
node);
list_del_init(&micro->msg->node);
ipaq_micro_trigger_tx(micro);
} else
micro->msg = NULL;
dev_dbg(micro->dev, "OK RX message 0x%02x\n", id);
} else {
dev_err(micro->dev,
"out of band RX message 0x%02x\n", id);
if(!micro->msg)
dev_info(micro->dev, "no message queued\n");
else
dev_info(micro->dev, "expected message %02x\n",
micro->msg->id);
}
break;
case MSG_KEYBOARD:
if (micro->key)
micro->key(micro->key_data, len, data);
else
dev_dbg(micro->dev, "key message ignored, no handle \n");
break;
case MSG_TOUCHSCREEN:
if (micro->ts)
micro->ts(micro->ts_data, len, data);
else
dev_dbg(micro->dev, "touchscreen message ignored, no handle \n");
break;
default:
dev_err(micro->dev,
"unknown msg %d [%d] ", id, len);
for (i = 0; i < len; ++i)
pr_cont("0x%02x ", data[i]);
pr_cont("\n");
}
spin_unlock(&micro->lock);
}
static void micro_process_char(struct ipaq_micro *micro, u8 ch)
{
struct ipaq_micro_rxdev *rx = &micro->rx;
switch (rx->state) {
case STATE_SOF: /* Looking for SOF */
if (ch == CHAR_SOF)
rx->state = STATE_ID; /* Next byte is the id and len */
break;
case STATE_ID: /* Looking for id and len byte */
rx->id = (ch & 0xf0) >> 4 ;
rx->len = (ch & 0x0f);
rx->index = 0;
rx->chksum = ch;
rx->state = (rx->len > 0) ? STATE_DATA : STATE_CHKSUM;
break;
case STATE_DATA: /* Looking for 'len' data bytes */
rx->chksum += ch;
rx->buf[rx->index] = ch;
if (++rx->index == rx->len)
rx->state = STATE_CHKSUM;
break;
case STATE_CHKSUM: /* Looking for the checksum */
if (ch == rx->chksum)
micro_rx_msg(micro, rx->id, rx->len, rx->buf);
rx->state = STATE_SOF;
break;
}
}
static void micro_rx_chars(struct ipaq_micro *micro)
{
u32 status, ch;
while ((status = readl(micro->base + UTSR1)) & UTSR1_RNE) {
ch = readl(micro->base + UTDR);
if (status & UTSR1_PRE)
dev_err(micro->dev, "rx: parity error\n");
else if (status & UTSR1_FRE)
dev_err(micro->dev, "rx: framing error\n");
else if (status & UTSR1_ROR)
dev_err(micro->dev, "rx: overrun error\n");
micro_process_char(micro, ch);
}
}
static void ipaq_micro_get_version(struct ipaq_micro *micro)
{
struct ipaq_micro_msg msg = {
.id = MSG_VERSION,
};
ipaq_micro_tx_msg_sync(micro, &msg);
if (msg.rx_len == 4) {
memcpy(micro->version, msg.rx_data, 4);
micro->version[4] = '\0';
} else if (msg.rx_len == 9) {
memcpy(micro->version, msg.rx_data, 4);
micro->version[4] = '\0';
/* Bytes 4-7 are "pack", byte 8 is "boot type" */
} else {
dev_err(micro->dev,
"illegal version message %d bytes\n", msg.rx_len);
}
}
static void ipaq_micro_eeprom_read(struct ipaq_micro *micro,
u8 address, u8 len, u8 *data)
{
struct ipaq_micro_msg msg = {
.id = MSG_EEPROM_READ,
};
u8 i;
for (i = 0; i < len; i++) {
msg.tx_data[0] = address + i;
msg.tx_data[1] = 1;
msg.tx_len = 2;
ipaq_micro_tx_msg_sync(micro, &msg);
memcpy(data + (i * 2), msg.rx_data, 2);
}
}
static char *ipaq_micro_str(u8 *wchar, u8 len)
{
char retstr[256];
u8 i;
for (i = 0; i < len / 2; i++)
retstr[i] = wchar[i * 2];
return kstrdup(retstr, GFP_KERNEL);
}
static u16 ipaq_micro_to_u16(u8 *data)
{
return data[1] << 8 | data[0];
}
static void ipaq_micro_eeprom_dump(struct ipaq_micro *micro)
{
u8 dump[256];
char *str;
ipaq_micro_eeprom_read(micro, 0, 128, dump);
str = ipaq_micro_str(dump, 10);
if (str) {
dev_info(micro->dev, "HM version %s\n", str);
kfree(str);
}
str = ipaq_micro_str(dump+10, 40);
if (str) {
dev_info(micro->dev, "serial number: %s\n", str);
/* Feed the random pool with this */
add_device_randomness(str, strlen(str));
kfree(str);
}
str = ipaq_micro_str(dump+50, 20);
if (str) {
dev_info(micro->dev, "module ID: %s\n", str);
kfree(str);
}
str = ipaq_micro_str(dump+70, 10);
if (str) {
dev_info(micro->dev, "product revision: %s\n", str);
kfree(str);
}
dev_info(micro->dev, "product ID: %u\n", ipaq_micro_to_u16(dump+80));
dev_info(micro->dev, "frame rate: %u fps\n",
ipaq_micro_to_u16(dump+82));
dev_info(micro->dev, "page mode: %u\n", ipaq_micro_to_u16(dump+84));
dev_info(micro->dev, "country ID: %u\n", ipaq_micro_to_u16(dump+86));
dev_info(micro->dev, "color display: %s\n",
ipaq_micro_to_u16(dump+88) ? "yes" : "no");
dev_info(micro->dev, "ROM size: %u MiB\n", ipaq_micro_to_u16(dump+90));
dev_info(micro->dev, "RAM size: %u KiB\n", ipaq_micro_to_u16(dump+92));
dev_info(micro->dev, "screen: %u x %u\n",
ipaq_micro_to_u16(dump+94), ipaq_micro_to_u16(dump+96));
print_hex_dump(KERN_DEBUG, "eeprom: ", DUMP_PREFIX_OFFSET, 16, 1,
dump, 256, true);
}
static void micro_tx_chars(struct ipaq_micro *micro)
{
struct ipaq_micro_txdev *tx = &micro->tx;
u32 val;
while ((tx->index < tx->len) &&
(readl(micro->base + UTSR1) & UTSR1_TNF)) {
writel(tx->buf[tx->index], micro->base + UTDR);
tx->index++;
}
/* Stop interrupts */
val = readl(micro->base + UTCR3);
val &= ~UTCR3_TIE;
writel(val, micro->base + UTCR3);
}
static void micro_reset_comm(struct ipaq_micro *micro)
{
struct ipaq_micro_rxdev *rx = &micro->rx;
u32 val;
if (micro->msg)
complete(&micro->msg->ack);
/* Initialize Serial channel protocol frame */
rx->state = STATE_SOF; /* Reset the state machine */
/* Set up interrupts */
writel(0x01, micro->sdlc + 0x0); /* Select UART mode */
/* Clean up CR3 */
writel(0x0, micro->base + UTCR3);
/* Format: 8N1 */
writel(UTCR0_8BitData | UTCR0_1StpBit, micro->base + UTCR0);
/* Baud rate: 115200 */
writel(0x0, micro->base + UTCR1);
writel(0x1, micro->base + UTCR2);
/* Clear SR0 */
writel(0xff, micro->base + UTSR0);
/* Enable RX int, disable TX int */
writel(UTCR3_TXE | UTCR3_RXE | UTCR3_RIE, micro->base + UTCR3);
val = readl(micro->base + UTCR3);
val &= ~UTCR3_TIE;
writel(val, micro->base + UTCR3);
}
static irqreturn_t micro_serial_isr(int irq, void *dev_id)
{
struct ipaq_micro *micro = dev_id;
struct ipaq_micro_txdev *tx = &micro->tx;
u32 status;
status = readl(micro->base + UTSR0);
do {
if (status & (UTSR0_RID | UTSR0_RFS)) {
if (status & UTSR0_RID)
/* Clear the Receiver IDLE bit */
writel(UTSR0_RID, micro->base + UTSR0);
micro_rx_chars(micro);
}
/* Clear break bits */
if (status & (UTSR0_RBB | UTSR0_REB))
writel(status & (UTSR0_RBB | UTSR0_REB),
micro->base + UTSR0);
if (status & UTSR0_TFS)
micro_tx_chars(micro);
status = readl(micro->base + UTSR0);
} while (((tx->index < tx->len) && (status & UTSR0_TFS)) ||
(status & (UTSR0_RFS | UTSR0_RID)));
return IRQ_HANDLED;
}
static const struct mfd_cell micro_cells[] = {
{ .name = "ipaq-micro-backlight", },
{ .name = "ipaq-micro-battery", },
{ .name = "ipaq-micro-keys", },
{ .name = "ipaq-micro-ts", },
{ .name = "ipaq-micro-leds", },
};
static int micro_resume(struct device *dev)
{
struct ipaq_micro *micro = dev_get_drvdata(dev);
micro_reset_comm(micro);
mdelay(10);
return 0;
}
static int micro_probe(struct platform_device *pdev)
{
struct ipaq_micro *micro;
struct resource *res;
int ret;
int irq;
micro = devm_kzalloc(&pdev->dev, sizeof(*micro), GFP_KERNEL);
if (!micro)
return -ENOMEM;
micro->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
micro->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(micro->base))
return PTR_ERR(micro->base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res)
return -EINVAL;
micro->sdlc = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(micro->sdlc))
return PTR_ERR(micro->sdlc);
micro_reset_comm(micro);
irq = platform_get_irq(pdev, 0);
if (!irq)
return -EINVAL;
ret = devm_request_irq(&pdev->dev, irq, micro_serial_isr,
IRQF_SHARED, "ipaq-micro",
micro);
if (ret) {
dev_err(&pdev->dev, "unable to grab serial port IRQ\n");
return ret;
} else
dev_info(&pdev->dev, "grabbed serial port IRQ\n");
spin_lock_init(&micro->lock);
INIT_LIST_HEAD(&micro->queue);
platform_set_drvdata(pdev, micro);
ret = mfd_add_devices(&pdev->dev, pdev->id, micro_cells,
ARRAY_SIZE(micro_cells), NULL, 0, NULL);
if (ret) {
dev_err(&pdev->dev, "error adding MFD cells");
return ret;
}
/* Check version */
ipaq_micro_get_version(micro);
dev_info(&pdev->dev, "Atmel micro ASIC version %s\n", micro->version);
ipaq_micro_eeprom_dump(micro);
return 0;
}
static int micro_remove(struct platform_device *pdev)
{
struct ipaq_micro *micro = platform_get_drvdata(pdev);
u32 val;
mfd_remove_devices(&pdev->dev);
val = readl(micro->base + UTCR3);
val &= ~(UTCR3_RXE | UTCR3_RIE); /* disable receive interrupt */
val &= ~(UTCR3_TXE | UTCR3_TIE); /* disable transmit interrupt */
writel(val, micro->base + UTCR3);
return 0;
}
static const struct dev_pm_ops micro_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(NULL, micro_resume)
};
static struct platform_driver micro_device_driver = {
.driver = {
.name = "ipaq-h3xxx-micro",
.pm = &micro_dev_pm_ops,
},
.probe = micro_probe,
.remove = micro_remove,
/* .shutdown = micro_suspend, // FIXME */
};
module_platform_driver(micro_device_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("driver for iPAQ Atmel micro core and backlight");

View File

@ -86,7 +86,7 @@ enum kempld_cells {
KEMPLD_UART, KEMPLD_UART,
}; };
static struct mfd_cell kempld_devs[] = { static const struct mfd_cell kempld_devs[] = {
[KEMPLD_I2C] = { [KEMPLD_I2C] = {
.name = "kempld-i2c", .name = "kempld-i2c",
}, },
@ -288,9 +288,38 @@ EXPORT_SYMBOL_GPL(kempld_release_mutex);
*/ */
static int kempld_get_info(struct kempld_device_data *pld) static int kempld_get_info(struct kempld_device_data *pld)
{ {
int ret;
struct kempld_platform_data *pdata = dev_get_platdata(pld->dev); struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
char major, minor;
return pdata->get_info(pld); ret = pdata->get_info(pld);
if (ret)
return ret;
/* The Kontron PLD firmware version string has the following format:
* Pwxy.zzzz
* P: Fixed
* w: PLD number - 1 hex digit
* x: Major version - 1 alphanumerical digit (0-9A-V)
* y: Minor version - 1 alphanumerical digit (0-9A-V)
* zzzz: Build number - 4 zero padded hex digits */
if (pld->info.major < 10)
major = pld->info.major + '0';
else
major = (pld->info.major - 10) + 'A';
if (pld->info.minor < 10)
minor = pld->info.minor + '0';
else
minor = (pld->info.minor - 10) + 'A';
ret = scnprintf(pld->info.version, sizeof(pld->info.version),
"P%X%c%c.%04X", pld->info.number, major, minor,
pld->info.buildnr);
if (ret < 0)
return ret;
return 0;
} }
/* /*
@ -307,9 +336,71 @@ static int kempld_register_cells(struct kempld_device_data *pld)
return pdata->register_cells(pld); return pdata->register_cells(pld);
} }
static const char *kempld_get_type_string(struct kempld_device_data *pld)
{
const char *version_type;
switch (pld->info.type) {
case 0:
version_type = "release";
break;
case 1:
version_type = "debug";
break;
case 2:
version_type = "custom";
break;
default:
version_type = "unspecified";
break;
}
return version_type;
}
static ssize_t kempld_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct kempld_device_data *pld = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%s\n", pld->info.version);
}
static ssize_t kempld_specification_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct kempld_device_data *pld = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d.%d\n", pld->info.spec_major,
pld->info.spec_minor);
}
static ssize_t kempld_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct kempld_device_data *pld = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%s\n", kempld_get_type_string(pld));
}
static DEVICE_ATTR(pld_version, S_IRUGO, kempld_version_show, NULL);
static DEVICE_ATTR(pld_specification, S_IRUGO, kempld_specification_show,
NULL);
static DEVICE_ATTR(pld_type, S_IRUGO, kempld_type_show, NULL);
static struct attribute *pld_attributes[] = {
&dev_attr_pld_version.attr,
&dev_attr_pld_specification.attr,
&dev_attr_pld_type.attr,
NULL
};
static const struct attribute_group pld_attr_group = {
.attrs = pld_attributes,
};
static int kempld_detect_device(struct kempld_device_data *pld) static int kempld_detect_device(struct kempld_device_data *pld)
{ {
char *version_type;
u8 index_reg; u8 index_reg;
int ret; int ret;
@ -335,27 +426,19 @@ static int kempld_detect_device(struct kempld_device_data *pld)
if (ret) if (ret)
return ret; return ret;
switch (pld->info.type) { dev_info(pld->dev, "Found Kontron PLD - %s (%s), spec %d.%d\n",
case 0: pld->info.version, kempld_get_type_string(pld),
version_type = "release"; pld->info.spec_major, pld->info.spec_minor);
break;
case 1:
version_type = "debug";
break;
case 2:
version_type = "custom";
break;
default:
version_type = "unspecified";
}
dev_info(pld->dev, "Found Kontron PLD %d\n", pld->info.number); ret = sysfs_create_group(&pld->dev->kobj, &pld_attr_group);
dev_info(pld->dev, "%s version %d.%d build %d, specification %d.%d\n", if (ret)
version_type, pld->info.major, pld->info.minor, return ret;
pld->info.buildnr, pld->info.spec_major,
pld->info.spec_minor);
return kempld_register_cells(pld); ret = kempld_register_cells(pld);
if (ret)
sysfs_remove_group(&pld->dev->kobj, &pld_attr_group);
return ret;
} }
static int kempld_probe(struct platform_device *pdev) static int kempld_probe(struct platform_device *pdev)
@ -399,6 +482,8 @@ static int kempld_remove(struct platform_device *pdev)
struct kempld_device_data *pld = platform_get_drvdata(pdev); struct kempld_device_data *pld = platform_get_drvdata(pdev);
struct kempld_platform_data *pdata = dev_get_platdata(pld->dev); struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
sysfs_remove_group(&pld->dev->kobj, &pld_attr_group);
mfd_remove_devices(&pdev->dev); mfd_remove_devices(&pdev->dev);
pdata->release_hardware_mutex(pld); pdata->release_hardware_mutex(pld);

View File

@ -62,7 +62,7 @@ static const struct lp3943_reg_cfg lp3943_mux_cfg[] = {
{ LP3943_REG_MUX3, 0xC0, 6 }, { LP3943_REG_MUX3, 0xC0, 6 },
}; };
static struct mfd_cell lp3943_devs[] = { static const struct mfd_cell lp3943_devs[] = {
{ {
.name = "lp3943-pwm", .name = "lp3943-pwm",
.of_compatible = "ti,lp3943-pwm", .of_compatible = "ti,lp3943-pwm",

View File

@ -488,6 +488,7 @@ static struct lpc_ich_info lpc_chipset_info[] = {
[LPC_PPT] = { [LPC_PPT] = {
.name = "Panther Point", .name = "Panther Point",
.iTCO_version = 2, .iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
}, },
[LPC_LPT] = { [LPC_LPT] = {
.name = "Lynx Point", .name = "Lynx Point",

View File

@ -26,7 +26,7 @@
#include <linux/mfd/max14577.h> #include <linux/mfd/max14577.h>
#include <linux/mfd/max14577-private.h> #include <linux/mfd/max14577-private.h>
static struct mfd_cell max14577_devs[] = { static const struct mfd_cell max14577_devs[] = {
{ {
.name = "max14577-muic", .name = "max14577-muic",
.of_compatible = "maxim,max14577-muic", .of_compatible = "maxim,max14577-muic",
@ -38,7 +38,7 @@ static struct mfd_cell max14577_devs[] = {
{ .name = "max14577-charger", }, { .name = "max14577-charger", },
}; };
static struct mfd_cell max77836_devs[] = { static const struct mfd_cell max77836_devs[] = {
{ {
.name = "max77836-muic", .name = "max77836-muic",
.of_compatible = "maxim,max77836-muic", .of_compatible = "maxim,max77836-muic",
@ -57,7 +57,7 @@ static struct mfd_cell max77836_devs[] = {
}, },
}; };
static struct of_device_id max14577_dt_match[] = { static const struct of_device_id max14577_dt_match[] = {
{ {
.compatible = "maxim,max14577", .compatible = "maxim,max14577",
.data = (void *)MAXIM_DEVICE_TYPE_MAX14577, .data = (void *)MAXIM_DEVICE_TYPE_MAX14577,
@ -292,7 +292,7 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
struct device_node *np = i2c->dev.of_node; struct device_node *np = i2c->dev.of_node;
int ret = 0; int ret = 0;
const struct regmap_irq_chip *irq_chip; const struct regmap_irq_chip *irq_chip;
struct mfd_cell *mfd_devs; const struct mfd_cell *mfd_devs;
unsigned int mfd_devs_size; unsigned int mfd_devs_size;
int irq_flags; int irq_flags;
@ -331,7 +331,8 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
of_id = of_match_device(max14577_dt_match, &i2c->dev); of_id = of_match_device(max14577_dt_match, &i2c->dev);
if (of_id) if (of_id)
max14577->dev_type = (unsigned int)of_id->data; max14577->dev_type =
(enum maxim_device_type)of_id->data;
} else { } else {
max14577->dev_type = id->driver_data; max14577->dev_type = id->driver_data;
} }
@ -414,20 +415,18 @@ static int max14577_suspend(struct device *dev)
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct max14577 *max14577 = i2c_get_clientdata(i2c); struct max14577 *max14577 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev)) { if (device_may_wakeup(dev))
enable_irq_wake(max14577->irq); enable_irq_wake(max14577->irq);
/* /*
* MUIC IRQ must be disabled during suspend if this is * MUIC IRQ must be disabled during suspend because if it happens
* a wake up source because it will be handled before * while suspended it will be handled before resuming I2C.
* resuming I2C. *
* * When device is woken up from suspend (e.g. by ADC change),
* When device is woken up from suspend (e.g. by ADC change), * an interrupt occurs before resuming I2C bus controller.
* an interrupt occurs before resuming I2C bus controller. * Interrupt handler tries to read registers but this read
* Interrupt handler tries to read registers but this read * will fail because I2C is still suspended.
* will fail because I2C is still suspended. */
*/ disable_irq(max14577->irq);
disable_irq(max14577->irq);
}
return 0; return 0;
} }
@ -437,10 +436,9 @@ static int max14577_resume(struct device *dev)
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct max14577 *max14577 = i2c_get_clientdata(i2c); struct max14577 *max14577 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev)) { if (device_may_wakeup(dev))
disable_irq_wake(max14577->irq); disable_irq_wake(max14577->irq);
enable_irq(max14577->irq); enable_irq(max14577->irq);
}
return 0; return 0;
} }

View File

@ -47,7 +47,7 @@ static struct regmap_config max77686_regmap_config = {
}; };
#ifdef CONFIG_OF #ifdef CONFIG_OF
static struct of_device_id max77686_pmic_dt_match[] = { static const struct of_device_id max77686_pmic_dt_match[] = {
{.compatible = "maxim,max77686", .data = NULL}, {.compatible = "maxim,max77686", .data = NULL},
{}, {},
}; };

View File

@ -243,7 +243,7 @@ static const struct dev_pm_ops max77693_pm = {
}; };
#ifdef CONFIG_OF #ifdef CONFIG_OF
static struct of_device_id max77693_dt_match[] = { static const struct of_device_id max77693_dt_match[] = {
{ .compatible = "maxim,max77693" }, { .compatible = "maxim,max77693" },
{}, {},
}; };

View File

@ -305,7 +305,7 @@ static int max8907_i2c_remove(struct i2c_client *i2c)
} }
#ifdef CONFIG_OF #ifdef CONFIG_OF
static struct of_device_id max8907_of_match[] = { static const struct of_device_id max8907_of_match[] = {
{ .compatible = "maxim,max8907" }, { .compatible = "maxim,max8907" },
{ }, { },
}; };

View File

@ -51,7 +51,7 @@ static const struct mfd_cell max8997_devs[] = {
}; };
#ifdef CONFIG_OF #ifdef CONFIG_OF
static struct of_device_id max8997_pmic_dt_match[] = { static const struct of_device_id max8997_pmic_dt_match[] = {
{ .compatible = "maxim,max8997-pmic", .data = (void *)TYPE_MAX8997 }, { .compatible = "maxim,max8997-pmic", .data = (void *)TYPE_MAX8997 },
{}, {},
}; };

View File

@ -132,7 +132,7 @@ int max8998_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
EXPORT_SYMBOL(max8998_update_reg); EXPORT_SYMBOL(max8998_update_reg);
#ifdef CONFIG_OF #ifdef CONFIG_OF
static struct of_device_id max8998_dt_match[] = { static const struct of_device_id max8998_dt_match[] = {
{ .compatible = "maxim,max8998", .data = (void *)TYPE_MAX8998 }, { .compatible = "maxim,max8998", .data = (void *)TYPE_MAX8998 },
{ .compatible = "national,lp3974", .data = (void *)TYPE_LP3974 }, { .compatible = "national,lp3974", .data = (void *)TYPE_LP3974 },
{ .compatible = "ti,lp3974", .data = (void *)TYPE_LP3974 }, { .compatible = "ti,lp3974", .data = (void *)TYPE_LP3974 },

View File

@ -660,34 +660,22 @@ int mc13xxx_common_init(struct device *dev)
if (ret) if (ret)
return ret; return ret;
mutex_init(&mc13xxx->lock);
ret = request_threaded_irq(mc13xxx->irq, NULL, mc13xxx_irq_thread, ret = request_threaded_irq(mc13xxx->irq, NULL, mc13xxx_irq_thread,
IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx); IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx);
if (ret) if (ret)
return ret; return ret;
mutex_init(&mc13xxx->lock);
if (mc13xxx_probe_flags_dt(mc13xxx) < 0 && pdata) if (mc13xxx_probe_flags_dt(mc13xxx) < 0 && pdata)
mc13xxx->flags = pdata->flags; mc13xxx->flags = pdata->flags;
if (mc13xxx->flags & MC13XXX_USE_ADC) if (mc13xxx->flags & MC13XXX_USE_ADC)
mc13xxx_add_subdevice(mc13xxx, "%s-adc"); mc13xxx_add_subdevice(mc13xxx, "%s-adc");
if (mc13xxx->flags & MC13XXX_USE_CODEC) {
if (pdata)
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec",
pdata->codec, sizeof(*pdata->codec));
else
mc13xxx_add_subdevice(mc13xxx, "%s-codec");
}
if (mc13xxx->flags & MC13XXX_USE_RTC) if (mc13xxx->flags & MC13XXX_USE_RTC)
mc13xxx_add_subdevice(mc13xxx, "%s-rtc"); mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN)
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-ts",
&pdata->touch, sizeof(pdata->touch));
if (pdata) { if (pdata) {
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator", mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
&pdata->regulators, sizeof(pdata->regulators)); &pdata->regulators, sizeof(pdata->regulators));
@ -695,10 +683,20 @@ int mc13xxx_common_init(struct device *dev)
pdata->leds, sizeof(*pdata->leds)); pdata->leds, sizeof(*pdata->leds));
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-pwrbutton", mc13xxx_add_subdevice_pdata(mc13xxx, "%s-pwrbutton",
pdata->buttons, sizeof(*pdata->buttons)); pdata->buttons, sizeof(*pdata->buttons));
if (mc13xxx->flags & MC13XXX_USE_CODEC)
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec",
pdata->codec, sizeof(*pdata->codec));
if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN)
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-ts",
&pdata->touch, sizeof(pdata->touch));
} else { } else {
mc13xxx_add_subdevice(mc13xxx, "%s-regulator"); mc13xxx_add_subdevice(mc13xxx, "%s-regulator");
mc13xxx_add_subdevice(mc13xxx, "%s-led"); mc13xxx_add_subdevice(mc13xxx, "%s-led");
mc13xxx_add_subdevice(mc13xxx, "%s-pwrbutton"); mc13xxx_add_subdevice(mc13xxx, "%s-pwrbutton");
if (mc13xxx->flags & MC13XXX_USE_CODEC)
mc13xxx_add_subdevice(mc13xxx, "%s-codec");
if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN)
mc13xxx_add_subdevice(mc13xxx, "%s-ts");
} }
return 0; return 0;

View File

@ -1287,29 +1287,8 @@ static struct i2c_driver menelaus_i2c_driver = {
.id_table = menelaus_id, .id_table = menelaus_id,
}; };
static int __init menelaus_init(void) module_i2c_driver(menelaus_i2c_driver);
{
int res;
res = i2c_add_driver(&menelaus_i2c_driver);
if (res < 0) {
pr_err(DRIVER_NAME ": driver registration failed\n");
return res;
}
return 0;
}
static void __exit menelaus_exit(void)
{
i2c_del_driver(&menelaus_i2c_driver);
/* FIXME: Shutdown menelaus parts that can be shut down */
}
MODULE_AUTHOR("Texas Instruments, Inc. (and others)"); MODULE_AUTHOR("Texas Instruments, Inc. (and others)");
MODULE_DESCRIPTION("I2C interface for Menelaus."); MODULE_DESCRIPTION("I2C interface for Menelaus.");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
module_init(menelaus_init);
module_exit(menelaus_exit);

View File

@ -102,7 +102,7 @@ static int mfd_add_device(struct device *parent, int id,
pdev->dev.dma_mask = parent->dma_mask; pdev->dev.dma_mask = parent->dma_mask;
pdev->dev.dma_parms = parent->dma_parms; pdev->dev.dma_parms = parent->dma_parms;
ret = devm_regulator_bulk_register_supply_alias( ret = regulator_bulk_register_supply_alias(
&pdev->dev, cell->parent_supplies, &pdev->dev, cell->parent_supplies,
parent, cell->parent_supplies, parent, cell->parent_supplies,
cell->num_parent_supplies); cell->num_parent_supplies);
@ -182,9 +182,9 @@ static int mfd_add_device(struct device *parent, int id,
return 0; return 0;
fail_alias: fail_alias:
devm_regulator_bulk_unregister_supply_alias(&pdev->dev, regulator_bulk_unregister_supply_alias(&pdev->dev,
cell->parent_supplies, cell->parent_supplies,
cell->num_parent_supplies); cell->num_parent_supplies);
fail_res: fail_res:
kfree(res); kfree(res);
fail_device: fail_device:
@ -238,6 +238,9 @@ static int mfd_remove_devices_fn(struct device *dev, void *c)
pdev = to_platform_device(dev); pdev = to_platform_device(dev);
cell = mfd_get_cell(pdev); cell = mfd_get_cell(pdev);
regulator_bulk_unregister_supply_alias(dev, cell->parent_supplies,
cell->num_parent_supplies);
/* find the base address of usage_count pointers (for freeing) */ /* find the base address of usage_count pointers (for freeing) */
if (!*usage_count || (cell->usage_count < *usage_count)) if (!*usage_count || (cell->usage_count < *usage_count))
*usage_count = cell->usage_count; *usage_count = cell->usage_count;

View File

@ -557,7 +557,7 @@ static int usbhs_omap_get_dt_pdata(struct device *dev,
return 0; return 0;
} }
static struct of_device_id usbhs_child_match_table[] = { static const struct of_device_id usbhs_child_match_table[] = {
{ .compatible = "ti,omap-ehci", }, { .compatible = "ti,omap-ehci", },
{ .compatible = "ti,omap-ohci", }, { .compatible = "ti,omap-ohci", },
{ } { }

View File

@ -26,7 +26,6 @@
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/mfd/core.h> #include <linux/mfd/core.h>
#include <linux/mfd/pm8xxx/core.h>
#define SSBI_REG_ADDR_IRQ_BASE 0x1BB #define SSBI_REG_ADDR_IRQ_BASE 0x1BB
@ -57,7 +56,6 @@
#define PM8921_NR_IRQS 256 #define PM8921_NR_IRQS 256
struct pm_irq_chip { struct pm_irq_chip {
struct device *dev;
struct regmap *regmap; struct regmap *regmap;
spinlock_t pm_irq_lock; spinlock_t pm_irq_lock;
struct irq_domain *irqdomain; struct irq_domain *irqdomain;
@ -67,11 +65,6 @@ struct pm_irq_chip {
u8 config[0]; u8 config[0];
}; };
struct pm8921 {
struct device *dev;
struct pm_irq_chip *irq_chip;
};
static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp, static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp,
unsigned int *ip) unsigned int *ip)
{ {
@ -255,55 +248,6 @@ static struct irq_chip pm8xxx_irq_chip = {
.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
}; };
/**
* pm8xxx_get_irq_stat - get the status of the irq line
* @chip: pointer to identify a pmic irq controller
* @irq: the irq number
*
* The pm8xxx gpio and mpp rely on the interrupt block to read
* the values on their pins. This function is to facilitate reading
* the status of a gpio or an mpp line. The caller has to convert the
* gpio number to irq number.
*
* RETURNS:
* an int indicating the value read on that line
*/
static int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
{
int pmirq, rc;
unsigned int block, bits, bit;
unsigned long flags;
struct irq_data *irq_data = irq_get_irq_data(irq);
pmirq = irq_data->hwirq;
block = pmirq / 8;
bit = pmirq % 8;
spin_lock_irqsave(&chip->pm_irq_lock, flags);
rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
if (rc) {
pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
irq, pmirq, block, rc);
goto bail_out;
}
rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
if (rc) {
pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
irq, pmirq, block, rc);
goto bail_out;
}
rc = (bits & (1 << bit)) ? 1 : 0;
bail_out:
spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
return rc;
}
static int pm8xxx_irq_domain_map(struct irq_domain *d, unsigned int irq, static int pm8xxx_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hwirq) irq_hw_number_t hwirq)
{ {
@ -324,56 +268,6 @@ static const struct irq_domain_ops pm8xxx_irq_domain_ops = {
.map = pm8xxx_irq_domain_map, .map = pm8xxx_irq_domain_map,
}; };
static int pm8921_readb(const struct device *dev, u16 addr, u8 *val)
{
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
return ssbi_read(pmic->dev->parent, addr, val, 1);
}
static int pm8921_writeb(const struct device *dev, u16 addr, u8 val)
{
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
return ssbi_write(pmic->dev->parent, addr, &val, 1);
}
static int pm8921_read_buf(const struct device *dev, u16 addr, u8 *buf,
int cnt)
{
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
return ssbi_read(pmic->dev->parent, addr, buf, cnt);
}
static int pm8921_write_buf(const struct device *dev, u16 addr, u8 *buf,
int cnt)
{
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
return ssbi_write(pmic->dev->parent, addr, buf, cnt);
}
static int pm8921_read_irq_stat(const struct device *dev, int irq)
{
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
}
static struct pm8xxx_drvdata pm8921_drvdata = {
.pmic_readb = pm8921_readb,
.pmic_writeb = pm8921_writeb,
.pmic_read_buf = pm8921_read_buf,
.pmic_write_buf = pm8921_write_buf,
.pmic_read_irq_stat = pm8921_read_irq_stat,
};
static const struct regmap_config ssbi_regmap_config = { static const struct regmap_config ssbi_regmap_config = {
.reg_bits = 16, .reg_bits = 16,
.val_bits = 8, .val_bits = 8,
@ -392,7 +286,6 @@ MODULE_DEVICE_TABLE(of, pm8921_id_table);
static int pm8921_probe(struct platform_device *pdev) static int pm8921_probe(struct platform_device *pdev)
{ {
struct pm8921 *pmic;
struct regmap *regmap; struct regmap *regmap;
int irq, rc; int irq, rc;
unsigned int val; unsigned int val;
@ -404,12 +297,6 @@ static int pm8921_probe(struct platform_device *pdev)
if (irq < 0) if (irq < 0)
return irq; return irq;
pmic = devm_kzalloc(&pdev->dev, sizeof(struct pm8921), GFP_KERNEL);
if (!pmic) {
pr_err("Cannot alloc pm8921 struct\n");
return -ENOMEM;
}
regmap = devm_regmap_init(&pdev->dev, NULL, pdev->dev.parent, regmap = devm_regmap_init(&pdev->dev, NULL, pdev->dev.parent,
&ssbi_regmap_config); &ssbi_regmap_config);
if (IS_ERR(regmap)) if (IS_ERR(regmap))
@ -434,18 +321,13 @@ static int pm8921_probe(struct platform_device *pdev)
pr_info("PMIC revision 2: %02X\n", val); pr_info("PMIC revision 2: %02X\n", val);
rev |= val << BITS_PER_BYTE; rev |= val << BITS_PER_BYTE;
pmic->dev = &pdev->dev;
pm8921_drvdata.pm_chip_data = pmic;
platform_set_drvdata(pdev, &pm8921_drvdata);
chip = devm_kzalloc(&pdev->dev, sizeof(*chip) + chip = devm_kzalloc(&pdev->dev, sizeof(*chip) +
sizeof(chip->config[0]) * nirqs, sizeof(chip->config[0]) * nirqs,
GFP_KERNEL); GFP_KERNEL);
if (!chip) if (!chip)
return -ENOMEM; return -ENOMEM;
pmic->irq_chip = chip; platform_set_drvdata(pdev, chip);
chip->dev = &pdev->dev;
chip->regmap = regmap; chip->regmap = regmap;
chip->num_irqs = nirqs; chip->num_irqs = nirqs;
chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8); chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
@ -481,8 +363,7 @@ static int pm8921_remove_child(struct device *dev, void *unused)
static int pm8921_remove(struct platform_device *pdev) static int pm8921_remove(struct platform_device *pdev)
{ {
int irq = platform_get_irq(pdev, 0); int irq = platform_get_irq(pdev, 0);
struct pm8921 *pmic = pm8921_drvdata.pm_chip_data; struct pm_irq_chip *chip = platform_get_drvdata(pdev);
struct pm_irq_chip *chip = pmic->irq_chip;
device_for_each_child(&pdev->dev, NULL, pm8921_remove_child); device_for_each_child(&pdev->dev, NULL, pm8921_remove_child);
irq_set_chained_handler(irq, NULL); irq_set_chained_handler(irq, NULL);

View File

@ -38,7 +38,7 @@ static struct resource rdc321x_wdt_resource[] = {
}; };
static struct rdc321x_gpio_pdata rdc321x_gpio_pdata = { static struct rdc321x_gpio_pdata rdc321x_gpio_pdata = {
.max_gpios = RDC321X_MAX_GPIO, .max_gpios = RDC321X_NUM_GPIO,
}; };
static struct resource rdc321x_gpio_resources[] = { static struct resource rdc321x_gpio_resources[] = {

View File

@ -29,7 +29,7 @@ static int polling_pipe = 1;
module_param(polling_pipe, int, S_IRUGO | S_IWUSR); module_param(polling_pipe, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(polling_pipe, "polling pipe (0: ctl, 1: bulk)"); MODULE_PARM_DESC(polling_pipe, "polling pipe (0: ctl, 1: bulk)");
static struct mfd_cell rtsx_usb_cells[] = { static const struct mfd_cell rtsx_usb_cells[] = {
[RTSX_USB_SD_CARD] = { [RTSX_USB_SD_CARD] = {
.name = "rtsx_usb_sdmmc", .name = "rtsx_usb_sdmmc",
.pdata_size = 0, .pdata_size = 0,
@ -67,7 +67,7 @@ static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr,
ucr->sg_timer.expires = jiffies + msecs_to_jiffies(timeout); ucr->sg_timer.expires = jiffies + msecs_to_jiffies(timeout);
add_timer(&ucr->sg_timer); add_timer(&ucr->sg_timer);
usb_sg_wait(&ucr->current_sg); usb_sg_wait(&ucr->current_sg);
del_timer(&ucr->sg_timer); del_timer_sync(&ucr->sg_timer);
if (act_len) if (act_len)
*act_len = ucr->current_sg.bytes; *act_len = ucr->current_sg.bytes;
@ -644,14 +644,14 @@ static int rtsx_usb_probe(struct usb_interface *intf,
if (ret) if (ret)
goto out_init_fail; goto out_init_fail;
/* initialize USB SG transfer timer */
setup_timer(&ucr->sg_timer, rtsx_usb_sg_timed_out, (unsigned long) ucr);
ret = mfd_add_devices(&intf->dev, usb_dev->devnum, rtsx_usb_cells, ret = mfd_add_devices(&intf->dev, usb_dev->devnum, rtsx_usb_cells,
ARRAY_SIZE(rtsx_usb_cells), NULL, 0, NULL); ARRAY_SIZE(rtsx_usb_cells), NULL, 0, NULL);
if (ret) if (ret)
goto out_init_fail; goto out_init_fail;
/* initialize USB SG transfer timer */
init_timer(&ucr->sg_timer);
setup_timer(&ucr->sg_timer, rtsx_usb_sg_timed_out, (unsigned long) ucr);
#ifdef CONFIG_PM #ifdef CONFIG_PM
intf->needs_remote_wakeup = 1; intf->needs_remote_wakeup = 1;
usb_enable_autosuspend(usb_dev); usb_enable_autosuspend(usb_dev);
@ -687,9 +687,15 @@ static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message)
dev_dbg(&intf->dev, "%s called with pm message 0x%04u\n", dev_dbg(&intf->dev, "%s called with pm message 0x%04u\n",
__func__, message.event); __func__, message.event);
/*
* Call to make sure LED is off during suspend to save more power.
* It is NOT a permanent state and could be turned on anytime later.
* Thus no need to call turn_on when resunming.
*/
mutex_lock(&ucr->dev_mutex); mutex_lock(&ucr->dev_mutex);
rtsx_usb_turn_off_led(ucr); rtsx_usb_turn_off_led(ucr);
mutex_unlock(&ucr->dev_mutex); mutex_unlock(&ucr->dev_mutex);
return 0; return 0;
} }

View File

@ -25,7 +25,6 @@
#include <linux/mfd/core.h> #include <linux/mfd/core.h>
#include <linux/mfd/samsung/core.h> #include <linux/mfd/samsung/core.h>
#include <linux/mfd/samsung/irq.h> #include <linux/mfd/samsung/irq.h>
#include <linux/mfd/samsung/rtc.h>
#include <linux/mfd/samsung/s2mpa01.h> #include <linux/mfd/samsung/s2mpa01.h>
#include <linux/mfd/samsung/s2mps11.h> #include <linux/mfd/samsung/s2mps11.h>
#include <linux/mfd/samsung/s2mps14.h> #include <linux/mfd/samsung/s2mps14.h>
@ -91,7 +90,7 @@ static const struct mfd_cell s2mpa01_devs[] = {
}; };
#ifdef CONFIG_OF #ifdef CONFIG_OF
static struct of_device_id sec_dt_match[] = { static const struct of_device_id sec_dt_match[] = {
{ .compatible = "samsung,s5m8767-pmic", { .compatible = "samsung,s5m8767-pmic",
.data = (void *)S5M8767X, .data = (void *)S5M8767X,
}, { }, {
@ -196,20 +195,6 @@ static const struct regmap_config s5m8767_regmap_config = {
.cache_type = REGCACHE_FLAT, .cache_type = REGCACHE_FLAT,
}; };
static const struct regmap_config s5m_rtc_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = SEC_RTC_REG_MAX,
};
static const struct regmap_config s2mps14_rtc_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = S2MPS_RTC_REG_MAX,
};
#ifdef CONFIG_OF #ifdef CONFIG_OF
/* /*
* Only the common platform data elements for s5m8767 are parsed here from the * Only the common platform data elements for s5m8767 are parsed here from the
@ -264,8 +249,9 @@ static int sec_pmic_probe(struct i2c_client *i2c,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct sec_platform_data *pdata = dev_get_platdata(&i2c->dev); struct sec_platform_data *pdata = dev_get_platdata(&i2c->dev);
const struct regmap_config *regmap, *regmap_rtc; const struct regmap_config *regmap;
struct sec_pmic_dev *sec_pmic; struct sec_pmic_dev *sec_pmic;
unsigned long device_type;
int ret; int ret;
sec_pmic = devm_kzalloc(&i2c->dev, sizeof(struct sec_pmic_dev), sec_pmic = devm_kzalloc(&i2c->dev, sizeof(struct sec_pmic_dev),
@ -277,7 +263,7 @@ static int sec_pmic_probe(struct i2c_client *i2c,
sec_pmic->dev = &i2c->dev; sec_pmic->dev = &i2c->dev;
sec_pmic->i2c = i2c; sec_pmic->i2c = i2c;
sec_pmic->irq = i2c->irq; sec_pmic->irq = i2c->irq;
sec_pmic->type = sec_i2c_get_driver_data(i2c, id); device_type = sec_i2c_get_driver_data(i2c, id);
if (sec_pmic->dev->of_node) { if (sec_pmic->dev->of_node) {
pdata = sec_pmic_i2c_parse_dt_pdata(sec_pmic->dev); pdata = sec_pmic_i2c_parse_dt_pdata(sec_pmic->dev);
@ -285,7 +271,7 @@ static int sec_pmic_probe(struct i2c_client *i2c,
ret = PTR_ERR(pdata); ret = PTR_ERR(pdata);
return ret; return ret;
} }
pdata->device_type = sec_pmic->type; pdata->device_type = device_type;
} }
if (pdata) { if (pdata) {
sec_pmic->device_type = pdata->device_type; sec_pmic->device_type = pdata->device_type;
@ -298,39 +284,21 @@ static int sec_pmic_probe(struct i2c_client *i2c,
switch (sec_pmic->device_type) { switch (sec_pmic->device_type) {
case S2MPA01: case S2MPA01:
regmap = &s2mpa01_regmap_config; regmap = &s2mpa01_regmap_config;
/*
* The rtc-s5m driver does not support S2MPA01 and there
* is no mfd_cell for S2MPA01 RTC device.
* However we must pass something to devm_regmap_init_i2c()
* so use S5M-like regmap config even though it wouldn't work.
*/
regmap_rtc = &s5m_rtc_regmap_config;
break; break;
case S2MPS11X: case S2MPS11X:
regmap = &s2mps11_regmap_config; regmap = &s2mps11_regmap_config;
/*
* The rtc-s5m driver does not support S2MPS11 and there
* is no mfd_cell for S2MPS11 RTC device.
* However we must pass something to devm_regmap_init_i2c()
* so use S5M-like regmap config even though it wouldn't work.
*/
regmap_rtc = &s5m_rtc_regmap_config;
break; break;
case S2MPS14X: case S2MPS14X:
regmap = &s2mps14_regmap_config; regmap = &s2mps14_regmap_config;
regmap_rtc = &s2mps14_rtc_regmap_config;
break; break;
case S5M8763X: case S5M8763X:
regmap = &s5m8763_regmap_config; regmap = &s5m8763_regmap_config;
regmap_rtc = &s5m_rtc_regmap_config;
break; break;
case S5M8767X: case S5M8767X:
regmap = &s5m8767_regmap_config; regmap = &s5m8767_regmap_config;
regmap_rtc = &s5m_rtc_regmap_config;
break; break;
default: default:
regmap = &sec_regmap_config; regmap = &sec_regmap_config;
regmap_rtc = &s5m_rtc_regmap_config;
break; break;
} }
@ -342,21 +310,6 @@ static int sec_pmic_probe(struct i2c_client *i2c,
return ret; return ret;
} }
sec_pmic->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
if (!sec_pmic->rtc) {
dev_err(&i2c->dev, "Failed to allocate I2C for RTC\n");
return -ENODEV;
}
i2c_set_clientdata(sec_pmic->rtc, sec_pmic);
sec_pmic->regmap_rtc = devm_regmap_init_i2c(sec_pmic->rtc, regmap_rtc);
if (IS_ERR(sec_pmic->regmap_rtc)) {
ret = PTR_ERR(sec_pmic->regmap_rtc);
dev_err(&i2c->dev, "Failed to allocate RTC register map: %d\n",
ret);
goto err_regmap_rtc;
}
if (pdata && pdata->cfg_pmic_irq) if (pdata && pdata->cfg_pmic_irq)
pdata->cfg_pmic_irq(); pdata->cfg_pmic_irq();
@ -403,8 +356,6 @@ static int sec_pmic_probe(struct i2c_client *i2c,
err_mfd: err_mfd:
sec_irq_exit(sec_pmic); sec_irq_exit(sec_pmic);
err_regmap_rtc:
i2c_unregister_device(sec_pmic->rtc);
return ret; return ret;
} }
@ -414,7 +365,6 @@ static int sec_pmic_remove(struct i2c_client *i2c)
mfd_remove_devices(sec_pmic->dev); mfd_remove_devices(sec_pmic->dev);
sec_irq_exit(sec_pmic); sec_irq_exit(sec_pmic);
i2c_unregister_device(sec_pmic->rtc);
return 0; return 0;
} }
@ -424,19 +374,18 @@ static int sec_pmic_suspend(struct device *dev)
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c); struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev)) { if (device_may_wakeup(dev))
enable_irq_wake(sec_pmic->irq); enable_irq_wake(sec_pmic->irq);
/* /*
* PMIC IRQ must be disabled during suspend for RTC alarm * PMIC IRQ must be disabled during suspend for RTC alarm
* to work properly. * to work properly.
* When device is woken up from suspend by RTC Alarm, an * When device is woken up from suspend, an
* interrupt occurs before resuming I2C bus controller. * interrupt occurs before resuming I2C bus controller.
* The interrupt is handled by regmap_irq_thread which tries * The interrupt is handled by regmap_irq_thread which tries
* to read RTC registers. This read fails (I2C is still * to read RTC registers. This read fails (I2C is still
* suspended) and RTC Alarm interrupt is disabled. * suspended) and RTC Alarm interrupt is disabled.
*/ */
disable_irq(sec_pmic->irq); disable_irq(sec_pmic->irq);
}
return 0; return 0;
} }
@ -446,10 +395,9 @@ static int sec_pmic_resume(struct device *dev)
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c); struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev)) { if (device_may_wakeup(dev))
disable_irq_wake(sec_pmic->irq); disable_irq_wake(sec_pmic->irq);
enable_irq(sec_pmic->irq); enable_irq(sec_pmic->irq);
}
return 0; return 0;
} }

View File

@ -385,7 +385,7 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic)
&sec_pmic->irq_data); &sec_pmic->irq_data);
break; break;
default: default:
dev_err(sec_pmic->dev, "Unknown device type %d\n", dev_err(sec_pmic->dev, "Unknown device type %lu\n",
sec_pmic->device_type); sec_pmic->device_type);
return -EINVAL; return -EINVAL;
} }

View File

@ -1726,7 +1726,7 @@ static struct pci_driver sm501_pci_driver = {
MODULE_ALIAS("platform:sm501"); MODULE_ALIAS("platform:sm501");
static struct of_device_id of_sm501_match_tbl[] = { static const struct of_device_id of_sm501_match_tbl[] = {
{ .compatible = "smi,sm501", }, { .compatible = "smi,sm501", },
{ /* end */ } { /* end */ }
}; };

View File

@ -14,6 +14,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/of_device.h>
#include "stmpe.h" #include "stmpe.h"
static int i2c_reg_read(struct stmpe *stmpe, u8 reg) static int i2c_reg_read(struct stmpe *stmpe, u8 reg)
@ -52,15 +53,41 @@ static struct stmpe_client_info i2c_ci = {
.write_block = i2c_block_write, .write_block = i2c_block_write,
}; };
static const struct of_device_id stmpe_of_match[] = {
{ .compatible = "st,stmpe610", .data = (void *)STMPE610, },
{ .compatible = "st,stmpe801", .data = (void *)STMPE801, },
{ .compatible = "st,stmpe811", .data = (void *)STMPE811, },
{ .compatible = "st,stmpe1601", .data = (void *)STMPE1601, },
{ .compatible = "st,stmpe1801", .data = (void *)STMPE1801, },
{ .compatible = "st,stmpe2401", .data = (void *)STMPE2401, },
{ .compatible = "st,stmpe2403", .data = (void *)STMPE2403, },
{},
};
MODULE_DEVICE_TABLE(of, stmpe_of_match);
static int static int
stmpe_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) stmpe_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
{ {
int partnum;
const struct of_device_id *of_id;
i2c_ci.data = (void *)id; i2c_ci.data = (void *)id;
i2c_ci.irq = i2c->irq; i2c_ci.irq = i2c->irq;
i2c_ci.client = i2c; i2c_ci.client = i2c;
i2c_ci.dev = &i2c->dev; i2c_ci.dev = &i2c->dev;
return stmpe_probe(&i2c_ci, id->driver_data); of_id = of_match_device(stmpe_of_match, &i2c->dev);
if (!of_id) {
/*
* This happens when the I2C ID matches the node name
* but no real compatible string has been given.
*/
dev_info(&i2c->dev, "matching on node name, compatible is preferred\n");
partnum = id->driver_data;
} else
partnum = (int)of_id->data;
return stmpe_probe(&i2c_ci, partnum);
} }
static int stmpe_i2c_remove(struct i2c_client *i2c) static int stmpe_i2c_remove(struct i2c_client *i2c)
@ -89,6 +116,7 @@ static struct i2c_driver stmpe_i2c_driver = {
#ifdef CONFIG_PM #ifdef CONFIG_PM
.pm = &stmpe_dev_pm_ops, .pm = &stmpe_dev_pm_ops,
#endif #endif
.of_match_table = stmpe_of_match,
}, },
.probe = stmpe_i2c_probe, .probe = stmpe_i2c_probe,
.remove = stmpe_i2c_remove, .remove = stmpe_i2c_remove,

View File

@ -20,6 +20,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mfd/core.h> #include <linux/mfd/core.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include "stmpe.h" #include "stmpe.h"
static int __stmpe_enable(struct stmpe *stmpe, unsigned int blocks) static int __stmpe_enable(struct stmpe *stmpe, unsigned int blocks)
@ -605,9 +606,18 @@ static int stmpe1601_enable(struct stmpe *stmpe, unsigned int blocks,
if (blocks & STMPE_BLOCK_GPIO) if (blocks & STMPE_BLOCK_GPIO)
mask |= STMPE1601_SYS_CTRL_ENABLE_GPIO; mask |= STMPE1601_SYS_CTRL_ENABLE_GPIO;
else
mask &= ~STMPE1601_SYS_CTRL_ENABLE_GPIO;
if (blocks & STMPE_BLOCK_KEYPAD) if (blocks & STMPE_BLOCK_KEYPAD)
mask |= STMPE1601_SYS_CTRL_ENABLE_KPC; mask |= STMPE1601_SYS_CTRL_ENABLE_KPC;
else
mask &= ~STMPE1601_SYS_CTRL_ENABLE_KPC;
if (blocks & STMPE_BLOCK_PWM)
mask |= STMPE1601_SYS_CTRL_ENABLE_SPWM;
else
mask &= ~STMPE1601_SYS_CTRL_ENABLE_SPWM;
return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL, mask, return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL, mask,
enable ? mask : 0); enable ? mask : 0);
@ -986,9 +996,6 @@ static int stmpe_irq_init(struct stmpe *stmpe, struct device_node *np)
int base = 0; int base = 0;
int num_irqs = stmpe->variant->num_irqs; int num_irqs = stmpe->variant->num_irqs;
if (!np)
base = stmpe->irq_base;
stmpe->domain = irq_domain_add_simple(np, num_irqs, base, stmpe->domain = irq_domain_add_simple(np, num_irqs, base,
&stmpe_irq_ops, stmpe); &stmpe_irq_ops, stmpe);
if (!stmpe->domain) { if (!stmpe->domain) {
@ -1067,7 +1074,7 @@ static int stmpe_chip_init(struct stmpe *stmpe)
static int stmpe_add_device(struct stmpe *stmpe, const struct mfd_cell *cell) static int stmpe_add_device(struct stmpe *stmpe, const struct mfd_cell *cell)
{ {
return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1, return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1,
NULL, stmpe->irq_base, stmpe->domain); NULL, 0, stmpe->domain);
} }
static int stmpe_devices_init(struct stmpe *stmpe) static int stmpe_devices_init(struct stmpe *stmpe)
@ -1171,12 +1178,23 @@ int stmpe_probe(struct stmpe_client_info *ci, int partnum)
stmpe->dev = ci->dev; stmpe->dev = ci->dev;
stmpe->client = ci->client; stmpe->client = ci->client;
stmpe->pdata = pdata; stmpe->pdata = pdata;
stmpe->irq_base = pdata->irq_base;
stmpe->ci = ci; stmpe->ci = ci;
stmpe->partnum = partnum; stmpe->partnum = partnum;
stmpe->variant = stmpe_variant_info[partnum]; stmpe->variant = stmpe_variant_info[partnum];
stmpe->regs = stmpe->variant->regs; stmpe->regs = stmpe->variant->regs;
stmpe->num_gpios = stmpe->variant->num_gpios; stmpe->num_gpios = stmpe->variant->num_gpios;
stmpe->vcc = devm_regulator_get_optional(ci->dev, "vcc");
if (!IS_ERR(stmpe->vcc)) {
ret = regulator_enable(stmpe->vcc);
if (ret)
dev_warn(ci->dev, "failed to enable VCC supply\n");
}
stmpe->vio = devm_regulator_get_optional(ci->dev, "vio");
if (!IS_ERR(stmpe->vio)) {
ret = regulator_enable(stmpe->vio);
if (ret)
dev_warn(ci->dev, "failed to enable VIO supply\n");
}
dev_set_drvdata(stmpe->dev, stmpe); dev_set_drvdata(stmpe->dev, stmpe);
if (ci->init) if (ci->init)
@ -1243,6 +1261,11 @@ int stmpe_probe(struct stmpe_client_info *ci, int partnum)
int stmpe_remove(struct stmpe *stmpe) int stmpe_remove(struct stmpe *stmpe)
{ {
if (!IS_ERR(stmpe->vio))
regulator_disable(stmpe->vio);
if (!IS_ERR(stmpe->vcc))
regulator_disable(stmpe->vcc);
mfd_remove_devices(stmpe->dev); mfd_remove_devices(stmpe->dev);
return 0; return 0;

View File

@ -192,7 +192,7 @@ int stmpe_remove(struct stmpe *stmpe);
#define STMPE1601_SYS_CTRL_ENABLE_GPIO (1 << 3) #define STMPE1601_SYS_CTRL_ENABLE_GPIO (1 << 3)
#define STMPE1601_SYS_CTRL_ENABLE_KPC (1 << 1) #define STMPE1601_SYS_CTRL_ENABLE_KPC (1 << 1)
#define STMPE1601_SYSCON_ENABLE_SPWM (1 << 0) #define STMPE1601_SYS_CTRL_ENABLE_SPWM (1 << 0)
/* The 1601/2403 share the same masks */ /* The 1601/2403 share the same masks */
#define STMPE1601_AUTOSLEEP_TIMEOUT_MASK (0x7) #define STMPE1601_AUTOSLEEP_TIMEOUT_MASK (0x7)

134
drivers/mfd/sun6i-prcm.c Normal file
View File

@ -0,0 +1,134 @@
/*
* Copyright (C) 2014 Free Electrons
*
* License Terms: GNU General Public License v2
* Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
*
* Allwinner PRCM (Power/Reset/Clock Management) driver
*
*/
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of.h>
struct prcm_data {
int nsubdevs;
const struct mfd_cell *subdevs;
};
static const struct resource sun6i_a31_ar100_clk_res[] = {
{
.start = 0x0,
.end = 0x3,
.flags = IORESOURCE_MEM,
},
};
static const struct resource sun6i_a31_apb0_clk_res[] = {
{
.start = 0xc,
.end = 0xf,
.flags = IORESOURCE_MEM,
},
};
static const struct resource sun6i_a31_apb0_gates_clk_res[] = {
{
.start = 0x28,
.end = 0x2b,
.flags = IORESOURCE_MEM,
},
};
static const struct resource sun6i_a31_apb0_rstc_res[] = {
{
.start = 0xb0,
.end = 0xb3,
.flags = IORESOURCE_MEM,
},
};
static const struct mfd_cell sun6i_a31_prcm_subdevs[] = {
{
.name = "sun6i-a31-ar100-clk",
.of_compatible = "allwinner,sun6i-a31-ar100-clk",
.num_resources = ARRAY_SIZE(sun6i_a31_ar100_clk_res),
.resources = sun6i_a31_ar100_clk_res,
},
{
.name = "sun6i-a31-apb0-clk",
.of_compatible = "allwinner,sun6i-a31-apb0-clk",
.num_resources = ARRAY_SIZE(sun6i_a31_apb0_clk_res),
.resources = sun6i_a31_apb0_clk_res,
},
{
.name = "sun6i-a31-apb0-gates-clk",
.of_compatible = "allwinner,sun6i-a31-apb0-gates-clk",
.num_resources = ARRAY_SIZE(sun6i_a31_apb0_gates_clk_res),
.resources = sun6i_a31_apb0_gates_clk_res,
},
{
.name = "sun6i-a31-apb0-clock-reset",
.of_compatible = "allwinner,sun6i-a31-clock-reset",
.num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res),
.resources = sun6i_a31_apb0_rstc_res,
},
};
static const struct prcm_data sun6i_a31_prcm_data = {
.nsubdevs = ARRAY_SIZE(sun6i_a31_prcm_subdevs),
.subdevs = sun6i_a31_prcm_subdevs,
};
static const struct of_device_id sun6i_prcm_dt_ids[] = {
{
.compatible = "allwinner,sun6i-a31-prcm",
.data = &sun6i_a31_prcm_data,
},
{ /* sentinel */ },
};
static int sun6i_prcm_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
const struct prcm_data *data;
struct resource *res;
int ret;
match = of_match_node(sun6i_prcm_dt_ids, np);
if (!match)
return -EINVAL;
data = match->data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "no prcm memory region provided\n");
return -ENOENT;
}
ret = mfd_add_devices(&pdev->dev, 0, data->subdevs, data->nsubdevs,
res, -1, NULL);
if (ret) {
dev_err(&pdev->dev, "failed to add subdevices\n");
return ret;
}
return 0;
}
static struct platform_driver sun6i_prcm_driver = {
.driver = {
.name = "sun6i-prcm",
.owner = THIS_MODULE,
.of_match_table = sun6i_prcm_dt_ids,
},
.probe = sun6i_prcm_probe,
};
module_platform_driver(sun6i_prcm_driver);
MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>");
MODULE_DESCRIPTION("Allwinner sun6i PRCM driver");
MODULE_LICENSE("GPL v2");

View File

@ -95,7 +95,11 @@ struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
struct device_node *syscon_np; struct device_node *syscon_np;
struct regmap *regmap; struct regmap *regmap;
syscon_np = of_parse_phandle(np, property, 0); if (property)
syscon_np = of_parse_phandle(np, property, 0);
else
syscon_np = np;
if (!syscon_np) if (!syscon_np)
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);

View File

@ -119,7 +119,7 @@ static const struct i2c_device_id tps6507x_i2c_id[] = {
MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id); MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id);
#ifdef CONFIG_OF #ifdef CONFIG_OF
static struct of_device_id tps6507x_of_match[] = { static const struct of_device_id tps6507x_of_match[] = {
{.compatible = "ti,tps6507x", }, {.compatible = "ti,tps6507x", },
{}, {},
}; };

View File

@ -197,6 +197,7 @@ static struct regmap_irq_chip tps65218_irq_chip = {
static const struct of_device_id of_tps65218_match_table[] = { static const struct of_device_id of_tps65218_match_table[] = {
{ .compatible = "ti,tps65218", }, { .compatible = "ti,tps65218", },
{}
}; };
static int tps65218_probe(struct i2c_client *client, static int tps65218_probe(struct i2c_client *client,

View File

@ -444,7 +444,7 @@ static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *clien
return pdata; return pdata;
} }
static struct of_device_id tps6586x_of_match[] = { static const struct of_device_id tps6586x_of_match[] = {
{ .compatible = "ti,tps6586x", }, { .compatible = "ti,tps6586x", },
{ }, { },
}; };

View File

@ -379,7 +379,7 @@ err_sleep_init:
} }
#ifdef CONFIG_OF #ifdef CONFIG_OF
static struct of_device_id tps65910_of_match[] = { static const struct of_device_id tps65910_of_match[] = {
{ .compatible = "ti,tps65910", .data = (void *)TPS65910}, { .compatible = "ti,tps65910", .data = (void *)TPS65910},
{ .compatible = "ti,tps65911", .data = (void *)TPS65911}, { .compatible = "ti,tps65911", .data = (void *)TPS65911},
{ }, { },

View File

@ -87,8 +87,13 @@ static struct reg_default twl6040_defaults[] = {
}; };
static struct reg_default twl6040_patch[] = { static struct reg_default twl6040_patch[] = {
/* Select I2C bus access to dual access registers */ /*
{ TWL6040_REG_ACCCTL, 0x09 }, * Select I2C bus access to dual access registers
* Interrupt register is cleared on read
* Select fast mode for i2c (400KHz)
*/
{ TWL6040_REG_ACCCTL,
TWL6040_I2CSEL | TWL6040_INTCLRMODE | TWL6040_I2CMODE(1) },
}; };
@ -286,6 +291,8 @@ int twl6040_power(struct twl6040 *twl6040, int on)
if (twl6040->power_count++) if (twl6040->power_count++)
goto out; goto out;
clk_prepare_enable(twl6040->clk32k);
/* Allow writes to the chip */ /* Allow writes to the chip */
regcache_cache_only(twl6040->regmap, false); regcache_cache_only(twl6040->regmap, false);
@ -341,6 +348,8 @@ int twl6040_power(struct twl6040 *twl6040, int on)
twl6040->sysclk = 0; twl6040->sysclk = 0;
twl6040->mclk = 0; twl6040->mclk = 0;
clk_disable_unprepare(twl6040->clk32k);
} }
out: out:
@ -432,12 +441,9 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
TWL6040_HPLLENA; TWL6040_HPLLENA;
break; break;
case 19200000: case 19200000:
/* /* PLL enabled, bypass mode */
* PLL disabled hppllctl |= TWL6040_MCLK_19200KHZ |
* (enable PLL if MCLK jitter quality TWL6040_HPLLBP | TWL6040_HPLLENA;
* doesn't meet specification)
*/
hppllctl |= TWL6040_MCLK_19200KHZ;
break; break;
case 26000000: case 26000000:
/* PLL enabled, active mode */ /* PLL enabled, active mode */
@ -445,9 +451,9 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
TWL6040_HPLLENA; TWL6040_HPLLENA;
break; break;
case 38400000: case 38400000:
/* PLL enabled, active mode */ /* PLL enabled, bypass mode */
hppllctl |= TWL6040_MCLK_38400KHZ | hppllctl |= TWL6040_MCLK_38400KHZ |
TWL6040_HPLLENA; TWL6040_HPLLBP | TWL6040_HPLLENA;
break; break;
default: default:
dev_err(twl6040->dev, dev_err(twl6040->dev,
@ -639,6 +645,12 @@ static int twl6040_probe(struct i2c_client *client,
i2c_set_clientdata(client, twl6040); i2c_set_clientdata(client, twl6040);
twl6040->clk32k = devm_clk_get(&client->dev, "clk32k");
if (IS_ERR(twl6040->clk32k)) {
dev_info(&client->dev, "clk32k is not handled\n");
twl6040->clk32k = NULL;
}
twl6040->supplies[0].supply = "vio"; twl6040->supplies[0].supply = "vio";
twl6040->supplies[1].supply = "v2v1"; twl6040->supplies[1].supply = "v2v1";
ret = devm_regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES, ret = devm_regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES,
@ -660,6 +672,9 @@ static int twl6040_probe(struct i2c_client *client,
mutex_init(&twl6040->mutex); mutex_init(&twl6040->mutex);
init_completion(&twl6040->ready); init_completion(&twl6040->ready);
regmap_register_patch(twl6040->regmap, twl6040_patch,
ARRAY_SIZE(twl6040_patch));
twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV); twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
if (twl6040->rev < 0) { if (twl6040->rev < 0) {
dev_err(&client->dev, "Failed to read revision register: %d\n", dev_err(&client->dev, "Failed to read revision register: %d\n",
@ -679,6 +694,9 @@ static int twl6040_probe(struct i2c_client *client,
GPIOF_OUT_INIT_LOW, "audpwron"); GPIOF_OUT_INIT_LOW, "audpwron");
if (ret) if (ret)
goto gpio_err; goto gpio_err;
/* Clear any pending interrupt */
twl6040_reg_read(twl6040, TWL6040_REG_INTID);
} }
ret = regmap_add_irq_chip(twl6040->regmap, twl6040->irq, IRQF_ONESHOT, ret = regmap_add_irq_chip(twl6040->regmap, twl6040->irq, IRQF_ONESHOT,
@ -707,10 +725,6 @@ static int twl6040_probe(struct i2c_client *client,
goto readyirq_err; goto readyirq_err;
} }
/* dual-access registers controlled by I2C only */
regmap_register_patch(twl6040->regmap, twl6040_patch,
ARRAY_SIZE(twl6040_patch));
/* /*
* The main functionality of twl6040 to provide audio on OMAP4+ systems. * The main functionality of twl6040 to provide audio on OMAP4+ systems.
* We can add the ASoC codec child whenever this driver has been loaded. * We can add the ASoC codec child whenever this driver has been loaded.

View File

@ -333,7 +333,7 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x000001AA, 0x0004 }, /* R426 - FLL2 GPIO Clock */ { 0x000001AA, 0x0004 }, /* R426 - FLL2 GPIO Clock */
{ 0x00000200, 0x0006 }, /* R512 - Mic Charge Pump 1 */ { 0x00000200, 0x0006 }, /* R512 - Mic Charge Pump 1 */
{ 0x00000210, 0x00D4 }, /* R528 - LDO1 Control 1 */ { 0x00000210, 0x00D4 }, /* R528 - LDO1 Control 1 */
{ 0x00000212, 0x0001 }, /* R530 - LDO1 Control 2 */ { 0x00000212, 0x0000 }, /* R530 - LDO1 Control 2 */
{ 0x00000213, 0x0344 }, /* R531 - LDO2 Control 1 */ { 0x00000213, 0x0344 }, /* R531 - LDO2 Control 1 */
{ 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */ { 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */
{ 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */ { 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */
@ -1037,6 +1037,8 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4: case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5: case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6: case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_7:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_8:
case ARIZONA_COMFORT_NOISE_GENERATOR: case ARIZONA_COMFORT_NOISE_GENERATOR:
case ARIZONA_HAPTICS_CONTROL_1: case ARIZONA_HAPTICS_CONTROL_1:
case ARIZONA_HAPTICS_CONTROL_2: case ARIZONA_HAPTICS_CONTROL_2:

View File

@ -468,10 +468,12 @@ static const struct reg_default wm5110_reg_default[] = {
{ 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */ { 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */
{ 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */ { 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */
{ 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */ { 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */
{ 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 1 */ { 0x00000066, 0x01FF }, /* R102 - Always On Triggers Sequence Select 1 */
{ 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 2 */ { 0x00000067, 0x01FF }, /* R103 - Always On Triggers Sequence Select 2 */
{ 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 3 */ { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 3 */
{ 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 4 */ { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 4 */
{ 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 5 */
{ 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 6 */
{ 0x00000070, 0x0000 }, /* R112 - Comfort Noise Generator */ { 0x00000070, 0x0000 }, /* R112 - Comfort Noise Generator */
{ 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */ { 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */
{ 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */ { 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */
@ -549,6 +551,7 @@ static const struct reg_default wm5110_reg_default[] = {
{ 0x000002A8, 0x1422 }, /* R680 - Mic Detect Level 3 */ { 0x000002A8, 0x1422 }, /* R680 - Mic Detect Level 3 */
{ 0x000002A9, 0x300A }, /* R681 - Mic Detect Level 4 */ { 0x000002A9, 0x300A }, /* R681 - Mic Detect Level 4 */
{ 0x000002C3, 0x0000 }, /* R707 - Mic noise mix control 1 */ { 0x000002C3, 0x0000 }, /* R707 - Mic noise mix control 1 */
{ 0x000002CB, 0x0000 }, /* R715 - Isolation control */
{ 0x000002D3, 0x0000 }, /* R723 - Jack detect analogue */ { 0x000002D3, 0x0000 }, /* R723 - Jack detect analogue */
{ 0x00000300, 0x0000 }, /* R768 - Input Enables */ { 0x00000300, 0x0000 }, /* R768 - Input Enables */
{ 0x00000308, 0x0000 }, /* R776 - Input Rate */ { 0x00000308, 0x0000 }, /* R776 - Input Rate */
@ -1498,6 +1501,8 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2: case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3: case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4: case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6:
case ARIZONA_COMFORT_NOISE_GENERATOR: case ARIZONA_COMFORT_NOISE_GENERATOR:
case ARIZONA_HAPTICS_CONTROL_1: case ARIZONA_HAPTICS_CONTROL_1:
case ARIZONA_HAPTICS_CONTROL_2: case ARIZONA_HAPTICS_CONTROL_2:
@ -1580,6 +1585,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_MIC_DETECT_LEVEL_3: case ARIZONA_MIC_DETECT_LEVEL_3:
case ARIZONA_MIC_DETECT_LEVEL_4: case ARIZONA_MIC_DETECT_LEVEL_4:
case ARIZONA_MIC_NOISE_MIX_CONTROL_1: case ARIZONA_MIC_NOISE_MIX_CONTROL_1:
case ARIZONA_ISOLATION_CONTROL:
case ARIZONA_JACK_DETECT_ANALOGUE: case ARIZONA_JACK_DETECT_ANALOGUE:
case ARIZONA_INPUT_ENABLES: case ARIZONA_INPUT_ENABLES:
case ARIZONA_INPUT_ENABLES_STATUS: case ARIZONA_INPUT_ENABLES_STATUS:

View File

@ -64,7 +64,7 @@ EXPORT_SYMBOL_GPL(wm8400_block_read);
static int wm8400_register_codec(struct wm8400 *wm8400) static int wm8400_register_codec(struct wm8400 *wm8400)
{ {
struct mfd_cell cell = { const struct mfd_cell cell = {
.name = "wm8400-codec", .name = "wm8400-codec",
.platform_data = wm8400, .platform_data = wm8400,
.pdata_size = sizeof(*wm8400), .pdata_size = sizeof(*wm8400),

View File

@ -174,10 +174,10 @@ static const struct reg_default wm8997_reg_default[] = {
{ 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */ { 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */
{ 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */ { 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */
{ 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */ { 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */
{ 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 1 */ { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 3 */
{ 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 2 */ { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 4 */
{ 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 3 */ { 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 5 */
{ 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 4 */ { 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 6 */
{ 0x00000070, 0x0000 }, /* R112 - Comfort Noise Generator */ { 0x00000070, 0x0000 }, /* R112 - Comfort Noise Generator */
{ 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */ { 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */
{ 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */ { 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */
@ -814,10 +814,10 @@ static bool wm8997_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2: case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2:
case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3: case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3:
case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4: case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_1:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3: case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4: case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5:
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6:
case ARIZONA_COMFORT_NOISE_GENERATOR: case ARIZONA_COMFORT_NOISE_GENERATOR:
case ARIZONA_HAPTICS_CONTROL_1: case ARIZONA_HAPTICS_CONTROL_1:
case ARIZONA_HAPTICS_CONTROL_2: case ARIZONA_HAPTICS_CONTROL_2:
@ -846,6 +846,7 @@ static bool wm8997_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_RATE_ESTIMATOR_3: case ARIZONA_RATE_ESTIMATOR_3:
case ARIZONA_RATE_ESTIMATOR_4: case ARIZONA_RATE_ESTIMATOR_4:
case ARIZONA_RATE_ESTIMATOR_5: case ARIZONA_RATE_ESTIMATOR_5:
case ARIZONA_DYNAMIC_FREQUENCY_SCALING_1:
case ARIZONA_FLL1_CONTROL_1: case ARIZONA_FLL1_CONTROL_1:
case ARIZONA_FLL1_CONTROL_2: case ARIZONA_FLL1_CONTROL_2:
case ARIZONA_FLL1_CONTROL_3: case ARIZONA_FLL1_CONTROL_3:
@ -880,6 +881,7 @@ static bool wm8997_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_FLL2_GPIO_CLOCK: case ARIZONA_FLL2_GPIO_CLOCK:
case ARIZONA_MIC_CHARGE_PUMP_1: case ARIZONA_MIC_CHARGE_PUMP_1:
case ARIZONA_LDO1_CONTROL_1: case ARIZONA_LDO1_CONTROL_1:
case ARIZONA_LDO1_CONTROL_2:
case ARIZONA_LDO2_CONTROL_1: case ARIZONA_LDO2_CONTROL_1:
case ARIZONA_MIC_BIAS_CTRL_1: case ARIZONA_MIC_BIAS_CTRL_1:
case ARIZONA_MIC_BIAS_CTRL_2: case ARIZONA_MIC_BIAS_CTRL_2:

View File

@ -694,3 +694,10 @@ config MMC_REALTEK_PCI
help help
Say Y here to include driver code to support SD/MMC card interface Say Y here to include driver code to support SD/MMC card interface
of Realtek PCI-E card reader of Realtek PCI-E card reader
config MMC_REALTEK_USB
tristate "Realtek USB SD/MMC Card Interface Driver"
depends on MFD_RTSX_USB
help
Say Y here to include driver code to support SD/MMC card interface
of Realtek RTS5129/39 series card reader

View File

@ -52,6 +52,7 @@ obj-$(CONFIG_MMC_USHC) += ushc.o
obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o
obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o
obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o

File diff suppressed because it is too large Load Diff

View File

@ -40,6 +40,7 @@
struct s5m_rtc_info { struct s5m_rtc_info {
struct device *dev; struct device *dev;
struct i2c_client *i2c;
struct sec_pmic_dev *s5m87xx; struct sec_pmic_dev *s5m87xx;
struct regmap *regmap; struct regmap *regmap;
struct rtc_device *rtc_dev; struct rtc_device *rtc_dev;
@ -49,6 +50,20 @@ struct s5m_rtc_info {
bool wtsr_smpl; bool wtsr_smpl;
}; };
static const struct regmap_config s5m_rtc_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = SEC_RTC_REG_MAX,
};
static const struct regmap_config s2mps14_rtc_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = S2MPS_RTC_REG_MAX,
};
static void s5m8767_data_to_tm(u8 *data, struct rtc_time *tm, static void s5m8767_data_to_tm(u8 *data, struct rtc_time *tm,
int rtc_24hr_mode) int rtc_24hr_mode)
{ {
@ -554,6 +569,7 @@ static int s5m_rtc_probe(struct platform_device *pdev)
struct sec_pmic_dev *s5m87xx = dev_get_drvdata(pdev->dev.parent); struct sec_pmic_dev *s5m87xx = dev_get_drvdata(pdev->dev.parent);
struct sec_platform_data *pdata = s5m87xx->pdata; struct sec_platform_data *pdata = s5m87xx->pdata;
struct s5m_rtc_info *info; struct s5m_rtc_info *info;
const struct regmap_config *regmap_cfg;
int ret; int ret;
if (!pdata) { if (!pdata) {
@ -565,9 +581,37 @@ static int s5m_rtc_probe(struct platform_device *pdev)
if (!info) if (!info)
return -ENOMEM; return -ENOMEM;
switch (pdata->device_type) {
case S2MPS14X:
regmap_cfg = &s2mps14_rtc_regmap_config;
break;
case S5M8763X:
regmap_cfg = &s5m_rtc_regmap_config;
break;
case S5M8767X:
regmap_cfg = &s5m_rtc_regmap_config;
break;
default:
dev_err(&pdev->dev, "Device type is not supported by RTC driver\n");
return -ENODEV;
}
info->i2c = i2c_new_dummy(s5m87xx->i2c->adapter, RTC_I2C_ADDR);
if (!info->i2c) {
dev_err(&pdev->dev, "Failed to allocate I2C for RTC\n");
return -ENODEV;
}
info->regmap = devm_regmap_init_i2c(info->i2c, regmap_cfg);
if (IS_ERR(info->regmap)) {
ret = PTR_ERR(info->regmap);
dev_err(&pdev->dev, "Failed to allocate RTC register map: %d\n",
ret);
goto err;
}
info->dev = &pdev->dev; info->dev = &pdev->dev;
info->s5m87xx = s5m87xx; info->s5m87xx = s5m87xx;
info->regmap = s5m87xx->regmap_rtc;
info->device_type = s5m87xx->device_type; info->device_type = s5m87xx->device_type;
info->wtsr_smpl = s5m87xx->wtsr_smpl; info->wtsr_smpl = s5m87xx->wtsr_smpl;
@ -585,7 +629,7 @@ static int s5m_rtc_probe(struct platform_device *pdev)
default: default:
ret = -EINVAL; ret = -EINVAL;
dev_err(&pdev->dev, "Unsupported device type: %d\n", ret); dev_err(&pdev->dev, "Unsupported device type: %d\n", ret);
return ret; goto err;
} }
platform_set_drvdata(pdev, info); platform_set_drvdata(pdev, info);
@ -602,15 +646,24 @@ static int s5m_rtc_probe(struct platform_device *pdev)
info->rtc_dev = devm_rtc_device_register(&pdev->dev, "s5m-rtc", info->rtc_dev = devm_rtc_device_register(&pdev->dev, "s5m-rtc",
&s5m_rtc_ops, THIS_MODULE); &s5m_rtc_ops, THIS_MODULE);
if (IS_ERR(info->rtc_dev)) if (IS_ERR(info->rtc_dev)) {
return PTR_ERR(info->rtc_dev); ret = PTR_ERR(info->rtc_dev);
goto err;
}
ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
s5m_rtc_alarm_irq, 0, "rtc-alarm0", s5m_rtc_alarm_irq, 0, "rtc-alarm0",
info); info);
if (ret < 0) if (ret < 0) {
dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
info->irq, ret); info->irq, ret);
goto err;
}
return 0;
err:
i2c_unregister_device(info->i2c);
return ret; return ret;
} }
@ -639,6 +692,17 @@ static void s5m_rtc_shutdown(struct platform_device *pdev)
s5m_rtc_enable_smpl(info, false); s5m_rtc_enable_smpl(info, false);
} }
static int s5m_rtc_remove(struct platform_device *pdev)
{
struct s5m_rtc_info *info = platform_get_drvdata(pdev);
/* Perform also all shutdown steps when removing */
s5m_rtc_shutdown(pdev);
i2c_unregister_device(info->i2c);
return 0;
}
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int s5m_rtc_resume(struct device *dev) static int s5m_rtc_resume(struct device *dev)
{ {
@ -676,6 +740,7 @@ static struct platform_driver s5m_rtc_driver = {
.pm = &s5m_rtc_pm_ops, .pm = &s5m_rtc_pm_ops,
}, },
.probe = s5m_rtc_probe, .probe = s5m_rtc_probe,
.remove = s5m_rtc_remove,
.shutdown = s5m_rtc_shutdown, .shutdown = s5m_rtc_shutdown,
.id_table = s5m_rtc_id, .id_table = s5m_rtc_id,
}; };

View File

@ -330,7 +330,6 @@ int abx500_mask_and_set_register_interruptible(struct device *dev, u8 bank,
int abx500_get_chip_id(struct device *dev); int abx500_get_chip_id(struct device *dev);
int abx500_event_registers_startup_state_get(struct device *dev, u8 *event); int abx500_event_registers_startup_state_get(struct device *dev, u8 *event);
int abx500_startup_irq_enabled(struct device *dev, unsigned int irq); int abx500_startup_irq_enabled(struct device *dev, unsigned int irq);
void abx500_dump_all_banks(void);
struct abx500_ops { struct abx500_ops {
int (*get_chip_id) (struct device *); int (*get_chip_id) (struct device *);

View File

@ -42,12 +42,14 @@
#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2 0x62 #define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2 0x62
#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3 0x63 #define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3 0x63
#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4 0x64 #define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4 0x64
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_1 0x68 #define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_1 0x66
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2 0x69 #define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2 0x67
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3 0x6A #define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3 0x68
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4 0x6B #define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4 0x69
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5 0x6C #define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5 0x6A
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6 0x6D #define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6 0x6B
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_7 0x6C
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_8 0x6D
#define ARIZONA_COMFORT_NOISE_GENERATOR 0x70 #define ARIZONA_COMFORT_NOISE_GENERATOR 0x70
#define ARIZONA_HAPTICS_CONTROL_1 0x90 #define ARIZONA_HAPTICS_CONTROL_1 0x90
#define ARIZONA_HAPTICS_CONTROL_2 0x91 #define ARIZONA_HAPTICS_CONTROL_2 0x91

180
include/linux/mfd/axp20x.h Normal file
View File

@ -0,0 +1,180 @@
/*
* Functions and registers to access AXP20X power management chip.
*
* Copyright (C) 2013, Carlo Caione <carlo@caione.org>
*
* 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 __LINUX_MFD_AXP20X_H
#define __LINUX_MFD_AXP20X_H
enum {
AXP202_ID = 0,
AXP209_ID,
};
#define AXP20X_DATACACHE(m) (0x04 + (m))
/* Power supply */
#define AXP20X_PWR_INPUT_STATUS 0x00
#define AXP20X_PWR_OP_MODE 0x01
#define AXP20X_USB_OTG_STATUS 0x02
#define AXP20X_PWR_OUT_CTRL 0x12
#define AXP20X_DCDC2_V_OUT 0x23
#define AXP20X_DCDC2_LDO3_V_SCAL 0x25
#define AXP20X_DCDC3_V_OUT 0x27
#define AXP20X_LDO24_V_OUT 0x28
#define AXP20X_LDO3_V_OUT 0x29
#define AXP20X_VBUS_IPSOUT_MGMT 0x30
#define AXP20X_V_OFF 0x31
#define AXP20X_OFF_CTRL 0x32
#define AXP20X_CHRG_CTRL1 0x33
#define AXP20X_CHRG_CTRL2 0x34
#define AXP20X_CHRG_BAK_CTRL 0x35
#define AXP20X_PEK_KEY 0x36
#define AXP20X_DCDC_FREQ 0x37
#define AXP20X_V_LTF_CHRG 0x38
#define AXP20X_V_HTF_CHRG 0x39
#define AXP20X_APS_WARN_L1 0x3a
#define AXP20X_APS_WARN_L2 0x3b
#define AXP20X_V_LTF_DISCHRG 0x3c
#define AXP20X_V_HTF_DISCHRG 0x3d
/* Interrupt */
#define AXP20X_IRQ1_EN 0x40
#define AXP20X_IRQ2_EN 0x41
#define AXP20X_IRQ3_EN 0x42
#define AXP20X_IRQ4_EN 0x43
#define AXP20X_IRQ5_EN 0x44
#define AXP20X_IRQ1_STATE 0x48
#define AXP20X_IRQ2_STATE 0x49
#define AXP20X_IRQ3_STATE 0x4a
#define AXP20X_IRQ4_STATE 0x4b
#define AXP20X_IRQ5_STATE 0x4c
/* ADC */
#define AXP20X_ACIN_V_ADC_H 0x56
#define AXP20X_ACIN_V_ADC_L 0x57
#define AXP20X_ACIN_I_ADC_H 0x58
#define AXP20X_ACIN_I_ADC_L 0x59
#define AXP20X_VBUS_V_ADC_H 0x5a
#define AXP20X_VBUS_V_ADC_L 0x5b
#define AXP20X_VBUS_I_ADC_H 0x5c
#define AXP20X_VBUS_I_ADC_L 0x5d
#define AXP20X_TEMP_ADC_H 0x5e
#define AXP20X_TEMP_ADC_L 0x5f
#define AXP20X_TS_IN_H 0x62
#define AXP20X_TS_IN_L 0x63
#define AXP20X_GPIO0_V_ADC_H 0x64
#define AXP20X_GPIO0_V_ADC_L 0x65
#define AXP20X_GPIO1_V_ADC_H 0x66
#define AXP20X_GPIO1_V_ADC_L 0x67
#define AXP20X_PWR_BATT_H 0x70
#define AXP20X_PWR_BATT_M 0x71
#define AXP20X_PWR_BATT_L 0x72
#define AXP20X_BATT_V_H 0x78
#define AXP20X_BATT_V_L 0x79
#define AXP20X_BATT_CHRG_I_H 0x7a
#define AXP20X_BATT_CHRG_I_L 0x7b
#define AXP20X_BATT_DISCHRG_I_H 0x7c
#define AXP20X_BATT_DISCHRG_I_L 0x7d
#define AXP20X_IPSOUT_V_HIGH_H 0x7e
#define AXP20X_IPSOUT_V_HIGH_L 0x7f
/* Power supply */
#define AXP20X_DCDC_MODE 0x80
#define AXP20X_ADC_EN1 0x82
#define AXP20X_ADC_EN2 0x83
#define AXP20X_ADC_RATE 0x84
#define AXP20X_GPIO10_IN_RANGE 0x85
#define AXP20X_GPIO1_ADC_IRQ_RIS 0x86
#define AXP20X_GPIO1_ADC_IRQ_FAL 0x87
#define AXP20X_TIMER_CTRL 0x8a
#define AXP20X_VBUS_MON 0x8b
#define AXP20X_OVER_TMP 0x8f
/* GPIO */
#define AXP20X_GPIO0_CTRL 0x90
#define AXP20X_LDO5_V_OUT 0x91
#define AXP20X_GPIO1_CTRL 0x92
#define AXP20X_GPIO2_CTRL 0x93
#define AXP20X_GPIO20_SS 0x94
#define AXP20X_GPIO3_CTRL 0x95
/* Battery */
#define AXP20X_CHRG_CC_31_24 0xb0
#define AXP20X_CHRG_CC_23_16 0xb1
#define AXP20X_CHRG_CC_15_8 0xb2
#define AXP20X_CHRG_CC_7_0 0xb3
#define AXP20X_DISCHRG_CC_31_24 0xb4
#define AXP20X_DISCHRG_CC_23_16 0xb5
#define AXP20X_DISCHRG_CC_15_8 0xb6
#define AXP20X_DISCHRG_CC_7_0 0xb7
#define AXP20X_CC_CTRL 0xb8
#define AXP20X_FG_RES 0xb9
/* Regulators IDs */
enum {
AXP20X_LDO1 = 0,
AXP20X_LDO2,
AXP20X_LDO3,
AXP20X_LDO4,
AXP20X_LDO5,
AXP20X_DCDC2,
AXP20X_DCDC3,
AXP20X_REG_ID_MAX,
};
/* IRQs */
enum {
AXP20X_IRQ_ACIN_OVER_V = 1,
AXP20X_IRQ_ACIN_PLUGIN,
AXP20X_IRQ_ACIN_REMOVAL,
AXP20X_IRQ_VBUS_OVER_V,
AXP20X_IRQ_VBUS_PLUGIN,
AXP20X_IRQ_VBUS_REMOVAL,
AXP20X_IRQ_VBUS_V_LOW,
AXP20X_IRQ_BATT_PLUGIN,
AXP20X_IRQ_BATT_REMOVAL,
AXP20X_IRQ_BATT_ENT_ACT_MODE,
AXP20X_IRQ_BATT_EXIT_ACT_MODE,
AXP20X_IRQ_CHARG,
AXP20X_IRQ_CHARG_DONE,
AXP20X_IRQ_BATT_TEMP_HIGH,
AXP20X_IRQ_BATT_TEMP_LOW,
AXP20X_IRQ_DIE_TEMP_HIGH,
AXP20X_IRQ_CHARG_I_LOW,
AXP20X_IRQ_DCDC1_V_LONG,
AXP20X_IRQ_DCDC2_V_LONG,
AXP20X_IRQ_DCDC3_V_LONG,
AXP20X_IRQ_PEK_SHORT = 22,
AXP20X_IRQ_PEK_LONG,
AXP20X_IRQ_N_OE_PWR_ON,
AXP20X_IRQ_N_OE_PWR_OFF,
AXP20X_IRQ_VBUS_VALID,
AXP20X_IRQ_VBUS_NOT_VALID,
AXP20X_IRQ_VBUS_SESS_VALID,
AXP20X_IRQ_VBUS_SESS_END,
AXP20X_IRQ_LOW_PWR_LVL1,
AXP20X_IRQ_LOW_PWR_LVL2,
AXP20X_IRQ_TIMER,
AXP20X_IRQ_PEK_RIS_EDGE,
AXP20X_IRQ_PEK_FAL_EDGE,
AXP20X_IRQ_GPIO3_INPUT,
AXP20X_IRQ_GPIO2_INPUT,
AXP20X_IRQ_GPIO1_INPUT,
AXP20X_IRQ_GPIO0_INPUT,
};
struct axp20x_dev {
struct device *dev;
struct i2c_client *i2c_client;
struct regmap *regmap;
struct regmap_irq_chip_data *regmap_irqc;
long variant;
};
#endif /* __LINUX_MFD_AXP20X_H */

View File

@ -29,8 +29,8 @@ enum {
EC_MSG_RX_PROTO_BYTES = 3, EC_MSG_RX_PROTO_BYTES = 3,
/* Max length of messages */ /* Max length of messages */
EC_MSG_BYTES = EC_HOST_PARAM_SIZE + EC_MSG_TX_PROTO_BYTES, EC_MSG_BYTES = EC_PROTO2_MAX_PARAM_SIZE +
EC_MSG_TX_PROTO_BYTES,
}; };
/** /**

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,148 @@
/*
* Header file for the compaq Micro MFD
*/
#ifndef _MFD_IPAQ_MICRO_H_
#define _MFD_IPAQ_MICRO_H_
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/list.h>
#define TX_BUF_SIZE 32
#define RX_BUF_SIZE 16
#define CHAR_SOF 0x02
/*
* These are the different messages that can be sent to the microcontroller
* to control various aspects.
*/
#define MSG_VERSION 0x0
#define MSG_KEYBOARD 0x2
#define MSG_TOUCHSCREEN 0x3
#define MSG_EEPROM_READ 0x4
#define MSG_EEPROM_WRITE 0x5
#define MSG_THERMAL_SENSOR 0x6
#define MSG_NOTIFY_LED 0x8
#define MSG_BATTERY 0x9
#define MSG_SPI_READ 0xb
#define MSG_SPI_WRITE 0xc
#define MSG_BACKLIGHT 0xd /* H3600 only */
#define MSG_CODEC_CTRL 0xe /* H3100 only */
#define MSG_DISPLAY_CTRL 0xf /* H3100 only */
/* state of receiver parser */
enum rx_state {
STATE_SOF = 0, /* Next byte should be start of frame */
STATE_ID, /* Next byte is ID & message length */
STATE_DATA, /* Next byte is a data byte */
STATE_CHKSUM /* Next byte should be checksum */
};
/**
* struct ipaq_micro_txdev - TX state
* @len: length of message in TX buffer
* @index: current index into TX buffer
* @buf: TX buffer
*/
struct ipaq_micro_txdev {
u8 len;
u8 index;
u8 buf[TX_BUF_SIZE];
};
/**
* struct ipaq_micro_rxdev - RX state
* @state: context of RX state machine
* @chksum: calculated checksum
* @id: message ID from packet
* @len: RX buffer length
* @index: RX buffer index
* @buf: RX buffer
*/
struct ipaq_micro_rxdev {
enum rx_state state;
unsigned char chksum;
u8 id;
unsigned int len;
unsigned int index;
u8 buf[RX_BUF_SIZE];
};
/**
* struct ipaq_micro_msg - message to the iPAQ microcontroller
* @id: 4-bit ID of the message
* @tx_len: length of TX data
* @tx_data: TX data to send
* @rx_len: length of receieved RX data
* @rx_data: RX data to recieve
* @ack: a completion that will be completed when RX is complete
* @node: list node if message gets queued
*/
struct ipaq_micro_msg {
u8 id;
u8 tx_len;
u8 tx_data[TX_BUF_SIZE];
u8 rx_len;
u8 rx_data[RX_BUF_SIZE];
struct completion ack;
struct list_head node;
};
/**
* struct ipaq_micro - iPAQ microcontroller state
* @dev: corresponding platform device
* @base: virtual memory base for underlying serial device
* @sdlc: virtual memory base for Synchronous Data Link Controller
* @version: version string
* @tx: TX state
* @rx: RX state
* @lock: lock for this state container
* @msg: current message
* @queue: message queue
* @key: callback for asynchronous key events
* @key_data: data to pass along with key events
* @ts: callback for asynchronous touchscreen events
* @ts_data: data to pass along with key events
*/
struct ipaq_micro {
struct device *dev;
void __iomem *base;
void __iomem *sdlc;
char version[5];
struct ipaq_micro_txdev tx; /* transmit ISR state */
struct ipaq_micro_rxdev rx; /* receive ISR state */
spinlock_t lock;
struct ipaq_micro_msg *msg;
struct list_head queue;
void (*key) (void *data, int len, unsigned char *rxdata);
void *key_data;
void (*ts) (void *data, int len, unsigned char *rxdata);
void *ts_data;
};
extern int
ipaq_micro_tx_msg(struct ipaq_micro *micro, struct ipaq_micro_msg *msg);
static inline int
ipaq_micro_tx_msg_sync(struct ipaq_micro *micro,
struct ipaq_micro_msg *msg)
{
int ret;
init_completion(&msg->ack);
ret = ipaq_micro_tx_msg(micro, msg);
wait_for_completion(&msg->ack);
return ret;
}
static inline int
ipaq_micro_tx_msg_async(struct ipaq_micro *micro,
struct ipaq_micro_msg *msg)
{
init_completion(&msg->ack);
return ipaq_micro_tx_msg(micro, msg);
}
#endif /* _MFD_IPAQ_MICRO_H_ */

View File

@ -51,6 +51,8 @@
#define KEMPLD_TYPE_DEBUG 0x1 #define KEMPLD_TYPE_DEBUG 0x1
#define KEMPLD_TYPE_CUSTOM 0x2 #define KEMPLD_TYPE_CUSTOM 0x2
#define KEMPLD_VERSION_LEN 10
/** /**
* struct kempld_info - PLD device information structure * struct kempld_info - PLD device information structure
* @major: PLD major revision * @major: PLD major revision
@ -60,6 +62,7 @@
* @type: PLD type * @type: PLD type
* @spec_major: PLD FW specification major revision * @spec_major: PLD FW specification major revision
* @spec_minor: PLD FW specification minor revision * @spec_minor: PLD FW specification minor revision
* @version: PLD version string
*/ */
struct kempld_info { struct kempld_info {
unsigned int major; unsigned int major;
@ -69,6 +72,7 @@ struct kempld_info {
unsigned int type; unsigned int type;
unsigned int spec_major; unsigned int spec_major;
unsigned int spec_minor; unsigned int spec_minor;
char version[KEMPLD_VERSION_LEN];
}; };
/** /**

View File

@ -117,10 +117,6 @@ struct mc13xxx_led_platform_data {
#define MAX_LED_CONTROL_REGS 6 #define MAX_LED_CONTROL_REGS 6
struct mc13xxx_leds_platform_data {
struct mc13xxx_led_platform_data *led;
int num_leds;
/* MC13783 LED Control 0 */ /* MC13783 LED Control 0 */
#define MC13783_LED_C0_ENABLE (1 << 0) #define MC13783_LED_C0_ENABLE (1 << 0)
#define MC13783_LED_C0_TRIODE_MD (1 << 7) #define MC13783_LED_C0_TRIODE_MD (1 << 7)
@ -169,10 +165,13 @@ struct mc13xxx_leds_platform_data {
/* MC34708 LED Control 0 */ /* MC34708 LED Control 0 */
#define MC34708_LED_C0_CURRENT_R(x) (((x) & 0x3) << 9) #define MC34708_LED_C0_CURRENT_R(x) (((x) & 0x3) << 9)
#define MC34708_LED_C0_CURRENT_G(x) (((x) & 0x3) << 21) #define MC34708_LED_C0_CURRENT_G(x) (((x) & 0x3) << 21)
struct mc13xxx_leds_platform_data {
struct mc13xxx_led_platform_data *led;
int num_leds;
u32 led_control[MAX_LED_CONTROL_REGS]; u32 led_control[MAX_LED_CONTROL_REGS];
}; };
struct mc13xxx_buttons_platform_data {
#define MC13783_BUTTON_DBNC_0MS 0 #define MC13783_BUTTON_DBNC_0MS 0
#define MC13783_BUTTON_DBNC_30MS 1 #define MC13783_BUTTON_DBNC_30MS 1
#define MC13783_BUTTON_DBNC_150MS 2 #define MC13783_BUTTON_DBNC_150MS 2
@ -180,6 +179,8 @@ struct mc13xxx_buttons_platform_data {
#define MC13783_BUTTON_ENABLE (1 << 2) #define MC13783_BUTTON_ENABLE (1 << 2)
#define MC13783_BUTTON_POL_INVERT (1 << 3) #define MC13783_BUTTON_POL_INVERT (1 << 3)
#define MC13783_BUTTON_RESET_EN (1 << 4) #define MC13783_BUTTON_RESET_EN (1 << 4)
struct mc13xxx_buttons_platform_data {
int b1on_flags; int b1on_flags;
unsigned short b1on_key; unsigned short b1on_key;
int b2on_flags; int b2on_flags;
@ -188,14 +189,14 @@ struct mc13xxx_buttons_platform_data {
unsigned short b3on_key; unsigned short b3on_key;
}; };
#define MC13783_TS_ATO_FIRST false
#define MC13783_TS_ATO_EACH true
struct mc13xxx_ts_platform_data { struct mc13xxx_ts_platform_data {
/* Delay between Touchscreen polarization and ADC Conversion. /* Delay between Touchscreen polarization and ADC Conversion.
* Given in clock ticks of a 32 kHz clock which gives a granularity of * Given in clock ticks of a 32 kHz clock which gives a granularity of
* about 30.5ms */ * about 30.5ms */
u8 ato; u8 ato;
#define MC13783_TS_ATO_FIRST false
#define MC13783_TS_ATO_EACH true
/* Use the ATO delay only for the first conversion or for each one */ /* Use the ATO delay only for the first conversion or for each one */
bool atox; bool atox;
}; };
@ -210,11 +211,12 @@ struct mc13xxx_codec_platform_data {
enum mc13783_ssi_port dac_ssi_port; enum mc13783_ssi_port dac_ssi_port;
}; };
struct mc13xxx_platform_data { #define MC13XXX_USE_TOUCHSCREEN (1 << 0)
#define MC13XXX_USE_TOUCHSCREEN (1 << 0)
#define MC13XXX_USE_CODEC (1 << 1) #define MC13XXX_USE_CODEC (1 << 1)
#define MC13XXX_USE_ADC (1 << 2) #define MC13XXX_USE_ADC (1 << 2)
#define MC13XXX_USE_RTC (1 << 3) #define MC13XXX_USE_RTC (1 << 3)
struct mc13xxx_platform_data {
unsigned int flags; unsigned int flags;
struct mc13xxx_regulator_platform_data regulators; struct mc13xxx_regulator_platform_data regulators;

File diff suppressed because it is too large Load Diff

View File

@ -1,81 +0,0 @@
/*
* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*/
/*
* Qualcomm PMIC 8xxx driver header file
*
*/
#ifndef __MFD_PM8XXX_CORE_H
#define __MFD_PM8XXX_CORE_H
#include <linux/mfd/core.h>
struct pm8xxx_drvdata {
int (*pmic_readb) (const struct device *dev, u16 addr, u8 *val);
int (*pmic_writeb) (const struct device *dev, u16 addr, u8 val);
int (*pmic_read_buf) (const struct device *dev, u16 addr, u8 *buf,
int n);
int (*pmic_write_buf) (const struct device *dev, u16 addr, u8 *buf,
int n);
int (*pmic_read_irq_stat) (const struct device *dev, int irq);
void *pm_chip_data;
};
static inline int pm8xxx_readb(const struct device *dev, u16 addr, u8 *val)
{
struct pm8xxx_drvdata *dd = dev_get_drvdata(dev);
if (!dd)
return -EINVAL;
return dd->pmic_readb(dev, addr, val);
}
static inline int pm8xxx_writeb(const struct device *dev, u16 addr, u8 val)
{
struct pm8xxx_drvdata *dd = dev_get_drvdata(dev);
if (!dd)
return -EINVAL;
return dd->pmic_writeb(dev, addr, val);
}
static inline int pm8xxx_read_buf(const struct device *dev, u16 addr, u8 *buf,
int n)
{
struct pm8xxx_drvdata *dd = dev_get_drvdata(dev);
if (!dd)
return -EINVAL;
return dd->pmic_read_buf(dev, addr, buf, n);
}
static inline int pm8xxx_write_buf(const struct device *dev, u16 addr, u8 *buf,
int n)
{
struct pm8xxx_drvdata *dd = dev_get_drvdata(dev);
if (!dd)
return -EINVAL;
return dd->pmic_write_buf(dev, addr, buf, n);
}
static inline int pm8xxx_read_irq_stat(const struct device *dev, int irq)
{
struct pm8xxx_drvdata *dd = dev_get_drvdata(dev);
if (!dd)
return -EINVAL;
return dd->pmic_read_irq_stat(dev, irq);
}
#endif

View File

@ -12,7 +12,7 @@
#define RDC321X_GPIO_CTRL_REG2 0x84 #define RDC321X_GPIO_CTRL_REG2 0x84
#define RDC321X_GPIO_DATA_REG2 0x88 #define RDC321X_GPIO_DATA_REG2 0x88
#define RDC321X_MAX_GPIO 58 #define RDC321X_NUM_GPIO 59
struct rdc321x_gpio_pdata { struct rdc321x_gpio_pdata {
struct pci_dev *sb_pdev; struct pci_dev *sb_pdev;

View File

@ -24,35 +24,36 @@ enum sec_device_type {
}; };
/** /**
* struct sec_pmic_dev - s5m87xx master device for sub-drivers * struct sec_pmic_dev - s2m/s5m master device for sub-drivers
* @dev: master device of the chip (can be used to access platform data) * @dev: Master device of the chip
* @pdata: pointer to private data used to pass platform data to child * @pdata: Platform data populated with data from DTS
* @i2c: i2c client private data for regulator * or board files
* @rtc: i2c client private data for rtc * @regmap_pmic: Regmap associated with PMIC's I2C address
* @iolock: mutex for serializing io access * @i2c: I2C client of the main driver
* @irqlock: mutex for buslock * @device_type: Type of device, matches enum sec_device_type
* @irq_base: base IRQ number for sec-pmic, required for IRQs * @irq_base: Base IRQ number for device, required for IRQs
* @irq: generic IRQ number for s5m87xx * @irq: Generic IRQ number for device
* @ono: power onoff IRQ number for s5m87xx * @irq_data: Runtime data structure for IRQ controller
* @irq_masks_cur: currently active value * @ono: Power onoff IRQ number for s5m87xx
* @irq_masks_cache: cached hardware value * @wakeup: Whether or not this is a wakeup device
* @type: indicate which s5m87xx "variant" is used * @wtsr_smpl: Whether or not to enable in RTC driver the Watchdog
* Timer Software Reset (registers set to default value
* after PWRHOLD falling) and Sudden Momentary Power Loss
* (PMIC will enter power on sequence after short drop in
* VBATT voltage).
*/ */
struct sec_pmic_dev { struct sec_pmic_dev {
struct device *dev; struct device *dev;
struct sec_platform_data *pdata; struct sec_platform_data *pdata;
struct regmap *regmap_pmic; struct regmap *regmap_pmic;
struct regmap *regmap_rtc;
struct i2c_client *i2c; struct i2c_client *i2c;
struct i2c_client *rtc;
int device_type; unsigned long device_type;
int irq_base; int irq_base;
int irq; int irq;
struct regmap_irq_chip_data *irq_data; struct regmap_irq_chip_data *irq_data;
int ono; int ono;
unsigned long type;
bool wakeup; bool wakeup;
bool wtsr_smpl; bool wtsr_smpl;
}; };

View File

@ -11,6 +11,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
struct device; struct device;
struct regulator;
enum stmpe_block { enum stmpe_block {
STMPE_BLOCK_GPIO = 1 << 0, STMPE_BLOCK_GPIO = 1 << 0,
@ -62,6 +63,8 @@ struct stmpe_client_info;
/** /**
* struct stmpe - STMPE MFD structure * struct stmpe - STMPE MFD structure
* @vcc: optional VCC regulator
* @vio: optional VIO regulator
* @lock: lock protecting I/O operations * @lock: lock protecting I/O operations
* @irq_lock: IRQ bus lock * @irq_lock: IRQ bus lock
* @dev: device, mostly for dev_dbg() * @dev: device, mostly for dev_dbg()
@ -73,13 +76,14 @@ struct stmpe_client_info;
* @regs: list of addresses of registers which are at different addresses on * @regs: list of addresses of registers which are at different addresses on
* different variants. Indexed by one of STMPE_IDX_*. * different variants. Indexed by one of STMPE_IDX_*.
* @irq: irq number for stmpe * @irq: irq number for stmpe
* @irq_base: starting IRQ number for internal IRQs
* @num_gpios: number of gpios, differs for variants * @num_gpios: number of gpios, differs for variants
* @ier: cache of IER registers for bus_lock * @ier: cache of IER registers for bus_lock
* @oldier: cache of IER registers for bus_lock * @oldier: cache of IER registers for bus_lock
* @pdata: platform data * @pdata: platform data
*/ */
struct stmpe { struct stmpe {
struct regulator *vcc;
struct regulator *vio;
struct mutex lock; struct mutex lock;
struct mutex irq_lock; struct mutex irq_lock;
struct device *dev; struct device *dev;
@ -91,7 +95,6 @@ struct stmpe {
const u8 *regs; const u8 *regs;
int irq; int irq;
int irq_base;
int num_gpios; int num_gpios;
u8 ier[2]; u8 ier[2];
u8 oldier[2]; u8 oldier[2];
@ -132,8 +135,6 @@ struct stmpe_keypad_platform_data {
/** /**
* struct stmpe_gpio_platform_data - STMPE GPIO platform data * struct stmpe_gpio_platform_data - STMPE GPIO platform data
* @gpio_base: first gpio number assigned. A maximum of
* %STMPE_NR_GPIOS GPIOs will be allocated.
* @norequest_mask: bitmask specifying which GPIOs should _not_ be * @norequest_mask: bitmask specifying which GPIOs should _not_ be
* requestable due to different usage (e.g. touch, keypad) * requestable due to different usage (e.g. touch, keypad)
* STMPE_GPIO_NOREQ_* macros can be used here. * STMPE_GPIO_NOREQ_* macros can be used here.
@ -141,7 +142,6 @@ struct stmpe_keypad_platform_data {
* @remove: board specific remove callback * @remove: board specific remove callback
*/ */
struct stmpe_gpio_platform_data { struct stmpe_gpio_platform_data {
int gpio_base;
unsigned norequest_mask; unsigned norequest_mask;
void (*setup)(struct stmpe *stmpe, unsigned gpio_base); void (*setup)(struct stmpe *stmpe, unsigned gpio_base);
void (*remove)(struct stmpe *stmpe, unsigned gpio_base); void (*remove)(struct stmpe *stmpe, unsigned gpio_base);
@ -195,8 +195,6 @@ struct stmpe_ts_platform_data {
* @irq_trigger: IRQ trigger to use for the interrupt to the host * @irq_trigger: IRQ trigger to use for the interrupt to the host
* @autosleep: bool to enable/disable stmpe autosleep * @autosleep: bool to enable/disable stmpe autosleep
* @autosleep_timeout: inactivity timeout in milliseconds for autosleep * @autosleep_timeout: inactivity timeout in milliseconds for autosleep
* @irq_base: base IRQ number. %STMPE_NR_IRQS irqs will be used, or
* %STMPE_NR_INTERNAL_IRQS if the GPIO driver is not used.
* @irq_over_gpio: true if gpio is used to get irq * @irq_over_gpio: true if gpio is used to get irq
* @irq_gpio: gpio number over which irq will be requested (significant only if * @irq_gpio: gpio number over which irq will be requested (significant only if
* irq_over_gpio is true) * irq_over_gpio is true)
@ -207,7 +205,6 @@ struct stmpe_ts_platform_data {
struct stmpe_platform_data { struct stmpe_platform_data {
int id; int id;
unsigned int blocks; unsigned int blocks;
int irq_base;
unsigned int irq_trigger; unsigned int irq_trigger;
bool autosleep; bool autosleep;
bool irq_over_gpio; bool irq_over_gpio;
@ -219,10 +216,4 @@ struct stmpe_platform_data {
struct stmpe_ts_platform_data *ts; struct stmpe_ts_platform_data *ts;
}; };
#define STMPE_NR_INTERNAL_IRQS 9
#define STMPE_INT_GPIO(x) (STMPE_NR_INTERNAL_IRQS + (x))
#define STMPE_NR_GPIOS 24
#define STMPE_NR_IRQS STMPE_INT_GPIO(STMPE_NR_GPIOS)
#endif #endif

View File

@ -15,6 +15,8 @@
#ifndef __LINUX_MFD_SYSCON_H__ #ifndef __LINUX_MFD_SYSCON_H__
#define __LINUX_MFD_SYSCON_H__ #define __LINUX_MFD_SYSCON_H__
#include <linux/err.h>
struct device_node; struct device_node;
#ifdef CONFIG_MFD_SYSCON #ifdef CONFIG_MFD_SYSCON

View File

@ -267,7 +267,6 @@ struct tps65218 {
u32 irq_mask; u32 irq_mask;
struct regmap_irq_chip_data *irq_data; struct regmap_irq_chip_data *irq_data;
struct regulator_desc desc[TPS65218_NUM_REGULATOR]; struct regulator_desc desc[TPS65218_NUM_REGULATOR];
struct regulator_dev *rdev[TPS65218_NUM_REGULATOR];
struct tps_info *info[TPS65218_NUM_REGULATOR]; struct tps_info *info[TPS65218_NUM_REGULATOR];
struct regmap *regmap; struct regmap *regmap;
}; };

View File

@ -28,6 +28,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/mfd/core.h> #include <linux/mfd/core.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/clk.h>
#define TWL6040_REG_ASICID 0x01 #define TWL6040_REG_ASICID 0x01
#define TWL6040_REG_ASICREV 0x02 #define TWL6040_REG_ASICREV 0x02
@ -157,6 +158,7 @@
#define TWL6040_I2CSEL 0x01 #define TWL6040_I2CSEL 0x01
#define TWL6040_RESETSPLIT 0x04 #define TWL6040_RESETSPLIT 0x04
#define TWL6040_INTCLRMODE 0x08 #define TWL6040_INTCLRMODE 0x08
#define TWL6040_I2CMODE(x) ((x & 0x3) << 4)
/* STATUS (0x2E) fields */ /* STATUS (0x2E) fields */
@ -222,6 +224,7 @@ struct twl6040 {
struct regmap *regmap; struct regmap *regmap;
struct regmap_irq_chip_data *irq_data; struct regmap_irq_chip_data *irq_data;
struct regulator_bulk_data supplies[2]; /* supplies for vio, v2v1 */ struct regulator_bulk_data supplies[2]; /* supplies for vio, v2v1 */
struct clk *clk32k;
struct mutex mutex; struct mutex mutex;
struct mutex irq_mutex; struct mutex irq_mutex;
struct mfd_cell cells[TWL6040_CELLS]; struct mfd_cell cells[TWL6040_CELLS];