forked from Minki/linux
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:
commit
1fe9eb1847
39
Documentation/devicetree/bindings/i2c/i2c-cros-ec-tunnel.txt
Normal file
39
Documentation/devicetree/bindings/i2c/i2c-cros-ec-tunnel.txt
Normal 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>;
|
||||
};
|
||||
};
|
||||
}
|
59
Documentation/devicetree/bindings/mfd/sun6i-prcm.txt
Normal file
59
Documentation/devicetree/bindings/mfd/sun6i-prcm.txt
Normal 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>;
|
||||
};
|
||||
};
|
@ -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>;
|
||||
};
|
@ -19,6 +19,8 @@ Required properties:
|
||||
|
||||
Optional properties, nodes:
|
||||
- enable-active-high: To power on the twl6040 during boot.
|
||||
- clocks: phandle to the clk32k clock provider
|
||||
- clock-names: Must be "clk32k"
|
||||
|
||||
Vibra functionality
|
||||
Required properties:
|
||||
|
@ -23,7 +23,8 @@
|
||||
enum { REG_RE, REG_FE, REG_IE };
|
||||
|
||||
#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 gpio_chip chip;
|
||||
@ -31,8 +32,6 @@ struct stmpe_gpio {
|
||||
struct device *dev;
|
||||
struct mutex irq_lock;
|
||||
struct irq_domain *domain;
|
||||
|
||||
int irq_base;
|
||||
unsigned norequest_mask;
|
||||
|
||||
/* 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,
|
||||
struct device_node *np)
|
||||
{
|
||||
int base = 0;
|
||||
|
||||
if (!np)
|
||||
base = stmpe_gpio->irq_base;
|
||||
|
||||
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);
|
||||
if (!stmpe_gpio->domain) {
|
||||
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
|
||||
stmpe_gpio->chip.of_node = np;
|
||||
#endif
|
||||
stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1;
|
||||
stmpe_gpio->chip.base = -1;
|
||||
|
||||
if (pdata)
|
||||
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",
|
||||
&stmpe_gpio->norequest_mask);
|
||||
|
||||
if (irq >= 0)
|
||||
stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0);
|
||||
else
|
||||
if (irq < 0)
|
||||
dev_info(&pdev->dev,
|
||||
"device configured in no-irq mode; "
|
||||
"irqs are not available\n");
|
||||
|
@ -993,6 +993,15 @@ config I2C_SIBYTE
|
||||
help
|
||||
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
|
||||
tristate "NatSemi SCx200 I2C using GPIO pins (DEPRECATED)"
|
||||
depends on SCx200_GPIO
|
||||
|
@ -95,6 +95,7 @@ obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o
|
||||
# Other I2C/SMBus bus drivers
|
||||
obj-$(CONFIG_I2C_ACORN) += i2c-acorn.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_PCA_ISA) += i2c-pca-isa.o
|
||||
obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
|
||||
|
318
drivers/i2c/busses/i2c-cros-ec-tunnel.c
Normal file
318
drivers/i2c/busses/i2c-cros-ec-tunnel.c
Normal 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 = ¶ms->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");
|
@ -52,3 +52,13 @@ config MEMSTICK_REALTEK_PCI
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
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.
|
||||
|
@ -6,3 +6,4 @@ obj-$(CONFIG_MEMSTICK_TIFM_MS) += tifm_ms.o
|
||||
obj-$(CONFIG_MEMSTICK_JMICRON_38X) += jmb38x_ms.o
|
||||
obj-$(CONFIG_MEMSTICK_R592) += r592.o
|
||||
obj-$(CONFIG_MEMSTICK_REALTEK_PCI) += rtsx_pci_ms.o
|
||||
obj-$(CONFIG_MEMSTICK_REALTEK_USB) += rtsx_usb_ms.o
|
||||
|
839
drivers/memstick/host/rtsx_usb_ms.c
Normal file
839
drivers/memstick/host/rtsx_usb_ms.c
Normal 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");
|
@ -67,6 +67,18 @@ config MFD_BCM590XX
|
||||
help
|
||||
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
|
||||
tristate "ChromeOS Embedded Controller"
|
||||
select MFD_CORE
|
||||
@ -250,6 +262,16 @@ config MFD_INTEL_MSIC
|
||||
Passage) chip. This chip embeds audio, battery, GPIO, etc.
|
||||
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
|
||||
tristate "Janz CMOD-IO PCI MODULbus Carrier Board"
|
||||
select MFD_CORE
|
||||
@ -675,6 +697,7 @@ config MFD_DB8500_PRCMU
|
||||
config MFD_STMPE
|
||||
bool "STMicroelectronics STMPE"
|
||||
depends on (I2C=y || SPI_MASTER=y)
|
||||
depends on OF
|
||||
select MFD_CORE
|
||||
help
|
||||
Support for the STMPE family of I/O Expanders from
|
||||
@ -719,6 +742,14 @@ config MFD_STA2X11
|
||||
select MFD_CORE
|
||||
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
|
||||
bool "System Controller Register R/W Based on Regmap"
|
||||
select REGMAP_MMIO
|
||||
|
@ -29,6 +29,7 @@ obj-$(CONFIG_MFD_STA2X11) += sta2x11-mfd.o
|
||||
obj-$(CONFIG_MFD_STMPE) += stmpe.o
|
||||
obj-$(CONFIG_STMPE_I2C) += stmpe-i2c.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_T7L66XB) += t7l66xb.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_MFD_DA9052_SPI) += da9052-spi.o
|
||||
obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
|
||||
obj-$(CONFIG_MFD_AXP20X) += axp20x.o
|
||||
|
||||
obj-$(CONFIG_MFD_LP3943) += lp3943.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_AS3722) += as3722.o
|
||||
obj-$(CONFIG_MFD_STW481X) += stw481x.o
|
||||
obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o
|
||||
|
@ -151,22 +151,6 @@ int abx500_startup_irq_enabled(struct device *dev, unsigned int irq)
|
||||
}
|
||||
EXPORT_SYMBOL(abx500_startup_irq_enabled);
|
||||
|
||||
void abx500_dump_all_banks(void)
|
||||
{
|
||||
struct abx500_ops *ops;
|
||||
struct device dummy_child = {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_DESCRIPTION("ABX500 core driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -583,6 +583,7 @@ static const char *wm5102_supplies[] = {
|
||||
"CPVDD",
|
||||
"SPKVDDL",
|
||||
"SPKVDDR",
|
||||
"MICVDD",
|
||||
};
|
||||
|
||||
static const struct mfd_cell wm5102_devs[] = {
|
||||
|
@ -285,7 +285,7 @@ int arizona_irq_init(struct arizona *arizona)
|
||||
IRQF_ONESHOT, -1, irq,
|
||||
&arizona->irq_chip);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -114,7 +114,7 @@ static const struct regmap_config as3711_regmap_config = {
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id as3711_of_match[] = {
|
||||
static const struct of_device_id as3711_of_match[] = {
|
||||
{.compatible = "ams,as3711",},
|
||||
{}
|
||||
};
|
||||
|
258
drivers/mfd/axp20x.c
Normal file
258
drivers/mfd/axp20x.c
Normal 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");
|
@ -96,6 +96,12 @@ err:
|
||||
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[] = {
|
||||
{ .compatible = "brcm,bcm59056" },
|
||||
{ }
|
||||
@ -115,6 +121,7 @@ static struct i2c_driver bcm590xx_i2c_driver = {
|
||||
.of_match_table = of_match_ptr(bcm590xx_of_match),
|
||||
},
|
||||
.probe = bcm590xx_i2c_probe,
|
||||
.remove = bcm590xx_i2c_remove,
|
||||
.id_table = bcm590xx_i2c_id,
|
||||
};
|
||||
module_i2c_driver(bcm590xx_i2c_driver);
|
||||
@ -122,4 +129,4 @@ module_i2c_driver(bcm590xx_i2c_driver);
|
||||
MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
|
||||
MODULE_DESCRIPTION("BCM590xx multi-function driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:bcm590xx");
|
||||
MODULE_ALIAS("i2c:bcm590xx");
|
||||
|
@ -30,7 +30,7 @@ int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
|
||||
uint8_t *out;
|
||||
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[0] = EC_CMD_VERSION0 + msg->version;
|
||||
out[1] = msg->cmd;
|
||||
@ -90,6 +90,11 @@ static const struct mfd_cell cros_devs[] = {
|
||||
.id = 1,
|
||||
.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)
|
||||
@ -184,3 +189,6 @@ int cros_ec_resume(struct cros_ec_device *ec_dev)
|
||||
EXPORT_SYMBOL(cros_ec_resume);
|
||||
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("ChromeOS EC core driver");
|
||||
|
@ -39,14 +39,22 @@
|
||||
#define EC_MSG_PREAMBLE_COUNT 32
|
||||
|
||||
/*
|
||||
* We must get a response from the EC in 5ms. This is a very long
|
||||
* time, but the flash write command can take 2-3ms. The EC command
|
||||
* processing is currently not very fast (about 500us). We could
|
||||
* look at speeding this up and making the flash write command a
|
||||
* 'slow' command, requiring a GET_STATUS wait loop, like flash
|
||||
* erase.
|
||||
*/
|
||||
#define EC_MSG_DEADLINE_MS 5
|
||||
* Allow for a long time for the EC to respond. We support i2c
|
||||
* tunneling and support fairly long messages for the tunnel (249
|
||||
* bytes long at the moment). If we're talking to a 100 kHz device
|
||||
* on the other end and need to transfer ~256 bytes, then we need:
|
||||
* 10 us/bit * ~10 bits/byte * ~256 bytes = ~25ms
|
||||
*
|
||||
* We'll wait 4 times that to handle clock stretching and other
|
||||
* 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
|
||||
@ -65,11 +73,13 @@
|
||||
* if no record
|
||||
* @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.
|
||||
* @lock: mutex to ensure only one user of cros_ec_command_spi_xfer at a time
|
||||
*/
|
||||
struct cros_ec_spi {
|
||||
struct spi_device *spi;
|
||||
s64 last_transfer_ns;
|
||||
unsigned int end_of_msg_delay;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
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 */
|
||||
deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS);
|
||||
do {
|
||||
while (true) {
|
||||
unsigned long start_jiffies = jiffies;
|
||||
|
||||
memset(&trans, 0, sizeof(trans));
|
||||
trans.cs_change = 1;
|
||||
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;
|
||||
}
|
||||
}
|
||||
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");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
} while (ptr == end);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
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);
|
||||
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);
|
||||
delay = timespec_to_ns(&ts) - ec_spi->last_transfer_ns;
|
||||
if (delay < EC_SPI_RECOVERY_TIME_NS)
|
||||
ndelay(delay);
|
||||
ndelay(EC_SPI_RECOVERY_TIME_NS - delay);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
if (ret < 0) {
|
||||
dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
|
||||
return ret;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* 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",
|
||||
ec_msg->cmd, ptr[0]);
|
||||
debug_packet(ec_dev->dev, "in_err", ptr, len);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
len = ptr[1];
|
||||
sum = ptr[0] + ptr[1];
|
||||
if (len > ec_msg->in_len) {
|
||||
dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
|
||||
len, ec_msg->in_len);
|
||||
return -ENOSPC;
|
||||
ret = -ENOSPC;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
"bad packet checksum, expected %02x, got %02x\n",
|
||||
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)
|
||||
@ -327,6 +359,7 @@ static int cros_ec_spi_probe(struct spi_device *spi)
|
||||
if (ec_spi == NULL)
|
||||
return -ENOMEM;
|
||||
ec_spi->spi = spi;
|
||||
mutex_init(&ec_spi->lock);
|
||||
ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
|
||||
if (!ec_dev)
|
||||
return -ENOMEM;
|
||||
|
@ -2300,9 +2300,6 @@ int prcmu_ac_wake_req(void)
|
||||
|
||||
if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
|
||||
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",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
@ -3112,7 +3109,7 @@ static int db8500_prcmu_register_ab8500(struct device *parent,
|
||||
{
|
||||
struct device_node *np;
|
||||
struct resource ab8500_resource;
|
||||
struct mfd_cell ab8500_cell = {
|
||||
const struct mfd_cell ab8500_cell = {
|
||||
.name = "ab8500-core",
|
||||
.of_compatible = "stericsson,ab8500",
|
||||
.id = AB8500_VERSION_AB8500,
|
||||
|
482
drivers/mfd/ipaq-micro.c
Normal file
482
drivers/mfd/ipaq-micro.c
Normal 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 = µ->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(µ->lock, flags);
|
||||
if (micro->msg) {
|
||||
list_add_tail(&msg->node, µ->queue);
|
||||
spin_unlock_irqrestore(µ->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
micro->msg = msg;
|
||||
ipaq_micro_trigger_tx(micro);
|
||||
spin_unlock_irqrestore(µ->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(µ->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(µ->msg->ack);
|
||||
if (!list_empty(µ->queue)) {
|
||||
micro->msg = list_entry(micro->queue.next,
|
||||
struct ipaq_micro_msg,
|
||||
node);
|
||||
list_del_init(µ->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(µ->lock);
|
||||
}
|
||||
|
||||
static void micro_process_char(struct ipaq_micro *micro, u8 ch)
|
||||
{
|
||||
struct ipaq_micro_rxdev *rx = µ->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 = µ->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 = µ->rx;
|
||||
u32 val;
|
||||
|
||||
if (micro->msg)
|
||||
complete(µ->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 = µ->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(µ->lock);
|
||||
INIT_LIST_HEAD(µ->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 = µ_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");
|
@ -86,7 +86,7 @@ enum kempld_cells {
|
||||
KEMPLD_UART,
|
||||
};
|
||||
|
||||
static struct mfd_cell kempld_devs[] = {
|
||||
static const struct mfd_cell kempld_devs[] = {
|
||||
[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)
|
||||
{
|
||||
int ret;
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
char *version_type;
|
||||
u8 index_reg;
|
||||
int ret;
|
||||
|
||||
@ -335,27 +426,19 @@ static int kempld_detect_device(struct kempld_device_data *pld)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
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";
|
||||
}
|
||||
dev_info(pld->dev, "Found Kontron PLD - %s (%s), spec %d.%d\n",
|
||||
pld->info.version, kempld_get_type_string(pld),
|
||||
pld->info.spec_major, pld->info.spec_minor);
|
||||
|
||||
dev_info(pld->dev, "Found Kontron PLD %d\n", pld->info.number);
|
||||
dev_info(pld->dev, "%s version %d.%d build %d, specification %d.%d\n",
|
||||
version_type, pld->info.major, pld->info.minor,
|
||||
pld->info.buildnr, pld->info.spec_major,
|
||||
pld->info.spec_minor);
|
||||
ret = sysfs_create_group(&pld->dev->kobj, &pld_attr_group);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
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)
|
||||
@ -399,6 +482,8 @@ static int kempld_remove(struct platform_device *pdev)
|
||||
struct kempld_device_data *pld = platform_get_drvdata(pdev);
|
||||
struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
|
||||
|
||||
sysfs_remove_group(&pld->dev->kobj, &pld_attr_group);
|
||||
|
||||
mfd_remove_devices(&pdev->dev);
|
||||
pdata->release_hardware_mutex(pld);
|
||||
|
||||
|
@ -62,7 +62,7 @@ static const struct lp3943_reg_cfg lp3943_mux_cfg[] = {
|
||||
{ LP3943_REG_MUX3, 0xC0, 6 },
|
||||
};
|
||||
|
||||
static struct mfd_cell lp3943_devs[] = {
|
||||
static const struct mfd_cell lp3943_devs[] = {
|
||||
{
|
||||
.name = "lp3943-pwm",
|
||||
.of_compatible = "ti,lp3943-pwm",
|
||||
|
@ -488,6 +488,7 @@ static struct lpc_ich_info lpc_chipset_info[] = {
|
||||
[LPC_PPT] = {
|
||||
.name = "Panther Point",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_LPT] = {
|
||||
.name = "Lynx Point",
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include <linux/mfd/max14577.h>
|
||||
#include <linux/mfd/max14577-private.h>
|
||||
|
||||
static struct mfd_cell max14577_devs[] = {
|
||||
static const struct mfd_cell max14577_devs[] = {
|
||||
{
|
||||
.name = "max14577-muic",
|
||||
.of_compatible = "maxim,max14577-muic",
|
||||
@ -38,7 +38,7 @@ static struct mfd_cell max14577_devs[] = {
|
||||
{ .name = "max14577-charger", },
|
||||
};
|
||||
|
||||
static struct mfd_cell max77836_devs[] = {
|
||||
static const struct mfd_cell max77836_devs[] = {
|
||||
{
|
||||
.name = "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",
|
||||
.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;
|
||||
int ret = 0;
|
||||
const struct regmap_irq_chip *irq_chip;
|
||||
struct mfd_cell *mfd_devs;
|
||||
const struct mfd_cell *mfd_devs;
|
||||
unsigned int mfd_devs_size;
|
||||
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);
|
||||
if (of_id)
|
||||
max14577->dev_type = (unsigned int)of_id->data;
|
||||
max14577->dev_type =
|
||||
(enum maxim_device_type)of_id->data;
|
||||
} else {
|
||||
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 max14577 *max14577 = i2c_get_clientdata(i2c);
|
||||
|
||||
if (device_may_wakeup(dev)) {
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(max14577->irq);
|
||||
/*
|
||||
* MUIC IRQ must be disabled during suspend if this is
|
||||
* a wake up source because it will be handled before
|
||||
* resuming I2C.
|
||||
*
|
||||
* When device is woken up from suspend (e.g. by ADC change),
|
||||
* an interrupt occurs before resuming I2C bus controller.
|
||||
* Interrupt handler tries to read registers but this read
|
||||
* will fail because I2C is still suspended.
|
||||
*/
|
||||
disable_irq(max14577->irq);
|
||||
}
|
||||
/*
|
||||
* MUIC IRQ must be disabled during suspend because if it happens
|
||||
* while suspended it will be handled before resuming I2C.
|
||||
*
|
||||
* When device is woken up from suspend (e.g. by ADC change),
|
||||
* an interrupt occurs before resuming I2C bus controller.
|
||||
* Interrupt handler tries to read registers but this read
|
||||
* will fail because I2C is still suspended.
|
||||
*/
|
||||
disable_irq(max14577->irq);
|
||||
|
||||
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 max14577 *max14577 = i2c_get_clientdata(i2c);
|
||||
|
||||
if (device_may_wakeup(dev)) {
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(max14577->irq);
|
||||
enable_irq(max14577->irq);
|
||||
}
|
||||
enable_irq(max14577->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ static struct regmap_config max77686_regmap_config = {
|
||||
};
|
||||
|
||||
#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},
|
||||
{},
|
||||
};
|
||||
|
@ -243,7 +243,7 @@ static const struct dev_pm_ops max77693_pm = {
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id max77693_dt_match[] = {
|
||||
static const struct of_device_id max77693_dt_match[] = {
|
||||
{ .compatible = "maxim,max77693" },
|
||||
{},
|
||||
};
|
||||
|
@ -305,7 +305,7 @@ static int max8907_i2c_remove(struct i2c_client *i2c)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id max8907_of_match[] = {
|
||||
static const struct of_device_id max8907_of_match[] = {
|
||||
{ .compatible = "maxim,max8907" },
|
||||
{ },
|
||||
};
|
||||
|
@ -51,7 +51,7 @@ static const struct mfd_cell max8997_devs[] = {
|
||||
};
|
||||
|
||||
#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 },
|
||||
{},
|
||||
};
|
||||
|
@ -132,7 +132,7 @@ int max8998_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
|
||||
EXPORT_SYMBOL(max8998_update_reg);
|
||||
|
||||
#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 = "national,lp3974", .data = (void *)TYPE_LP3974 },
|
||||
{ .compatible = "ti,lp3974", .data = (void *)TYPE_LP3974 },
|
||||
|
@ -660,34 +660,22 @@ int mc13xxx_common_init(struct device *dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_init(&mc13xxx->lock);
|
||||
|
||||
ret = request_threaded_irq(mc13xxx->irq, NULL, mc13xxx_irq_thread,
|
||||
IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_init(&mc13xxx->lock);
|
||||
|
||||
if (mc13xxx_probe_flags_dt(mc13xxx) < 0 && pdata)
|
||||
mc13xxx->flags = pdata->flags;
|
||||
|
||||
if (mc13xxx->flags & MC13XXX_USE_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)
|
||||
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) {
|
||||
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
|
||||
&pdata->regulators, sizeof(pdata->regulators));
|
||||
@ -695,10 +683,20 @@ int mc13xxx_common_init(struct device *dev)
|
||||
pdata->leds, sizeof(*pdata->leds));
|
||||
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-pwrbutton",
|
||||
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 {
|
||||
mc13xxx_add_subdevice(mc13xxx, "%s-regulator");
|
||||
mc13xxx_add_subdevice(mc13xxx, "%s-led");
|
||||
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;
|
||||
|
@ -1287,29 +1287,8 @@ static struct i2c_driver menelaus_i2c_driver = {
|
||||
.id_table = menelaus_id,
|
||||
};
|
||||
|
||||
static int __init menelaus_init(void)
|
||||
{
|
||||
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_i2c_driver(menelaus_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Texas Instruments, Inc. (and others)");
|
||||
MODULE_DESCRIPTION("I2C interface for Menelaus.");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(menelaus_init);
|
||||
module_exit(menelaus_exit);
|
||||
|
@ -102,7 +102,7 @@ static int mfd_add_device(struct device *parent, int id,
|
||||
pdev->dev.dma_mask = parent->dma_mask;
|
||||
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,
|
||||
parent, cell->parent_supplies,
|
||||
cell->num_parent_supplies);
|
||||
@ -182,9 +182,9 @@ static int mfd_add_device(struct device *parent, int id,
|
||||
return 0;
|
||||
|
||||
fail_alias:
|
||||
devm_regulator_bulk_unregister_supply_alias(&pdev->dev,
|
||||
cell->parent_supplies,
|
||||
cell->num_parent_supplies);
|
||||
regulator_bulk_unregister_supply_alias(&pdev->dev,
|
||||
cell->parent_supplies,
|
||||
cell->num_parent_supplies);
|
||||
fail_res:
|
||||
kfree(res);
|
||||
fail_device:
|
||||
@ -238,6 +238,9 @@ static int mfd_remove_devices_fn(struct device *dev, void *c)
|
||||
pdev = to_platform_device(dev);
|
||||
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) */
|
||||
if (!*usage_count || (cell->usage_count < *usage_count))
|
||||
*usage_count = cell->usage_count;
|
||||
|
@ -557,7 +557,7 @@ static int usbhs_omap_get_dt_pdata(struct device *dev,
|
||||
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-ohci", },
|
||||
{ }
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/pm8xxx/core.h>
|
||||
|
||||
#define SSBI_REG_ADDR_IRQ_BASE 0x1BB
|
||||
|
||||
@ -57,7 +56,6 @@
|
||||
#define PM8921_NR_IRQS 256
|
||||
|
||||
struct pm_irq_chip {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
spinlock_t pm_irq_lock;
|
||||
struct irq_domain *irqdomain;
|
||||
@ -67,11 +65,6 @@ struct pm_irq_chip {
|
||||
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,
|
||||
unsigned int *ip)
|
||||
{
|
||||
@ -255,55 +248,6 @@ static struct irq_chip pm8xxx_irq_chip = {
|
||||
.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,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
@ -324,56 +268,6 @@ static const struct irq_domain_ops pm8xxx_irq_domain_ops = {
|
||||
.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 = {
|
||||
.reg_bits = 16,
|
||||
.val_bits = 8,
|
||||
@ -392,7 +286,6 @@ MODULE_DEVICE_TABLE(of, pm8921_id_table);
|
||||
|
||||
static int pm8921_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pm8921 *pmic;
|
||||
struct regmap *regmap;
|
||||
int irq, rc;
|
||||
unsigned int val;
|
||||
@ -404,12 +297,6 @@ static int pm8921_probe(struct platform_device *pdev)
|
||||
if (irq < 0)
|
||||
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,
|
||||
&ssbi_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
@ -434,18 +321,13 @@ static int pm8921_probe(struct platform_device *pdev)
|
||||
pr_info("PMIC revision 2: %02X\n", val);
|
||||
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) +
|
||||
sizeof(chip->config[0]) * nirqs,
|
||||
GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
pmic->irq_chip = chip;
|
||||
chip->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, chip);
|
||||
chip->regmap = regmap;
|
||||
chip->num_irqs = nirqs;
|
||||
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)
|
||||
{
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
struct pm8921 *pmic = pm8921_drvdata.pm_chip_data;
|
||||
struct pm_irq_chip *chip = pmic->irq_chip;
|
||||
struct pm_irq_chip *chip = platform_get_drvdata(pdev);
|
||||
|
||||
device_for_each_child(&pdev->dev, NULL, pm8921_remove_child);
|
||||
irq_set_chained_handler(irq, NULL);
|
||||
|
@ -38,7 +38,7 @@ static struct resource rdc321x_wdt_resource[] = {
|
||||
};
|
||||
|
||||
static struct rdc321x_gpio_pdata rdc321x_gpio_pdata = {
|
||||
.max_gpios = RDC321X_MAX_GPIO,
|
||||
.max_gpios = RDC321X_NUM_GPIO,
|
||||
};
|
||||
|
||||
static struct resource rdc321x_gpio_resources[] = {
|
||||
|
@ -29,7 +29,7 @@ static int polling_pipe = 1;
|
||||
module_param(polling_pipe, int, S_IRUGO | S_IWUSR);
|
||||
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] = {
|
||||
.name = "rtsx_usb_sdmmc",
|
||||
.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);
|
||||
add_timer(&ucr->sg_timer);
|
||||
usb_sg_wait(&ucr->current_sg);
|
||||
del_timer(&ucr->sg_timer);
|
||||
del_timer_sync(&ucr->sg_timer);
|
||||
|
||||
if (act_len)
|
||||
*act_len = ucr->current_sg.bytes;
|
||||
@ -644,14 +644,14 @@ static int rtsx_usb_probe(struct usb_interface *intf,
|
||||
if (ret)
|
||||
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,
|
||||
ARRAY_SIZE(rtsx_usb_cells), NULL, 0, NULL);
|
||||
if (ret)
|
||||
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
|
||||
intf->needs_remote_wakeup = 1;
|
||||
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",
|
||||
__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);
|
||||
rtsx_usb_turn_off_led(ucr);
|
||||
mutex_unlock(&ucr->dev_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/samsung/core.h>
|
||||
#include <linux/mfd/samsung/irq.h>
|
||||
#include <linux/mfd/samsung/rtc.h>
|
||||
#include <linux/mfd/samsung/s2mpa01.h>
|
||||
#include <linux/mfd/samsung/s2mps11.h>
|
||||
#include <linux/mfd/samsung/s2mps14.h>
|
||||
@ -91,7 +90,7 @@ static const struct mfd_cell s2mpa01_devs[] = {
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id sec_dt_match[] = {
|
||||
static const struct of_device_id sec_dt_match[] = {
|
||||
{ .compatible = "samsung,s5m8767-pmic",
|
||||
.data = (void *)S5M8767X,
|
||||
}, {
|
||||
@ -196,20 +195,6 @@ static const struct regmap_config s5m8767_regmap_config = {
|
||||
.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
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
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;
|
||||
unsigned long device_type;
|
||||
int ret;
|
||||
|
||||
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->i2c = i2c;
|
||||
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) {
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
pdata->device_type = sec_pmic->type;
|
||||
pdata->device_type = device_type;
|
||||
}
|
||||
if (pdata) {
|
||||
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) {
|
||||
case S2MPA01:
|
||||
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;
|
||||
case S2MPS11X:
|
||||
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;
|
||||
case S2MPS14X:
|
||||
regmap = &s2mps14_regmap_config;
|
||||
regmap_rtc = &s2mps14_rtc_regmap_config;
|
||||
break;
|
||||
case S5M8763X:
|
||||
regmap = &s5m8763_regmap_config;
|
||||
regmap_rtc = &s5m_rtc_regmap_config;
|
||||
break;
|
||||
case S5M8767X:
|
||||
regmap = &s5m8767_regmap_config;
|
||||
regmap_rtc = &s5m_rtc_regmap_config;
|
||||
break;
|
||||
default:
|
||||
regmap = &sec_regmap_config;
|
||||
regmap_rtc = &s5m_rtc_regmap_config;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -342,21 +310,6 @@ static int sec_pmic_probe(struct i2c_client *i2c,
|
||||
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)
|
||||
pdata->cfg_pmic_irq();
|
||||
|
||||
@ -403,8 +356,6 @@ static int sec_pmic_probe(struct i2c_client *i2c,
|
||||
|
||||
err_mfd:
|
||||
sec_irq_exit(sec_pmic);
|
||||
err_regmap_rtc:
|
||||
i2c_unregister_device(sec_pmic->rtc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -414,7 +365,6 @@ static int sec_pmic_remove(struct i2c_client *i2c)
|
||||
|
||||
mfd_remove_devices(sec_pmic->dev);
|
||||
sec_irq_exit(sec_pmic);
|
||||
i2c_unregister_device(sec_pmic->rtc);
|
||||
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 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);
|
||||
/*
|
||||
* PMIC IRQ must be disabled during suspend for RTC alarm
|
||||
* to work properly.
|
||||
* When device is woken up from suspend by RTC Alarm, an
|
||||
* interrupt occurs before resuming I2C bus controller.
|
||||
* The interrupt is handled by regmap_irq_thread which tries
|
||||
* to read RTC registers. This read fails (I2C is still
|
||||
* suspended) and RTC Alarm interrupt is disabled.
|
||||
*/
|
||||
disable_irq(sec_pmic->irq);
|
||||
}
|
||||
/*
|
||||
* PMIC IRQ must be disabled during suspend for RTC alarm
|
||||
* to work properly.
|
||||
* When device is woken up from suspend, an
|
||||
* interrupt occurs before resuming I2C bus controller.
|
||||
* The interrupt is handled by regmap_irq_thread which tries
|
||||
* to read RTC registers. This read fails (I2C is still
|
||||
* suspended) and RTC Alarm interrupt is disabled.
|
||||
*/
|
||||
disable_irq(sec_pmic->irq);
|
||||
|
||||
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 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);
|
||||
enable_irq(sec_pmic->irq);
|
||||
}
|
||||
enable_irq(sec_pmic->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -385,7 +385,7 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic)
|
||||
&sec_pmic->irq_data);
|
||||
break;
|
||||
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);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -1726,7 +1726,7 @@ static struct pci_driver sm501_pci_driver = {
|
||||
|
||||
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", },
|
||||
{ /* end */ }
|
||||
};
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/of_device.h>
|
||||
#include "stmpe.h"
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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
|
||||
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.irq = i2c->irq;
|
||||
i2c_ci.client = i2c;
|
||||
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)
|
||||
@ -89,6 +116,7 @@ static struct i2c_driver stmpe_i2c_driver = {
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &stmpe_dev_pm_ops,
|
||||
#endif
|
||||
.of_match_table = stmpe_of_match,
|
||||
},
|
||||
.probe = stmpe_i2c_probe,
|
||||
.remove = stmpe_i2c_remove,
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include "stmpe.h"
|
||||
|
||||
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)
|
||||
mask |= STMPE1601_SYS_CTRL_ENABLE_GPIO;
|
||||
else
|
||||
mask &= ~STMPE1601_SYS_CTRL_ENABLE_GPIO;
|
||||
|
||||
if (blocks & STMPE_BLOCK_KEYPAD)
|
||||
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,
|
||||
enable ? mask : 0);
|
||||
@ -986,9 +996,6 @@ static int stmpe_irq_init(struct stmpe *stmpe, struct device_node *np)
|
||||
int base = 0;
|
||||
int num_irqs = stmpe->variant->num_irqs;
|
||||
|
||||
if (!np)
|
||||
base = stmpe->irq_base;
|
||||
|
||||
stmpe->domain = irq_domain_add_simple(np, num_irqs, base,
|
||||
&stmpe_irq_ops, stmpe);
|
||||
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)
|
||||
{
|
||||
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)
|
||||
@ -1171,12 +1178,23 @@ int stmpe_probe(struct stmpe_client_info *ci, int partnum)
|
||||
stmpe->dev = ci->dev;
|
||||
stmpe->client = ci->client;
|
||||
stmpe->pdata = pdata;
|
||||
stmpe->irq_base = pdata->irq_base;
|
||||
stmpe->ci = ci;
|
||||
stmpe->partnum = partnum;
|
||||
stmpe->variant = stmpe_variant_info[partnum];
|
||||
stmpe->regs = stmpe->variant->regs;
|
||||
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);
|
||||
|
||||
if (ci->init)
|
||||
@ -1243,6 +1261,11 @@ int stmpe_probe(struct stmpe_client_info *ci, int partnum)
|
||||
|
||||
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);
|
||||
|
||||
return 0;
|
||||
|
@ -192,7 +192,7 @@ int stmpe_remove(struct stmpe *stmpe);
|
||||
|
||||
#define STMPE1601_SYS_CTRL_ENABLE_GPIO (1 << 3)
|
||||
#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 */
|
||||
#define STMPE1601_AUTOSLEEP_TIMEOUT_MASK (0x7)
|
||||
|
134
drivers/mfd/sun6i-prcm.c
Normal file
134
drivers/mfd/sun6i-prcm.c
Normal 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");
|
@ -95,7 +95,11 @@ struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
|
||||
struct device_node *syscon_np;
|
||||
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)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
|
@ -119,7 +119,7 @@ static const struct i2c_device_id tps6507x_i2c_id[] = {
|
||||
MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id tps6507x_of_match[] = {
|
||||
static const struct of_device_id tps6507x_of_match[] = {
|
||||
{.compatible = "ti,tps6507x", },
|
||||
{},
|
||||
};
|
||||
|
@ -197,6 +197,7 @@ static struct regmap_irq_chip tps65218_irq_chip = {
|
||||
|
||||
static const struct of_device_id of_tps65218_match_table[] = {
|
||||
{ .compatible = "ti,tps65218", },
|
||||
{}
|
||||
};
|
||||
|
||||
static int tps65218_probe(struct i2c_client *client,
|
||||
|
@ -444,7 +444,7 @@ static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *clien
|
||||
return pdata;
|
||||
}
|
||||
|
||||
static struct of_device_id tps6586x_of_match[] = {
|
||||
static const struct of_device_id tps6586x_of_match[] = {
|
||||
{ .compatible = "ti,tps6586x", },
|
||||
{ },
|
||||
};
|
||||
|
@ -379,7 +379,7 @@ err_sleep_init:
|
||||
}
|
||||
|
||||
#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,tps65911", .data = (void *)TPS65911},
|
||||
{ },
|
||||
|
@ -87,8 +87,13 @@ static struct reg_default twl6040_defaults[] = {
|
||||
};
|
||||
|
||||
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++)
|
||||
goto out;
|
||||
|
||||
clk_prepare_enable(twl6040->clk32k);
|
||||
|
||||
/* Allow writes to the chip */
|
||||
regcache_cache_only(twl6040->regmap, false);
|
||||
|
||||
@ -341,6 +348,8 @@ int twl6040_power(struct twl6040 *twl6040, int on)
|
||||
|
||||
twl6040->sysclk = 0;
|
||||
twl6040->mclk = 0;
|
||||
|
||||
clk_disable_unprepare(twl6040->clk32k);
|
||||
}
|
||||
|
||||
out:
|
||||
@ -432,12 +441,9 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
|
||||
TWL6040_HPLLENA;
|
||||
break;
|
||||
case 19200000:
|
||||
/*
|
||||
* PLL disabled
|
||||
* (enable PLL if MCLK jitter quality
|
||||
* doesn't meet specification)
|
||||
*/
|
||||
hppllctl |= TWL6040_MCLK_19200KHZ;
|
||||
/* PLL enabled, bypass mode */
|
||||
hppllctl |= TWL6040_MCLK_19200KHZ |
|
||||
TWL6040_HPLLBP | TWL6040_HPLLENA;
|
||||
break;
|
||||
case 26000000:
|
||||
/* PLL enabled, active mode */
|
||||
@ -445,9 +451,9 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
|
||||
TWL6040_HPLLENA;
|
||||
break;
|
||||
case 38400000:
|
||||
/* PLL enabled, active mode */
|
||||
/* PLL enabled, bypass mode */
|
||||
hppllctl |= TWL6040_MCLK_38400KHZ |
|
||||
TWL6040_HPLLENA;
|
||||
TWL6040_HPLLBP | TWL6040_HPLLENA;
|
||||
break;
|
||||
default:
|
||||
dev_err(twl6040->dev,
|
||||
@ -639,6 +645,12 @@ static int twl6040_probe(struct i2c_client *client,
|
||||
|
||||
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[1].supply = "v2v1";
|
||||
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);
|
||||
init_completion(&twl6040->ready);
|
||||
|
||||
regmap_register_patch(twl6040->regmap, twl6040_patch,
|
||||
ARRAY_SIZE(twl6040_patch));
|
||||
|
||||
twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
|
||||
if (twl6040->rev < 0) {
|
||||
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");
|
||||
if (ret)
|
||||
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,
|
||||
@ -707,10 +725,6 @@ static int twl6040_probe(struct i2c_client *client,
|
||||
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.
|
||||
* We can add the ASoC codec child whenever this driver has been loaded.
|
||||
|
@ -333,7 +333,7 @@ static const struct reg_default wm5102_reg_default[] = {
|
||||
{ 0x000001AA, 0x0004 }, /* R426 - FLL2 GPIO Clock */
|
||||
{ 0x00000200, 0x0006 }, /* R512 - Mic Charge Pump 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 */
|
||||
{ 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */
|
||||
{ 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_5:
|
||||
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_HAPTICS_CONTROL_1:
|
||||
case ARIZONA_HAPTICS_CONTROL_2:
|
||||
|
@ -468,10 +468,12 @@ static const struct reg_default wm5110_reg_default[] = {
|
||||
{ 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */
|
||||
{ 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */
|
||||
{ 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */
|
||||
{ 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 1 */
|
||||
{ 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 2 */
|
||||
{ 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 3 */
|
||||
{ 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 4 */
|
||||
{ 0x00000066, 0x01FF }, /* R102 - Always On Triggers Sequence Select 1 */
|
||||
{ 0x00000067, 0x01FF }, /* R103 - Always On Triggers Sequence Select 2 */
|
||||
{ 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 3 */
|
||||
{ 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 */
|
||||
{ 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */
|
||||
{ 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 */
|
||||
{ 0x000002A9, 0x300A }, /* R681 - Mic Detect Level 4 */
|
||||
{ 0x000002C3, 0x0000 }, /* R707 - Mic noise mix control 1 */
|
||||
{ 0x000002CB, 0x0000 }, /* R715 - Isolation control */
|
||||
{ 0x000002D3, 0x0000 }, /* R723 - Jack detect analogue */
|
||||
{ 0x00000300, 0x0000 }, /* R768 - Input Enables */
|
||||
{ 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_3:
|
||||
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_HAPTICS_CONTROL_1:
|
||||
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_4:
|
||||
case ARIZONA_MIC_NOISE_MIX_CONTROL_1:
|
||||
case ARIZONA_ISOLATION_CONTROL:
|
||||
case ARIZONA_JACK_DETECT_ANALOGUE:
|
||||
case ARIZONA_INPUT_ENABLES:
|
||||
case ARIZONA_INPUT_ENABLES_STATUS:
|
||||
|
@ -64,7 +64,7 @@ EXPORT_SYMBOL_GPL(wm8400_block_read);
|
||||
|
||||
static int wm8400_register_codec(struct wm8400 *wm8400)
|
||||
{
|
||||
struct mfd_cell cell = {
|
||||
const struct mfd_cell cell = {
|
||||
.name = "wm8400-codec",
|
||||
.platform_data = wm8400,
|
||||
.pdata_size = sizeof(*wm8400),
|
||||
|
@ -174,10 +174,10 @@ static const struct reg_default wm8997_reg_default[] = {
|
||||
{ 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */
|
||||
{ 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */
|
||||
{ 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */
|
||||
{ 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 1 */
|
||||
{ 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 2 */
|
||||
{ 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 3 */
|
||||
{ 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 4 */
|
||||
{ 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 3 */
|
||||
{ 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 */
|
||||
{ 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */
|
||||
{ 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_3:
|
||||
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_4:
|
||||
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5:
|
||||
case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6:
|
||||
case ARIZONA_COMFORT_NOISE_GENERATOR:
|
||||
case ARIZONA_HAPTICS_CONTROL_1:
|
||||
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_4:
|
||||
case ARIZONA_RATE_ESTIMATOR_5:
|
||||
case ARIZONA_DYNAMIC_FREQUENCY_SCALING_1:
|
||||
case ARIZONA_FLL1_CONTROL_1:
|
||||
case ARIZONA_FLL1_CONTROL_2:
|
||||
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_MIC_CHARGE_PUMP_1:
|
||||
case ARIZONA_LDO1_CONTROL_1:
|
||||
case ARIZONA_LDO1_CONTROL_2:
|
||||
case ARIZONA_LDO2_CONTROL_1:
|
||||
case ARIZONA_MIC_BIAS_CTRL_1:
|
||||
case ARIZONA_MIC_BIAS_CTRL_2:
|
||||
|
@ -694,3 +694,10 @@ config MMC_REALTEK_PCI
|
||||
help
|
||||
Say Y here to include driver code to support SD/MMC card interface
|
||||
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
|
||||
|
@ -52,6 +52,7 @@ obj-$(CONFIG_MMC_USHC) += ushc.o
|
||||
obj-$(CONFIG_MMC_WMT) += wmt-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_CNS3XXX) += sdhci-cns3xxx.o
|
||||
|
1455
drivers/mmc/host/rtsx_usb_sdmmc.c
Normal file
1455
drivers/mmc/host/rtsx_usb_sdmmc.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -40,6 +40,7 @@
|
||||
|
||||
struct s5m_rtc_info {
|
||||
struct device *dev;
|
||||
struct i2c_client *i2c;
|
||||
struct sec_pmic_dev *s5m87xx;
|
||||
struct regmap *regmap;
|
||||
struct rtc_device *rtc_dev;
|
||||
@ -49,6 +50,20 @@ struct s5m_rtc_info {
|
||||
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,
|
||||
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_platform_data *pdata = s5m87xx->pdata;
|
||||
struct s5m_rtc_info *info;
|
||||
const struct regmap_config *regmap_cfg;
|
||||
int ret;
|
||||
|
||||
if (!pdata) {
|
||||
@ -565,9 +581,37 @@ static int s5m_rtc_probe(struct platform_device *pdev)
|
||||
if (!info)
|
||||
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->s5m87xx = s5m87xx;
|
||||
info->regmap = s5m87xx->regmap_rtc;
|
||||
info->device_type = s5m87xx->device_type;
|
||||
info->wtsr_smpl = s5m87xx->wtsr_smpl;
|
||||
|
||||
@ -585,7 +629,7 @@ static int s5m_rtc_probe(struct platform_device *pdev)
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
dev_err(&pdev->dev, "Unsupported device type: %d\n", ret);
|
||||
return ret;
|
||||
goto err;
|
||||
}
|
||||
|
||||
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",
|
||||
&s5m_rtc_ops, THIS_MODULE);
|
||||
|
||||
if (IS_ERR(info->rtc_dev))
|
||||
return PTR_ERR(info->rtc_dev);
|
||||
if (IS_ERR(info->rtc_dev)) {
|
||||
ret = PTR_ERR(info->rtc_dev);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
|
||||
s5m_rtc_alarm_irq, 0, "rtc-alarm0",
|
||||
info);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
|
||||
info->irq, ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
i2c_unregister_device(info->i2c);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -639,6 +692,17 @@ static void s5m_rtc_shutdown(struct platform_device *pdev)
|
||||
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
|
||||
static int s5m_rtc_resume(struct device *dev)
|
||||
{
|
||||
@ -676,6 +740,7 @@ static struct platform_driver s5m_rtc_driver = {
|
||||
.pm = &s5m_rtc_pm_ops,
|
||||
},
|
||||
.probe = s5m_rtc_probe,
|
||||
.remove = s5m_rtc_remove,
|
||||
.shutdown = s5m_rtc_shutdown,
|
||||
.id_table = s5m_rtc_id,
|
||||
};
|
||||
|
@ -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_event_registers_startup_state_get(struct device *dev, u8 *event);
|
||||
int abx500_startup_irq_enabled(struct device *dev, unsigned int irq);
|
||||
void abx500_dump_all_banks(void);
|
||||
|
||||
struct abx500_ops {
|
||||
int (*get_chip_id) (struct device *);
|
||||
|
@ -42,12 +42,14 @@
|
||||
#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2 0x62
|
||||
#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3 0x63
|
||||
#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4 0x64
|
||||
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_1 0x68
|
||||
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2 0x69
|
||||
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3 0x6A
|
||||
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4 0x6B
|
||||
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5 0x6C
|
||||
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6 0x6D
|
||||
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_1 0x66
|
||||
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2 0x67
|
||||
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3 0x68
|
||||
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4 0x69
|
||||
#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5 0x6A
|
||||
#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_HAPTICS_CONTROL_1 0x90
|
||||
#define ARIZONA_HAPTICS_CONTROL_2 0x91
|
||||
|
180
include/linux/mfd/axp20x.h
Normal file
180
include/linux/mfd/axp20x.h
Normal 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 */
|
@ -29,8 +29,8 @@ enum {
|
||||
EC_MSG_RX_PROTO_BYTES = 3,
|
||||
|
||||
/* 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
148
include/linux/mfd/ipaq-micro.h
Normal file
148
include/linux/mfd/ipaq-micro.h
Normal 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_ */
|
@ -51,6 +51,8 @@
|
||||
#define KEMPLD_TYPE_DEBUG 0x1
|
||||
#define KEMPLD_TYPE_CUSTOM 0x2
|
||||
|
||||
#define KEMPLD_VERSION_LEN 10
|
||||
|
||||
/**
|
||||
* struct kempld_info - PLD device information structure
|
||||
* @major: PLD major revision
|
||||
@ -60,6 +62,7 @@
|
||||
* @type: PLD type
|
||||
* @spec_major: PLD FW specification major revision
|
||||
* @spec_minor: PLD FW specification minor revision
|
||||
* @version: PLD version string
|
||||
*/
|
||||
struct kempld_info {
|
||||
unsigned int major;
|
||||
@ -69,6 +72,7 @@ struct kempld_info {
|
||||
unsigned int type;
|
||||
unsigned int spec_major;
|
||||
unsigned int spec_minor;
|
||||
char version[KEMPLD_VERSION_LEN];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -117,10 +117,6 @@ struct mc13xxx_led_platform_data {
|
||||
|
||||
#define MAX_LED_CONTROL_REGS 6
|
||||
|
||||
struct mc13xxx_leds_platform_data {
|
||||
struct mc13xxx_led_platform_data *led;
|
||||
int num_leds;
|
||||
|
||||
/* MC13783 LED Control 0 */
|
||||
#define MC13783_LED_C0_ENABLE (1 << 0)
|
||||
#define MC13783_LED_C0_TRIODE_MD (1 << 7)
|
||||
@ -169,10 +165,13 @@ struct mc13xxx_leds_platform_data {
|
||||
/* MC34708 LED Control 0 */
|
||||
#define MC34708_LED_C0_CURRENT_R(x) (((x) & 0x3) << 9)
|
||||
#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];
|
||||
};
|
||||
|
||||
struct mc13xxx_buttons_platform_data {
|
||||
#define MC13783_BUTTON_DBNC_0MS 0
|
||||
#define MC13783_BUTTON_DBNC_30MS 1
|
||||
#define MC13783_BUTTON_DBNC_150MS 2
|
||||
@ -180,6 +179,8 @@ struct mc13xxx_buttons_platform_data {
|
||||
#define MC13783_BUTTON_ENABLE (1 << 2)
|
||||
#define MC13783_BUTTON_POL_INVERT (1 << 3)
|
||||
#define MC13783_BUTTON_RESET_EN (1 << 4)
|
||||
|
||||
struct mc13xxx_buttons_platform_data {
|
||||
int b1on_flags;
|
||||
unsigned short b1on_key;
|
||||
int b2on_flags;
|
||||
@ -188,14 +189,14 @@ struct mc13xxx_buttons_platform_data {
|
||||
unsigned short b3on_key;
|
||||
};
|
||||
|
||||
#define MC13783_TS_ATO_FIRST false
|
||||
#define MC13783_TS_ATO_EACH true
|
||||
|
||||
struct mc13xxx_ts_platform_data {
|
||||
/* Delay between Touchscreen polarization and ADC Conversion.
|
||||
* Given in clock ticks of a 32 kHz clock which gives a granularity of
|
||||
* about 30.5ms */
|
||||
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 */
|
||||
bool atox;
|
||||
};
|
||||
@ -210,11 +211,12 @@ struct mc13xxx_codec_platform_data {
|
||||
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_ADC (1 << 2)
|
||||
#define MC13XXX_USE_RTC (1 << 3)
|
||||
|
||||
struct mc13xxx_platform_data {
|
||||
unsigned int flags;
|
||||
|
||||
struct mc13xxx_regulator_platform_data regulators;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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
|
@ -12,7 +12,7 @@
|
||||
#define RDC321X_GPIO_CTRL_REG2 0x84
|
||||
#define RDC321X_GPIO_DATA_REG2 0x88
|
||||
|
||||
#define RDC321X_MAX_GPIO 58
|
||||
#define RDC321X_NUM_GPIO 59
|
||||
|
||||
struct rdc321x_gpio_pdata {
|
||||
struct pci_dev *sb_pdev;
|
||||
|
@ -24,35 +24,36 @@ enum sec_device_type {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sec_pmic_dev - s5m87xx master device for sub-drivers
|
||||
* @dev: master device of the chip (can be used to access platform data)
|
||||
* @pdata: pointer to private data used to pass platform data to child
|
||||
* @i2c: i2c client private data for regulator
|
||||
* @rtc: i2c client private data for rtc
|
||||
* @iolock: mutex for serializing io access
|
||||
* @irqlock: mutex for buslock
|
||||
* @irq_base: base IRQ number for sec-pmic, required for IRQs
|
||||
* @irq: generic IRQ number for s5m87xx
|
||||
* @ono: power onoff IRQ number for s5m87xx
|
||||
* @irq_masks_cur: currently active value
|
||||
* @irq_masks_cache: cached hardware value
|
||||
* @type: indicate which s5m87xx "variant" is used
|
||||
* struct sec_pmic_dev - s2m/s5m master device for sub-drivers
|
||||
* @dev: Master device of the chip
|
||||
* @pdata: Platform data populated with data from DTS
|
||||
* or board files
|
||||
* @regmap_pmic: Regmap associated with PMIC's I2C address
|
||||
* @i2c: I2C client of the main driver
|
||||
* @device_type: Type of device, matches enum sec_device_type
|
||||
* @irq_base: Base IRQ number for device, required for IRQs
|
||||
* @irq: Generic IRQ number for device
|
||||
* @irq_data: Runtime data structure for IRQ controller
|
||||
* @ono: Power onoff IRQ number for s5m87xx
|
||||
* @wakeup: Whether or not this is a wakeup device
|
||||
* @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 device *dev;
|
||||
struct sec_platform_data *pdata;
|
||||
struct regmap *regmap_pmic;
|
||||
struct regmap *regmap_rtc;
|
||||
struct i2c_client *i2c;
|
||||
struct i2c_client *rtc;
|
||||
|
||||
int device_type;
|
||||
unsigned long device_type;
|
||||
int irq_base;
|
||||
int irq;
|
||||
struct regmap_irq_chip_data *irq_data;
|
||||
|
||||
int ono;
|
||||
unsigned long type;
|
||||
bool wakeup;
|
||||
bool wtsr_smpl;
|
||||
};
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/mutex.h>
|
||||
|
||||
struct device;
|
||||
struct regulator;
|
||||
|
||||
enum stmpe_block {
|
||||
STMPE_BLOCK_GPIO = 1 << 0,
|
||||
@ -62,6 +63,8 @@ struct stmpe_client_info;
|
||||
|
||||
/**
|
||||
* struct stmpe - STMPE MFD structure
|
||||
* @vcc: optional VCC regulator
|
||||
* @vio: optional VIO regulator
|
||||
* @lock: lock protecting I/O operations
|
||||
* @irq_lock: IRQ bus lock
|
||||
* @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
|
||||
* different variants. Indexed by one of STMPE_IDX_*.
|
||||
* @irq: irq number for stmpe
|
||||
* @irq_base: starting IRQ number for internal IRQs
|
||||
* @num_gpios: number of gpios, differs for variants
|
||||
* @ier: cache of IER registers for bus_lock
|
||||
* @oldier: cache of IER registers for bus_lock
|
||||
* @pdata: platform data
|
||||
*/
|
||||
struct stmpe {
|
||||
struct regulator *vcc;
|
||||
struct regulator *vio;
|
||||
struct mutex lock;
|
||||
struct mutex irq_lock;
|
||||
struct device *dev;
|
||||
@ -91,7 +95,6 @@ struct stmpe {
|
||||
const u8 *regs;
|
||||
|
||||
int irq;
|
||||
int irq_base;
|
||||
int num_gpios;
|
||||
u8 ier[2];
|
||||
u8 oldier[2];
|
||||
@ -132,8 +135,6 @@ struct stmpe_keypad_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
|
||||
* requestable due to different usage (e.g. touch, keypad)
|
||||
* STMPE_GPIO_NOREQ_* macros can be used here.
|
||||
@ -141,7 +142,6 @@ struct stmpe_keypad_platform_data {
|
||||
* @remove: board specific remove callback
|
||||
*/
|
||||
struct stmpe_gpio_platform_data {
|
||||
int gpio_base;
|
||||
unsigned norequest_mask;
|
||||
void (*setup)(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
|
||||
* @autosleep: bool to enable/disable stmpe 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_gpio: gpio number over which irq will be requested (significant only if
|
||||
* irq_over_gpio is true)
|
||||
@ -207,7 +205,6 @@ struct stmpe_ts_platform_data {
|
||||
struct stmpe_platform_data {
|
||||
int id;
|
||||
unsigned int blocks;
|
||||
int irq_base;
|
||||
unsigned int irq_trigger;
|
||||
bool autosleep;
|
||||
bool irq_over_gpio;
|
||||
@ -219,10 +216,4 @@ struct stmpe_platform_data {
|
||||
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
|
||||
|
@ -15,6 +15,8 @@
|
||||
#ifndef __LINUX_MFD_SYSCON_H__
|
||||
#define __LINUX_MFD_SYSCON_H__
|
||||
|
||||
#include <linux/err.h>
|
||||
|
||||
struct device_node;
|
||||
|
||||
#ifdef CONFIG_MFD_SYSCON
|
||||
|
@ -267,7 +267,6 @@ struct tps65218 {
|
||||
u32 irq_mask;
|
||||
struct regmap_irq_chip_data *irq_data;
|
||||
struct regulator_desc desc[TPS65218_NUM_REGULATOR];
|
||||
struct regulator_dev *rdev[TPS65218_NUM_REGULATOR];
|
||||
struct tps_info *info[TPS65218_NUM_REGULATOR];
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#define TWL6040_REG_ASICID 0x01
|
||||
#define TWL6040_REG_ASICREV 0x02
|
||||
@ -157,6 +158,7 @@
|
||||
#define TWL6040_I2CSEL 0x01
|
||||
#define TWL6040_RESETSPLIT 0x04
|
||||
#define TWL6040_INTCLRMODE 0x08
|
||||
#define TWL6040_I2CMODE(x) ((x & 0x3) << 4)
|
||||
|
||||
/* STATUS (0x2E) fields */
|
||||
|
||||
@ -222,6 +224,7 @@ struct twl6040 {
|
||||
struct regmap *regmap;
|
||||
struct regmap_irq_chip_data *irq_data;
|
||||
struct regulator_bulk_data supplies[2]; /* supplies for vio, v2v1 */
|
||||
struct clk *clk32k;
|
||||
struct mutex mutex;
|
||||
struct mutex irq_mutex;
|
||||
struct mfd_cell cells[TWL6040_CELLS];
|
||||
|
Loading…
Reference in New Issue
Block a user