mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-07 14:32:23 +00:00
Char / misc driver patches for 3.16-rc1
Here is the big char / misc driver updates for 3.16-rc1. Lots of different driver updates for a variety of different drivers and minor driver subsystems. All have been in linux-next with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iEYEABECAAYFAlONWI8ACgkQMUfUDdst+ykvQACdGxTChdEU7edElDAXeelVmu8v D1UAoLDvqwUsN7t5v+WG2wkOvhf5MEA7 =tVMP -----END PGP SIGNATURE----- Merge tag 'char-misc-3.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc into next Pull char/misc driver patches from Greg KH: "Here is the big char / misc driver update for 3.16-rc1. Lots of different driver updates for a variety of different drivers and minor driver subsystems. All have been in linux-next with no reported issues" * tag 'char-misc-3.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (79 commits) hv: use correct order when freeing monitor_pages spmi: of: fixup generic SPMI devicetree binding example applicom: dereferencing NULL on error path misc: genwqe: fix uninitialized return value in genwqe_free_sync_sgl() miscdevice.h: Simple syntax fix to make pointers consistent. MAINTAINERS: Add miscdevice.h to file list for char/misc drivers. mcb: Add support for shared PCI IRQs drivers: Remove duplicate conditionally included subdirs misc: atmel_pwm: only build for supported platforms mei: me: move probe quirk to cfg structure mei: add per device configuration mei: me: read H_CSR after asserting reset mei: me: drop harmful wait optimization mei: me: fix hw ready reset flow mei: fix memory leak of mei_clients array uio: fix vma io range check in mmap drivers: uio_dmem_genirq: Fix memory leak in uio_dmem_genirq_probe() w1: do not unlock unheld list_mutex in __w1_remove_master_device() w1: optional bundling of netlink kernel replies connector: allow multiple messages to be sent in one packet ...
This commit is contained in:
commit
4046136afb
@ -24,7 +24,8 @@ netlink based networking for inter-process communication in a significantly
|
|||||||
easier way:
|
easier way:
|
||||||
|
|
||||||
int cn_add_callback(struct cb_id *id, char *name, void (*callback) (struct cn_msg *, struct netlink_skb_parms *));
|
int cn_add_callback(struct cb_id *id, char *name, void (*callback) (struct cn_msg *, struct netlink_skb_parms *));
|
||||||
void cn_netlink_send(struct cn_msg *msg, u32 __group, int gfp_mask);
|
void cn_netlink_send_multi(struct cn_msg *msg, u16 len, u32 portid, u32 __group, int gfp_mask);
|
||||||
|
void cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group, int gfp_mask);
|
||||||
|
|
||||||
struct cb_id
|
struct cb_id
|
||||||
{
|
{
|
||||||
@ -71,15 +72,21 @@ void cn_del_callback(struct cb_id *id);
|
|||||||
struct cb_id *id - unique connector's user identifier.
|
struct cb_id *id - unique connector's user identifier.
|
||||||
|
|
||||||
|
|
||||||
int cn_netlink_send(struct cn_msg *msg, u32 __groups, int gfp_mask);
|
int cn_netlink_send_multi(struct cn_msg *msg, u16 len, u32 portid, u32 __groups, int gfp_mask);
|
||||||
|
int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __groups, int gfp_mask);
|
||||||
|
|
||||||
Sends message to the specified groups. It can be safely called from
|
Sends message to the specified groups. It can be safely called from
|
||||||
softirq context, but may silently fail under strong memory pressure.
|
softirq context, but may silently fail under strong memory pressure.
|
||||||
If there are no listeners for given group -ESRCH can be returned.
|
If there are no listeners for given group -ESRCH can be returned.
|
||||||
|
|
||||||
struct cn_msg * - message header(with attached data).
|
struct cn_msg * - message header(with attached data).
|
||||||
|
u16 len - for *_multi multiple cn_msg messages can be sent
|
||||||
|
u32 port - destination port.
|
||||||
|
If non-zero the message will be sent to the
|
||||||
|
given port, which should be set to the
|
||||||
|
original sender.
|
||||||
u32 __group - destination group.
|
u32 __group - destination group.
|
||||||
If __group is zero, then appropriate group will
|
If port and __group is zero, then appropriate group will
|
||||||
be searched through all registered connector users,
|
be searched through all registered connector users,
|
||||||
and message will be delivered to the group which was
|
and message will be delivered to the group which was
|
||||||
created for user with the same ID as in msg.
|
created for user with the same ID as in msg.
|
||||||
@ -111,7 +118,7 @@ acknowledge number MUST be the same + 1.
|
|||||||
If we receive a message and its sequence number is not equal to one we
|
If we receive a message and its sequence number is not equal to one we
|
||||||
are expecting, then it is a new message. If we receive a message and
|
are expecting, then it is a new message. If we receive a message and
|
||||||
its sequence number is the same as one we are expecting, but its
|
its sequence number is the same as one we are expecting, but its
|
||||||
acknowledge is not equal to the acknowledge number in the original
|
acknowledge is not equal to the sequence number in the original
|
||||||
message + 1, then it is a new message.
|
message + 1, then it is a new message.
|
||||||
|
|
||||||
Obviously, the protocol header contains the above id.
|
Obviously, the protocol header contains the above id.
|
||||||
|
18
Documentation/devicetree/bindings/misc/arm-charlcd.txt
Normal file
18
Documentation/devicetree/bindings/misc/arm-charlcd.txt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
ARM Versatile Character LCD
|
||||||
|
-----------------------------------------------------
|
||||||
|
This binding defines the character LCD interface found on ARM Versatile AB
|
||||||
|
and PB reference platforms.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : "arm,versatile-clcd"
|
||||||
|
- reg : Location and size of character LCD registers
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- interrupts - single interrupt for character LCD. The character LCD can
|
||||||
|
operate in polled mode without an interrupt.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
lcd@10008000 {
|
||||||
|
compatible = "arm,versatile-lcd";
|
||||||
|
reg = <0x10008000 0x1000>;
|
||||||
|
};
|
@ -26,7 +26,7 @@ Each child node must have one and only one 'reg' entry of type SPMI_USID.
|
|||||||
reg = <...>;
|
reg = <...>;
|
||||||
|
|
||||||
#address-cells = <2>;
|
#address-cells = <2>;
|
||||||
#size-cells <0>;
|
#size-cells = <0>;
|
||||||
|
|
||||||
child@0 {
|
child@0 {
|
||||||
compatible = "...";
|
compatible = "...";
|
||||||
|
@ -82,7 +82,7 @@ driver - (standard) symlink to the w1 driver
|
|||||||
w1_master_add - Manually register a slave device
|
w1_master_add - Manually register a slave device
|
||||||
w1_master_attempts - the number of times a search was attempted
|
w1_master_attempts - the number of times a search was attempted
|
||||||
w1_master_max_slave_count
|
w1_master_max_slave_count
|
||||||
- the maximum slaves that may be attached to a master
|
- maximum number of slaves to search for at a time
|
||||||
w1_master_name - the name of the device (w1_bus_masterX)
|
w1_master_name - the name of the device (w1_bus_masterX)
|
||||||
w1_master_pullup - 5V strong pullup 0 enabled, 1 disabled
|
w1_master_pullup - 5V strong pullup 0 enabled, 1 disabled
|
||||||
w1_master_remove - Manually remove a slave device
|
w1_master_remove - Manually remove a slave device
|
||||||
|
@ -30,7 +30,7 @@ Protocol.
|
|||||||
W1_SLAVE_CMD
|
W1_SLAVE_CMD
|
||||||
userspace command for slave device
|
userspace command for slave device
|
||||||
(read/write/touch)
|
(read/write/touch)
|
||||||
__u8 res - reserved
|
__u8 status - error indication from kernel
|
||||||
__u16 len - size of data attached to this header data
|
__u16 len - size of data attached to this header data
|
||||||
union {
|
union {
|
||||||
__u8 id[8]; - slave unique device id
|
__u8 id[8]; - slave unique device id
|
||||||
@ -44,10 +44,14 @@ Protocol.
|
|||||||
__u8 cmd - command opcode.
|
__u8 cmd - command opcode.
|
||||||
W1_CMD_READ - read command
|
W1_CMD_READ - read command
|
||||||
W1_CMD_WRITE - write command
|
W1_CMD_WRITE - write command
|
||||||
W1_CMD_TOUCH - touch command
|
|
||||||
(write and sample data back to userspace)
|
|
||||||
W1_CMD_SEARCH - search command
|
W1_CMD_SEARCH - search command
|
||||||
W1_CMD_ALARM_SEARCH - alarm search command
|
W1_CMD_ALARM_SEARCH - alarm search command
|
||||||
|
W1_CMD_TOUCH - touch command
|
||||||
|
(write and sample data back to userspace)
|
||||||
|
W1_CMD_RESET - send bus reset
|
||||||
|
W1_CMD_SLAVE_ADD - add slave to kernel list
|
||||||
|
W1_CMD_SLAVE_REMOVE - remove slave from kernel list
|
||||||
|
W1_CMD_LIST_SLAVES - get slaves list from kernel
|
||||||
__u8 res - reserved
|
__u8 res - reserved
|
||||||
__u16 len - length of data for this command
|
__u16 len - length of data for this command
|
||||||
For read command data must be allocated like for write command
|
For read command data must be allocated like for write command
|
||||||
@ -87,8 +91,7 @@ format:
|
|||||||
id0 ... idN
|
id0 ... idN
|
||||||
|
|
||||||
Each message is at most 4k in size, so if number of master devices
|
Each message is at most 4k in size, so if number of master devices
|
||||||
exceeds this, it will be split into several messages,
|
exceeds this, it will be split into several messages.
|
||||||
cn.seq will be increased for each one.
|
|
||||||
|
|
||||||
W1 search and alarm search commands.
|
W1 search and alarm search commands.
|
||||||
request:
|
request:
|
||||||
|
@ -2188,6 +2188,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
|
|||||||
S: Supported
|
S: Supported
|
||||||
F: drivers/char/*
|
F: drivers/char/*
|
||||||
F: drivers/misc/*
|
F: drivers/misc/*
|
||||||
|
F: include/linux/miscdevice.h
|
||||||
|
|
||||||
CHECKPATCH
|
CHECKPATCH
|
||||||
M: Andy Whitcroft <apw@canonical.com>
|
M: Andy Whitcroft <apw@canonical.com>
|
||||||
|
@ -83,7 +83,6 @@ obj-$(CONFIG_PCCARD) += pcmcia/
|
|||||||
obj-$(CONFIG_DIO) += dio/
|
obj-$(CONFIG_DIO) += dio/
|
||||||
obj-$(CONFIG_SBUS) += sbus/
|
obj-$(CONFIG_SBUS) += sbus/
|
||||||
obj-$(CONFIG_ZORRO) += zorro/
|
obj-$(CONFIG_ZORRO) += zorro/
|
||||||
obj-$(CONFIG_MAC) += macintosh/
|
|
||||||
obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/
|
obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/
|
||||||
obj-$(CONFIG_PARIDE) += block/paride/
|
obj-$(CONFIG_PARIDE) += block/paride/
|
||||||
obj-$(CONFIG_TC) += tc/
|
obj-$(CONFIG_TC) += tc/
|
||||||
@ -141,7 +140,6 @@ obj-y += clk/
|
|||||||
|
|
||||||
obj-$(CONFIG_MAILBOX) += mailbox/
|
obj-$(CONFIG_MAILBOX) += mailbox/
|
||||||
obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
|
obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
|
||||||
obj-$(CONFIG_NFC) += nfc/
|
|
||||||
obj-$(CONFIG_IOMMU_SUPPORT) += iommu/
|
obj-$(CONFIG_IOMMU_SUPPORT) += iommu/
|
||||||
obj-$(CONFIG_REMOTEPROC) += remoteproc/
|
obj-$(CONFIG_REMOTEPROC) += remoteproc/
|
||||||
obj-$(CONFIG_RPMSG) += rpmsg/
|
obj-$(CONFIG_RPMSG) += rpmsg/
|
||||||
|
@ -345,7 +345,6 @@ static int __init applicom_init(void)
|
|||||||
free_irq(apbs[i].irq, &dummy);
|
free_irq(apbs[i].irq, &dummy);
|
||||||
iounmap(apbs[i].RamIO);
|
iounmap(apbs[i].RamIO);
|
||||||
}
|
}
|
||||||
pci_disable_device(dev);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +43,8 @@ static struct cn_dev cdev;
|
|||||||
static int cn_already_initialized;
|
static int cn_already_initialized;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* Sends mult (multiple) cn_msg at a time.
|
||||||
|
*
|
||||||
* msg->seq and msg->ack are used to determine message genealogy.
|
* msg->seq and msg->ack are used to determine message genealogy.
|
||||||
* When someone sends message it puts there locally unique sequence
|
* When someone sends message it puts there locally unique sequence
|
||||||
* and random acknowledge numbers. Sequence number may be copied into
|
* and random acknowledge numbers. Sequence number may be copied into
|
||||||
@ -62,10 +64,13 @@ static int cn_already_initialized;
|
|||||||
* the acknowledgement number in the original message + 1, then it is
|
* the acknowledgement number in the original message + 1, then it is
|
||||||
* a new message.
|
* a new message.
|
||||||
*
|
*
|
||||||
|
* If msg->len != len, then additional cn_msg messages are expected following
|
||||||
|
* the first msg.
|
||||||
|
*
|
||||||
* The message is sent to, the portid if given, the group if given, both if
|
* The message is sent to, the portid if given, the group if given, both if
|
||||||
* both, or if both are zero then the group is looked up and sent there.
|
* both, or if both are zero then the group is looked up and sent there.
|
||||||
*/
|
*/
|
||||||
int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
|
int cn_netlink_send_mult(struct cn_msg *msg, u16 len, u32 portid, u32 __group,
|
||||||
gfp_t gfp_mask)
|
gfp_t gfp_mask)
|
||||||
{
|
{
|
||||||
struct cn_callback_entry *__cbq;
|
struct cn_callback_entry *__cbq;
|
||||||
@ -98,7 +103,7 @@ int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
|
|||||||
if (!portid && !netlink_has_listeners(dev->nls, group))
|
if (!portid && !netlink_has_listeners(dev->nls, group))
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|
||||||
size = sizeof(*msg) + msg->len;
|
size = sizeof(*msg) + len;
|
||||||
|
|
||||||
skb = nlmsg_new(size, gfp_mask);
|
skb = nlmsg_new(size, gfp_mask);
|
||||||
if (!skb)
|
if (!skb)
|
||||||
@ -121,6 +126,14 @@ int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
|
|||||||
gfp_mask);
|
gfp_mask);
|
||||||
return netlink_unicast(dev->nls, skb, portid, !(gfp_mask&__GFP_WAIT));
|
return netlink_unicast(dev->nls, skb, portid, !(gfp_mask&__GFP_WAIT));
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(cn_netlink_send_mult);
|
||||||
|
|
||||||
|
/* same as cn_netlink_send_mult except msg->len is used for len */
|
||||||
|
int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
|
||||||
|
gfp_t gfp_mask)
|
||||||
|
{
|
||||||
|
return cn_netlink_send_mult(msg, msg->len, portid, __group, gfp_mask);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL_GPL(cn_netlink_send);
|
EXPORT_SYMBOL_GPL(cn_netlink_send);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -28,13 +28,13 @@ config EXTCON_ADC_JACK
|
|||||||
Say Y here to enable extcon device driver based on ADC values.
|
Say Y here to enable extcon device driver based on ADC values.
|
||||||
|
|
||||||
config EXTCON_MAX14577
|
config EXTCON_MAX14577
|
||||||
tristate "MAX14577 EXTCON Support"
|
tristate "MAX14577/77836 EXTCON Support"
|
||||||
depends on MFD_MAX14577
|
depends on MFD_MAX14577
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
select REGMAP_I2C
|
select REGMAP_I2C
|
||||||
help
|
help
|
||||||
If you say yes here you get support for the MUIC device of
|
If you say yes here you get support for the MUIC device of
|
||||||
Maxim MAX14577 PMIC. The MAX14577 MUIC is a USB port accessory
|
Maxim MAX14577/77836. The MAX14577/77836 MUIC is a USB port accessory
|
||||||
detector and switch.
|
detector and switch.
|
||||||
|
|
||||||
config EXTCON_MAX77693
|
config EXTCON_MAX77693
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
* @chan: iio channel being queried.
|
* @chan: iio channel being queried.
|
||||||
*/
|
*/
|
||||||
struct adc_jack_data {
|
struct adc_jack_data {
|
||||||
struct extcon_dev edev;
|
struct extcon_dev *edev;
|
||||||
|
|
||||||
const char **cable_names;
|
const char **cable_names;
|
||||||
int num_cables;
|
int num_cables;
|
||||||
@ -64,7 +64,7 @@ static void adc_jack_handler(struct work_struct *work)
|
|||||||
|
|
||||||
ret = iio_read_channel_raw(data->chan, &adc_val);
|
ret = iio_read_channel_raw(data->chan, &adc_val);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&data->edev.dev, "read channel() error: %d\n", ret);
|
dev_err(&data->edev->dev, "read channel() error: %d\n", ret);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ static void adc_jack_handler(struct work_struct *work)
|
|||||||
}
|
}
|
||||||
/* if no def has met, it means state = 0 (no cables attached) */
|
/* if no def has met, it means state = 0 (no cables attached) */
|
||||||
|
|
||||||
extcon_set_state(&data->edev, state);
|
extcon_set_state(data->edev, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
|
static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
|
||||||
@ -102,33 +102,33 @@ static int adc_jack_probe(struct platform_device *pdev)
|
|||||||
if (!data)
|
if (!data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
data->edev.name = pdata->name;
|
|
||||||
|
|
||||||
if (!pdata->cable_names) {
|
if (!pdata->cable_names) {
|
||||||
err = -EINVAL;
|
|
||||||
dev_err(&pdev->dev, "error: cable_names not defined.\n");
|
dev_err(&pdev->dev, "error: cable_names not defined.\n");
|
||||||
goto out;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->edev.dev.parent = &pdev->dev;
|
data->edev = devm_extcon_dev_allocate(&pdev->dev, pdata->cable_names);
|
||||||
data->edev.supported_cable = pdata->cable_names;
|
if (IS_ERR(data->edev)) {
|
||||||
|
dev_err(&pdev->dev, "failed to allocate extcon device\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
data->edev->dev.parent = &pdev->dev;
|
||||||
|
data->edev->name = pdata->name;
|
||||||
|
|
||||||
/* Check the length of array and set num_cables */
|
/* Check the length of array and set num_cables */
|
||||||
for (i = 0; data->edev.supported_cable[i]; i++)
|
for (i = 0; data->edev->supported_cable[i]; i++)
|
||||||
;
|
;
|
||||||
if (i == 0 || i > SUPPORTED_CABLE_MAX) {
|
if (i == 0 || i > SUPPORTED_CABLE_MAX) {
|
||||||
err = -EINVAL;
|
|
||||||
dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n",
|
dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n",
|
||||||
i - 1);
|
i - 1);
|
||||||
goto out;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
data->num_cables = i;
|
data->num_cables = i;
|
||||||
|
|
||||||
if (!pdata->adc_conditions ||
|
if (!pdata->adc_conditions ||
|
||||||
!pdata->adc_conditions[0].state) {
|
!pdata->adc_conditions[0].state) {
|
||||||
err = -EINVAL;
|
|
||||||
dev_err(&pdev->dev, "error: adc_conditions not defined.\n");
|
dev_err(&pdev->dev, "error: adc_conditions not defined.\n");
|
||||||
goto out;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
data->adc_conditions = pdata->adc_conditions;
|
data->adc_conditions = pdata->adc_conditions;
|
||||||
|
|
||||||
@ -138,10 +138,8 @@ static int adc_jack_probe(struct platform_device *pdev)
|
|||||||
data->num_conditions = i;
|
data->num_conditions = i;
|
||||||
|
|
||||||
data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel);
|
data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel);
|
||||||
if (IS_ERR(data->chan)) {
|
if (IS_ERR(data->chan))
|
||||||
err = PTR_ERR(data->chan);
|
return PTR_ERR(data->chan);
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms);
|
data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms);
|
||||||
|
|
||||||
@ -149,15 +147,14 @@ static int adc_jack_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
platform_set_drvdata(pdev, data);
|
platform_set_drvdata(pdev, data);
|
||||||
|
|
||||||
err = extcon_dev_register(&data->edev);
|
err = devm_extcon_dev_register(&pdev->dev, data->edev);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
return err;
|
||||||
|
|
||||||
data->irq = platform_get_irq(pdev, 0);
|
data->irq = platform_get_irq(pdev, 0);
|
||||||
if (!data->irq) {
|
if (!data->irq) {
|
||||||
dev_err(&pdev->dev, "platform_get_irq failed\n");
|
dev_err(&pdev->dev, "platform_get_irq failed\n");
|
||||||
err = -ENODEV;
|
return -ENODEV;
|
||||||
goto err_irq;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = request_any_context_irq(data->irq, adc_jack_irq_thread,
|
err = request_any_context_irq(data->irq, adc_jack_irq_thread,
|
||||||
@ -165,15 +162,10 @@ static int adc_jack_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(&pdev->dev, "error: irq %d\n", data->irq);
|
dev_err(&pdev->dev, "error: irq %d\n", data->irq);
|
||||||
goto err_irq;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_irq:
|
|
||||||
extcon_dev_unregister(&data->edev);
|
|
||||||
out:
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int adc_jack_remove(struct platform_device *pdev)
|
static int adc_jack_remove(struct platform_device *pdev)
|
||||||
@ -182,7 +174,6 @@ static int adc_jack_remove(struct platform_device *pdev)
|
|||||||
|
|
||||||
free_irq(data->irq, data);
|
free_irq(data->irq, data);
|
||||||
cancel_work_sync(&data->handler.work);
|
cancel_work_sync(&data->handler.work);
|
||||||
extcon_dev_unregister(&data->edev);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ struct arizona_extcon_info {
|
|||||||
|
|
||||||
int hpdet_ip;
|
int hpdet_ip;
|
||||||
|
|
||||||
struct extcon_dev edev;
|
struct extcon_dev *edev;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct arizona_micd_config micd_default_modes[] = {
|
static const struct arizona_micd_config micd_default_modes[] = {
|
||||||
@ -546,7 +546,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If the cable was removed while measuring ignore the result */
|
/* If the cable was removed while measuring ignore the result */
|
||||||
ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL);
|
ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(arizona->dev, "Failed to check cable state: %d\n",
|
dev_err(arizona->dev, "Failed to check cable state: %d\n",
|
||||||
ret);
|
ret);
|
||||||
@ -581,7 +581,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
|
|||||||
else
|
else
|
||||||
report = ARIZONA_CABLE_HEADPHONE;
|
report = ARIZONA_CABLE_HEADPHONE;
|
||||||
|
|
||||||
ret = extcon_set_cable_state_(&info->edev, report, true);
|
ret = extcon_set_cable_state_(info->edev, report, true);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
dev_err(arizona->dev, "Failed to report HP/line: %d\n",
|
dev_err(arizona->dev, "Failed to report HP/line: %d\n",
|
||||||
ret);
|
ret);
|
||||||
@ -664,7 +664,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
|
|||||||
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
|
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
|
||||||
|
|
||||||
/* Just report headphone */
|
/* Just report headphone */
|
||||||
ret = extcon_update_state(&info->edev,
|
ret = extcon_update_state(info->edev,
|
||||||
1 << ARIZONA_CABLE_HEADPHONE,
|
1 << ARIZONA_CABLE_HEADPHONE,
|
||||||
1 << ARIZONA_CABLE_HEADPHONE);
|
1 << ARIZONA_CABLE_HEADPHONE);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
@ -723,7 +723,7 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
|
|||||||
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
|
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
|
||||||
|
|
||||||
/* Just report headphone */
|
/* Just report headphone */
|
||||||
ret = extcon_update_state(&info->edev,
|
ret = extcon_update_state(info->edev,
|
||||||
1 << ARIZONA_CABLE_HEADPHONE,
|
1 << ARIZONA_CABLE_HEADPHONE,
|
||||||
1 << ARIZONA_CABLE_HEADPHONE);
|
1 << ARIZONA_CABLE_HEADPHONE);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
@ -764,7 +764,7 @@ static void arizona_micd_detect(struct work_struct *work)
|
|||||||
mutex_lock(&info->lock);
|
mutex_lock(&info->lock);
|
||||||
|
|
||||||
/* If the cable was removed while measuring ignore the result */
|
/* If the cable was removed while measuring ignore the result */
|
||||||
ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL);
|
ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(arizona->dev, "Failed to check cable state: %d\n",
|
dev_err(arizona->dev, "Failed to check cable state: %d\n",
|
||||||
ret);
|
ret);
|
||||||
@ -812,7 +812,7 @@ static void arizona_micd_detect(struct work_struct *work)
|
|||||||
if (info->detecting && (val & ARIZONA_MICD_LVL_8)) {
|
if (info->detecting && (val & ARIZONA_MICD_LVL_8)) {
|
||||||
arizona_identify_headphone(info);
|
arizona_identify_headphone(info);
|
||||||
|
|
||||||
ret = extcon_update_state(&info->edev,
|
ret = extcon_update_state(info->edev,
|
||||||
1 << ARIZONA_CABLE_MICROPHONE,
|
1 << ARIZONA_CABLE_MICROPHONE,
|
||||||
1 << ARIZONA_CABLE_MICROPHONE);
|
1 << ARIZONA_CABLE_MICROPHONE);
|
||||||
|
|
||||||
@ -999,7 +999,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
|||||||
|
|
||||||
if (info->last_jackdet == present) {
|
if (info->last_jackdet == present) {
|
||||||
dev_dbg(arizona->dev, "Detected jack\n");
|
dev_dbg(arizona->dev, "Detected jack\n");
|
||||||
ret = extcon_set_cable_state_(&info->edev,
|
ret = extcon_set_cable_state_(info->edev,
|
||||||
ARIZONA_CABLE_MECHANICAL, true);
|
ARIZONA_CABLE_MECHANICAL, true);
|
||||||
|
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
@ -1038,7 +1038,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
|||||||
info->micd_ranges[i].key, 0);
|
info->micd_ranges[i].key, 0);
|
||||||
input_sync(info->input);
|
input_sync(info->input);
|
||||||
|
|
||||||
ret = extcon_update_state(&info->edev, 0xffffffff, 0);
|
ret = extcon_update_state(info->edev, 0xffffffff, 0);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
dev_err(arizona->dev, "Removal report failed: %d\n",
|
dev_err(arizona->dev, "Removal report failed: %d\n",
|
||||||
ret);
|
ret);
|
||||||
@ -1105,15 +1105,14 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
|||||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||||
if (!info) {
|
if (!info) {
|
||||||
dev_err(&pdev->dev, "Failed to allocate memory\n");
|
dev_err(&pdev->dev, "Failed to allocate memory\n");
|
||||||
ret = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info->micvdd = devm_regulator_get(arizona->dev, "MICVDD");
|
info->micvdd = devm_regulator_get(arizona->dev, "MICVDD");
|
||||||
if (IS_ERR(info->micvdd)) {
|
if (IS_ERR(info->micvdd)) {
|
||||||
ret = PTR_ERR(info->micvdd);
|
ret = PTR_ERR(info->micvdd);
|
||||||
dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret);
|
dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret);
|
||||||
goto err;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_init(&info->lock);
|
mutex_init(&info->lock);
|
||||||
@ -1151,15 +1150,19 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
info->edev.name = "Headset Jack";
|
info->edev = devm_extcon_dev_allocate(&pdev->dev, arizona_cable);
|
||||||
info->edev.dev.parent = arizona->dev;
|
if (IS_ERR(info->edev)) {
|
||||||
info->edev.supported_cable = arizona_cable;
|
dev_err(&pdev->dev, "failed to allocate extcon device\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
info->edev->name = "Headset Jack";
|
||||||
|
info->edev->dev.parent = arizona->dev;
|
||||||
|
|
||||||
ret = extcon_dev_register(&info->edev);
|
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",
|
dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",
|
||||||
ret);
|
ret);
|
||||||
goto err;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
info->input = devm_input_allocate_device(&pdev->dev);
|
info->input = devm_input_allocate_device(&pdev->dev);
|
||||||
@ -1410,8 +1413,6 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
|||||||
err_input:
|
err_input:
|
||||||
err_register:
|
err_register:
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
extcon_dev_unregister(&info->edev);
|
|
||||||
err:
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1445,7 +1446,6 @@ static int arizona_extcon_remove(struct platform_device *pdev)
|
|||||||
regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
|
regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
|
||||||
ARIZONA_JD1_ENA, 0);
|
ARIZONA_JD1_ENA, 0);
|
||||||
arizona_clk32k_disable(arizona);
|
arizona_clk32k_disable(arizona);
|
||||||
extcon_dev_unregister(&info->edev);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -565,6 +565,100 @@ static void dummy_sysfs_dev_release(struct device *dev)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* extcon_dev_allocate() - Allocate the memory of extcon device.
|
||||||
|
* @supported_cable: Array of supported cable names ending with NULL.
|
||||||
|
* If supported_cable is NULL, cable name related APIs
|
||||||
|
* are disabled.
|
||||||
|
*
|
||||||
|
* This function allocates the memory for extcon device without allocating
|
||||||
|
* memory in each extcon provider driver and initialize default setting for
|
||||||
|
* extcon device.
|
||||||
|
*
|
||||||
|
* Return the pointer of extcon device if success or ERR_PTR(err) if fail
|
||||||
|
*/
|
||||||
|
struct extcon_dev *extcon_dev_allocate(const char **supported_cable)
|
||||||
|
{
|
||||||
|
struct extcon_dev *edev;
|
||||||
|
|
||||||
|
edev = kzalloc(sizeof(*edev), GFP_KERNEL);
|
||||||
|
if (!edev)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
edev->max_supported = 0;
|
||||||
|
edev->supported_cable = supported_cable;
|
||||||
|
|
||||||
|
return edev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* extcon_dev_free() - Free the memory of extcon device.
|
||||||
|
* @edev: the extcon device to free
|
||||||
|
*/
|
||||||
|
void extcon_dev_free(struct extcon_dev *edev)
|
||||||
|
{
|
||||||
|
kfree(edev);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(extcon_dev_free);
|
||||||
|
|
||||||
|
static int devm_extcon_dev_match(struct device *dev, void *res, void *data)
|
||||||
|
{
|
||||||
|
struct extcon_dev **r = res;
|
||||||
|
|
||||||
|
if (WARN_ON(!r || !*r))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return *r == data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void devm_extcon_dev_release(struct device *dev, void *res)
|
||||||
|
{
|
||||||
|
extcon_dev_free(*(struct extcon_dev **)res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* devm_extcon_dev_allocate - Allocate managed extcon device
|
||||||
|
* @dev: device owning the extcon device being created
|
||||||
|
* @supported_cable: Array of supported cable names ending with NULL.
|
||||||
|
* If supported_cable is NULL, cable name related APIs
|
||||||
|
* are disabled.
|
||||||
|
*
|
||||||
|
* This function manages automatically the memory of extcon device using device
|
||||||
|
* resource management and simplify the control of freeing the memory of extcon
|
||||||
|
* device.
|
||||||
|
*
|
||||||
|
* Returns the pointer memory of allocated extcon_dev if success
|
||||||
|
* or ERR_PTR(err) if fail
|
||||||
|
*/
|
||||||
|
struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
|
||||||
|
const char **supported_cable)
|
||||||
|
{
|
||||||
|
struct extcon_dev **ptr, *edev;
|
||||||
|
|
||||||
|
ptr = devres_alloc(devm_extcon_dev_release, sizeof(*ptr), GFP_KERNEL);
|
||||||
|
if (!ptr)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
edev = extcon_dev_allocate(supported_cable);
|
||||||
|
if (IS_ERR(edev)) {
|
||||||
|
devres_free(ptr);
|
||||||
|
return edev;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ptr = edev;
|
||||||
|
devres_add(dev, ptr);
|
||||||
|
|
||||||
|
return edev;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(devm_extcon_dev_allocate);
|
||||||
|
|
||||||
|
void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev)
|
||||||
|
{
|
||||||
|
WARN_ON(devres_release(dev, devm_extcon_dev_release,
|
||||||
|
devm_extcon_dev_match, edev));
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(devm_extcon_dev_free);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* extcon_dev_register() - Register a new extcon device
|
* extcon_dev_register() - Register a new extcon device
|
||||||
* @edev : the new extcon device (should be allocated before calling)
|
* @edev : the new extcon device (should be allocated before calling)
|
||||||
@ -819,6 +913,63 @@ void extcon_dev_unregister(struct extcon_dev *edev)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(extcon_dev_unregister);
|
EXPORT_SYMBOL_GPL(extcon_dev_unregister);
|
||||||
|
|
||||||
|
static void devm_extcon_dev_unreg(struct device *dev, void *res)
|
||||||
|
{
|
||||||
|
extcon_dev_unregister(*(struct extcon_dev **)res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* devm_extcon_dev_register() - Resource-managed extcon_dev_register()
|
||||||
|
* @dev: device to allocate extcon device
|
||||||
|
* @edev: the new extcon device to register
|
||||||
|
*
|
||||||
|
* Managed extcon_dev_register() function. If extcon device is attached with
|
||||||
|
* this function, that extcon device is automatically unregistered on driver
|
||||||
|
* detach. Internally this function calls extcon_dev_register() function.
|
||||||
|
* To get more information, refer that function.
|
||||||
|
*
|
||||||
|
* If extcon device is registered with this function and the device needs to be
|
||||||
|
* unregistered separately, devm_extcon_dev_unregister() should be used.
|
||||||
|
*
|
||||||
|
* Returns 0 if success or negaive error number if failure.
|
||||||
|
*/
|
||||||
|
int devm_extcon_dev_register(struct device *dev, struct extcon_dev *edev)
|
||||||
|
{
|
||||||
|
struct extcon_dev **ptr;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ptr = devres_alloc(devm_extcon_dev_unreg, sizeof(*ptr), GFP_KERNEL);
|
||||||
|
if (!ptr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = extcon_dev_register(edev);
|
||||||
|
if (ret) {
|
||||||
|
devres_free(ptr);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ptr = edev;
|
||||||
|
devres_add(dev, ptr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(devm_extcon_dev_register);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* devm_extcon_dev_unregister() - Resource-managed extcon_dev_unregister()
|
||||||
|
* @dev: device the extcon belongs to
|
||||||
|
* @edev: the extcon device to unregister
|
||||||
|
*
|
||||||
|
* Unregister extcon device that is registered with devm_extcon_dev_register()
|
||||||
|
* function.
|
||||||
|
*/
|
||||||
|
void devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev)
|
||||||
|
{
|
||||||
|
WARN_ON(devres_release(dev, devm_extcon_dev_unreg,
|
||||||
|
devm_extcon_dev_match, edev));
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(devm_extcon_dev_unregister);
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
/*
|
/*
|
||||||
* extcon_get_edev_by_phandle - Get the extcon device from devicetree
|
* extcon_get_edev_by_phandle - Get the extcon device from devicetree
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
#include <linux/extcon/extcon-gpio.h>
|
#include <linux/extcon/extcon-gpio.h>
|
||||||
|
|
||||||
struct gpio_extcon_data {
|
struct gpio_extcon_data {
|
||||||
struct extcon_dev edev;
|
struct extcon_dev *edev;
|
||||||
unsigned gpio;
|
unsigned gpio;
|
||||||
bool gpio_active_low;
|
bool gpio_active_low;
|
||||||
const char *state_on;
|
const char *state_on;
|
||||||
@ -53,7 +53,7 @@ static void gpio_extcon_work(struct work_struct *work)
|
|||||||
state = gpio_get_value(data->gpio);
|
state = gpio_get_value(data->gpio);
|
||||||
if (data->gpio_active_low)
|
if (data->gpio_active_low)
|
||||||
state = !state;
|
state = !state;
|
||||||
extcon_set_state(&data->edev, state);
|
extcon_set_state(data->edev, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
|
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
|
||||||
@ -67,9 +67,10 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
|
|||||||
|
|
||||||
static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)
|
static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)
|
||||||
{
|
{
|
||||||
struct gpio_extcon_data *extcon_data =
|
struct device *dev = edev->dev.parent;
|
||||||
container_of(edev, struct gpio_extcon_data, edev);
|
struct gpio_extcon_data *extcon_data = dev_get_drvdata(dev);
|
||||||
const char *state;
|
const char *state;
|
||||||
|
|
||||||
if (extcon_get_state(edev))
|
if (extcon_get_state(edev))
|
||||||
state = extcon_data->state_on;
|
state = extcon_data->state_on;
|
||||||
else
|
else
|
||||||
@ -98,15 +99,21 @@ static int gpio_extcon_probe(struct platform_device *pdev)
|
|||||||
if (!extcon_data)
|
if (!extcon_data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
extcon_data->edev.name = pdata->name;
|
extcon_data->edev = devm_extcon_dev_allocate(&pdev->dev, NULL);
|
||||||
extcon_data->edev.dev.parent = &pdev->dev;
|
if (IS_ERR(extcon_data->edev)) {
|
||||||
|
dev_err(&pdev->dev, "failed to allocate extcon device\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
extcon_data->edev->name = pdata->name;
|
||||||
|
extcon_data->edev->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
extcon_data->gpio = pdata->gpio;
|
extcon_data->gpio = pdata->gpio;
|
||||||
extcon_data->gpio_active_low = pdata->gpio_active_low;
|
extcon_data->gpio_active_low = pdata->gpio_active_low;
|
||||||
extcon_data->state_on = pdata->state_on;
|
extcon_data->state_on = pdata->state_on;
|
||||||
extcon_data->state_off = pdata->state_off;
|
extcon_data->state_off = pdata->state_off;
|
||||||
extcon_data->check_on_resume = pdata->check_on_resume;
|
extcon_data->check_on_resume = pdata->check_on_resume;
|
||||||
if (pdata->state_on && pdata->state_off)
|
if (pdata->state_on && pdata->state_off)
|
||||||
extcon_data->edev.print_state = extcon_gpio_print_state;
|
extcon_data->edev->print_state = extcon_gpio_print_state;
|
||||||
|
|
||||||
ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN,
|
ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN,
|
||||||
pdev->name);
|
pdev->name);
|
||||||
@ -121,34 +128,27 @@ static int gpio_extcon_probe(struct platform_device *pdev)
|
|||||||
msecs_to_jiffies(pdata->debounce);
|
msecs_to_jiffies(pdata->debounce);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = extcon_dev_register(&extcon_data->edev);
|
ret = devm_extcon_dev_register(&pdev->dev, extcon_data->edev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work);
|
INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work);
|
||||||
|
|
||||||
extcon_data->irq = gpio_to_irq(extcon_data->gpio);
|
extcon_data->irq = gpio_to_irq(extcon_data->gpio);
|
||||||
if (extcon_data->irq < 0) {
|
if (extcon_data->irq < 0)
|
||||||
ret = extcon_data->irq;
|
return extcon_data->irq;
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler,
|
ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler,
|
||||||
pdata->irq_flags, pdev->name,
|
pdata->irq_flags, pdev->name,
|
||||||
extcon_data);
|
extcon_data);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err;
|
return ret;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, extcon_data);
|
platform_set_drvdata(pdev, extcon_data);
|
||||||
/* Perform initial detection */
|
/* Perform initial detection */
|
||||||
gpio_extcon_work(&extcon_data->work.work);
|
gpio_extcon_work(&extcon_data->work.work);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
|
||||||
extcon_dev_unregister(&extcon_data->edev);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gpio_extcon_remove(struct platform_device *pdev)
|
static int gpio_extcon_remove(struct platform_device *pdev)
|
||||||
@ -157,7 +157,6 @@ static int gpio_extcon_remove(struct platform_device *pdev)
|
|||||||
|
|
||||||
cancel_delayed_work_sync(&extcon_data->work);
|
cancel_delayed_work_sync(&extcon_data->work);
|
||||||
free_irq(extcon_data->irq, extcon_data);
|
free_irq(extcon_data->irq, extcon_data);
|
||||||
extcon_dev_unregister(&extcon_data->edev);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
* extcon-max14577.c - MAX14577 extcon driver to support MAX14577 MUIC
|
* extcon-max14577.c - MAX14577/77836 extcon driver to support MUIC
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013 Samsung Electrnoics
|
* Copyright (C) 2013,2014 Samsung Electrnoics
|
||||||
* Chanwoo Choi <cw00.choi@samsung.com>
|
* Chanwoo Choi <cw00.choi@samsung.com>
|
||||||
|
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -24,7 +25,6 @@
|
|||||||
#include <linux/mfd/max14577-private.h>
|
#include <linux/mfd/max14577-private.h>
|
||||||
#include <linux/extcon.h>
|
#include <linux/extcon.h>
|
||||||
|
|
||||||
#define DEV_NAME "max14577-muic"
|
|
||||||
#define DELAY_MS_DEFAULT 17000 /* unit: millisecond */
|
#define DELAY_MS_DEFAULT 17000 /* unit: millisecond */
|
||||||
|
|
||||||
enum max14577_muic_adc_debounce_time {
|
enum max14577_muic_adc_debounce_time {
|
||||||
@ -40,6 +40,42 @@ enum max14577_muic_status {
|
|||||||
MAX14577_MUIC_STATUS_END,
|
MAX14577_MUIC_STATUS_END,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct max14577_muic_irq
|
||||||
|
* @irq: the index of irq list of MUIC device.
|
||||||
|
* @name: the name of irq.
|
||||||
|
* @virq: the virtual irq to use irq domain
|
||||||
|
*/
|
||||||
|
struct max14577_muic_irq {
|
||||||
|
unsigned int irq;
|
||||||
|
const char *name;
|
||||||
|
unsigned int virq;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct max14577_muic_irq max14577_muic_irqs[] = {
|
||||||
|
{ MAX14577_IRQ_INT1_ADC, "muic-ADC" },
|
||||||
|
{ MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" },
|
||||||
|
{ MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" },
|
||||||
|
{ MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" },
|
||||||
|
{ MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" },
|
||||||
|
{ MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" },
|
||||||
|
{ MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" },
|
||||||
|
{ MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct max14577_muic_irq max77836_muic_irqs[] = {
|
||||||
|
{ MAX14577_IRQ_INT1_ADC, "muic-ADC" },
|
||||||
|
{ MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" },
|
||||||
|
{ MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" },
|
||||||
|
{ MAX77836_IRQ_INT1_ADC1K, "muic-ADC1K" },
|
||||||
|
{ MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" },
|
||||||
|
{ MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" },
|
||||||
|
{ MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" },
|
||||||
|
{ MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" },
|
||||||
|
{ MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" },
|
||||||
|
{ MAX77836_IRQ_INT2_VIDRM, "muic-VIDRM" },
|
||||||
|
};
|
||||||
|
|
||||||
struct max14577_muic_info {
|
struct max14577_muic_info {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct max14577 *max14577;
|
struct max14577 *max14577;
|
||||||
@ -48,6 +84,8 @@ struct max14577_muic_info {
|
|||||||
int prev_chg_type;
|
int prev_chg_type;
|
||||||
u8 status[MAX14577_MUIC_STATUS_END];
|
u8 status[MAX14577_MUIC_STATUS_END];
|
||||||
|
|
||||||
|
struct max14577_muic_irq *muic_irqs;
|
||||||
|
unsigned int muic_irqs_num;
|
||||||
bool irq_adc;
|
bool irq_adc;
|
||||||
bool irq_chg;
|
bool irq_chg;
|
||||||
struct work_struct irq_work;
|
struct work_struct irq_work;
|
||||||
@ -74,29 +112,6 @@ enum max14577_muic_cable_group {
|
|||||||
MAX14577_CABLE_GROUP_CHG,
|
MAX14577_CABLE_GROUP_CHG,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* struct max14577_muic_irq
|
|
||||||
* @irq: the index of irq list of MUIC device.
|
|
||||||
* @name: the name of irq.
|
|
||||||
* @virq: the virtual irq to use irq domain
|
|
||||||
*/
|
|
||||||
struct max14577_muic_irq {
|
|
||||||
unsigned int irq;
|
|
||||||
const char *name;
|
|
||||||
unsigned int virq;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct max14577_muic_irq muic_irqs[] = {
|
|
||||||
{ MAX14577_IRQ_INT1_ADC, "muic-ADC" },
|
|
||||||
{ MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" },
|
|
||||||
{ MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" },
|
|
||||||
{ MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" },
|
|
||||||
{ MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" },
|
|
||||||
{ MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" },
|
|
||||||
{ MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" },
|
|
||||||
{ MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" },
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Define supported accessory type */
|
/* Define supported accessory type */
|
||||||
enum max14577_muic_acc_type {
|
enum max14577_muic_acc_type {
|
||||||
MAX14577_MUIC_ADC_GROUND = 0x0,
|
MAX14577_MUIC_ADC_GROUND = 0x0,
|
||||||
@ -528,21 +543,12 @@ static void max14577_muic_irq_work(struct work_struct *work)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
|
/*
|
||||||
|
* Sets irq_adc or irq_chg in max14577_muic_info and returns 1.
|
||||||
|
* Returns 0 if irq_type does not match registered IRQ for this device type.
|
||||||
|
*/
|
||||||
|
static int max14577_parse_irq(struct max14577_muic_info *info, int irq_type)
|
||||||
{
|
{
|
||||||
struct max14577_muic_info *info = data;
|
|
||||||
int i, irq_type = -1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We may be called multiple times for different nested IRQ-s.
|
|
||||||
* Including changes in INT1_ADC and INT2_CGHTYP at once.
|
|
||||||
* However we only need to know whether it was ADC, charger
|
|
||||||
* or both interrupts so decode IRQ and turn on proper flags.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
|
|
||||||
if (irq == muic_irqs[i].virq)
|
|
||||||
irq_type = muic_irqs[i].irq;
|
|
||||||
|
|
||||||
switch (irq_type) {
|
switch (irq_type) {
|
||||||
case MAX14577_IRQ_INT1_ADC:
|
case MAX14577_IRQ_INT1_ADC:
|
||||||
case MAX14577_IRQ_INT1_ADCLOW:
|
case MAX14577_IRQ_INT1_ADCLOW:
|
||||||
@ -550,7 +556,7 @@ static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
|
|||||||
/* Handle all of accessory except for
|
/* Handle all of accessory except for
|
||||||
type of charger accessory */
|
type of charger accessory */
|
||||||
info->irq_adc = true;
|
info->irq_adc = true;
|
||||||
break;
|
return 1;
|
||||||
case MAX14577_IRQ_INT2_CHGTYP:
|
case MAX14577_IRQ_INT2_CHGTYP:
|
||||||
case MAX14577_IRQ_INT2_CHGDETRUN:
|
case MAX14577_IRQ_INT2_CHGDETRUN:
|
||||||
case MAX14577_IRQ_INT2_DCDTMR:
|
case MAX14577_IRQ_INT2_DCDTMR:
|
||||||
@ -558,8 +564,62 @@ static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
|
|||||||
case MAX14577_IRQ_INT2_VBVOLT:
|
case MAX14577_IRQ_INT2_VBVOLT:
|
||||||
/* Handle charger accessory */
|
/* Handle charger accessory */
|
||||||
info->irq_chg = true;
|
info->irq_chg = true;
|
||||||
break;
|
return 1;
|
||||||
default:
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets irq_adc or irq_chg in max14577_muic_info and returns 1.
|
||||||
|
* Returns 0 if irq_type does not match registered IRQ for this device type.
|
||||||
|
*/
|
||||||
|
static int max77836_parse_irq(struct max14577_muic_info *info, int irq_type)
|
||||||
|
{
|
||||||
|
/* First check common max14577 interrupts */
|
||||||
|
if (max14577_parse_irq(info, irq_type))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
switch (irq_type) {
|
||||||
|
case MAX77836_IRQ_INT1_ADC1K:
|
||||||
|
info->irq_adc = true;
|
||||||
|
return 1;
|
||||||
|
case MAX77836_IRQ_INT2_VIDRM:
|
||||||
|
/* Handle charger accessory */
|
||||||
|
info->irq_chg = true;
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct max14577_muic_info *info = data;
|
||||||
|
int i, irq_type = -1;
|
||||||
|
bool irq_parsed;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We may be called multiple times for different nested IRQ-s.
|
||||||
|
* Including changes in INT1_ADC and INT2_CGHTYP at once.
|
||||||
|
* However we only need to know whether it was ADC, charger
|
||||||
|
* or both interrupts so decode IRQ and turn on proper flags.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < info->muic_irqs_num; i++)
|
||||||
|
if (irq == info->muic_irqs[i].virq)
|
||||||
|
irq_type = info->muic_irqs[i].irq;
|
||||||
|
|
||||||
|
switch (info->max14577->dev_type) {
|
||||||
|
case MAXIM_DEVICE_TYPE_MAX77836:
|
||||||
|
irq_parsed = max77836_parse_irq(info, irq_type);
|
||||||
|
break;
|
||||||
|
case MAXIM_DEVICE_TYPE_MAX14577:
|
||||||
|
default:
|
||||||
|
irq_parsed = max14577_parse_irq(info, irq_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!irq_parsed) {
|
||||||
dev_err(info->dev, "muic interrupt: irq %d occurred, skipped\n",
|
dev_err(info->dev, "muic interrupt: irq %d occurred, skipped\n",
|
||||||
irq_type);
|
irq_type);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
@ -644,13 +704,24 @@ static int max14577_muic_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
INIT_WORK(&info->irq_work, max14577_muic_irq_work);
|
INIT_WORK(&info->irq_work, max14577_muic_irq_work);
|
||||||
|
|
||||||
|
switch (max14577->dev_type) {
|
||||||
|
case MAXIM_DEVICE_TYPE_MAX77836:
|
||||||
|
info->muic_irqs = max77836_muic_irqs;
|
||||||
|
info->muic_irqs_num = ARRAY_SIZE(max77836_muic_irqs);
|
||||||
|
break;
|
||||||
|
case MAXIM_DEVICE_TYPE_MAX14577:
|
||||||
|
default:
|
||||||
|
info->muic_irqs = max14577_muic_irqs;
|
||||||
|
info->muic_irqs_num = ARRAY_SIZE(max14577_muic_irqs);
|
||||||
|
}
|
||||||
|
|
||||||
/* Support irq domain for max14577 MUIC device */
|
/* Support irq domain for max14577 MUIC device */
|
||||||
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
|
for (i = 0; i < info->muic_irqs_num; i++) {
|
||||||
struct max14577_muic_irq *muic_irq = &muic_irqs[i];
|
struct max14577_muic_irq *muic_irq = &info->muic_irqs[i];
|
||||||
unsigned int virq = 0;
|
unsigned int virq = 0;
|
||||||
|
|
||||||
virq = regmap_irq_get_virq(max14577->irq_data, muic_irq->irq);
|
virq = regmap_irq_get_virq(max14577->irq_data, muic_irq->irq);
|
||||||
if (!virq)
|
if (virq <= 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
muic_irq->virq = virq;
|
muic_irq->virq = virq;
|
||||||
|
|
||||||
@ -668,14 +739,16 @@ static int max14577_muic_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize extcon device */
|
/* Initialize extcon device */
|
||||||
info->edev = devm_kzalloc(&pdev->dev, sizeof(*info->edev), GFP_KERNEL);
|
info->edev = devm_extcon_dev_allocate(&pdev->dev,
|
||||||
if (!info->edev) {
|
max14577_extcon_cable);
|
||||||
|
if (IS_ERR(info->edev)) {
|
||||||
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
|
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
info->edev->name = DEV_NAME;
|
|
||||||
info->edev->supported_cable = max14577_extcon_cable;
|
info->edev->name = dev_name(&pdev->dev);
|
||||||
ret = extcon_dev_register(info->edev);
|
|
||||||
|
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "failed to register extcon device\n");
|
dev_err(&pdev->dev, "failed to register extcon device\n");
|
||||||
return ret;
|
return ret;
|
||||||
@ -694,7 +767,7 @@ static int max14577_muic_probe(struct platform_device *pdev)
|
|||||||
MAX14577_REG_DEVICEID, &id);
|
MAX14577_REG_DEVICEID, &id);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "failed to read revision number\n");
|
dev_err(&pdev->dev, "failed to read revision number\n");
|
||||||
goto err_extcon;
|
return ret;
|
||||||
}
|
}
|
||||||
dev_info(info->dev, "device ID : 0x%x\n", id);
|
dev_info(info->dev, "device ID : 0x%x\n", id);
|
||||||
|
|
||||||
@ -710,19 +783,10 @@ static int max14577_muic_probe(struct platform_device *pdev)
|
|||||||
* driver should notify cable state to upper layer.
|
* driver should notify cable state to upper layer.
|
||||||
*/
|
*/
|
||||||
INIT_DELAYED_WORK(&info->wq_detcable, max14577_muic_detect_cable_wq);
|
INIT_DELAYED_WORK(&info->wq_detcable, max14577_muic_detect_cable_wq);
|
||||||
ret = queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
|
queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
|
||||||
delay_jiffies);
|
delay_jiffies);
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&pdev->dev,
|
|
||||||
"failed to schedule delayed work for cable detect\n");
|
|
||||||
goto err_extcon;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
err_extcon:
|
|
||||||
extcon_dev_unregister(info->edev);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int max14577_muic_remove(struct platform_device *pdev)
|
static int max14577_muic_remove(struct platform_device *pdev)
|
||||||
@ -730,23 +794,30 @@ static int max14577_muic_remove(struct platform_device *pdev)
|
|||||||
struct max14577_muic_info *info = platform_get_drvdata(pdev);
|
struct max14577_muic_info *info = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
cancel_work_sync(&info->irq_work);
|
cancel_work_sync(&info->irq_work);
|
||||||
extcon_dev_unregister(info->edev);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct platform_device_id max14577_muic_id[] = {
|
||||||
|
{ "max14577-muic", MAXIM_DEVICE_TYPE_MAX14577, },
|
||||||
|
{ "max77836-muic", MAXIM_DEVICE_TYPE_MAX77836, },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(platform, max14577_muic_id);
|
||||||
|
|
||||||
static struct platform_driver max14577_muic_driver = {
|
static struct platform_driver max14577_muic_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DEV_NAME,
|
.name = "max14577-muic",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
},
|
},
|
||||||
.probe = max14577_muic_probe,
|
.probe = max14577_muic_probe,
|
||||||
.remove = max14577_muic_remove,
|
.remove = max14577_muic_remove,
|
||||||
|
.id_table = max14577_muic_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_platform_driver(max14577_muic_driver);
|
module_platform_driver(max14577_muic_driver);
|
||||||
|
|
||||||
MODULE_DESCRIPTION("MAXIM 14577 Extcon driver");
|
MODULE_DESCRIPTION("Maxim 14577/77836 Extcon driver");
|
||||||
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
|
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_ALIAS("platform:extcon-max14577");
|
MODULE_ALIAS("platform:extcon-max14577");
|
||||||
|
@ -1175,25 +1175,24 @@ static int max77693_muic_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize extcon device */
|
/* Initialize extcon device */
|
||||||
info->edev = devm_kzalloc(&pdev->dev, sizeof(struct extcon_dev),
|
info->edev = devm_extcon_dev_allocate(&pdev->dev,
|
||||||
GFP_KERNEL);
|
max77693_extcon_cable);
|
||||||
if (!info->edev) {
|
if (IS_ERR(info->edev)) {
|
||||||
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
|
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto err_irq;
|
goto err_irq;
|
||||||
}
|
}
|
||||||
info->edev->name = DEV_NAME;
|
info->edev->name = DEV_NAME;
|
||||||
info->edev->dev.parent = &pdev->dev;
|
info->edev->dev.parent = &pdev->dev;
|
||||||
info->edev->supported_cable = max77693_extcon_cable;
|
|
||||||
ret = extcon_dev_register(info->edev);
|
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "failed to register extcon device\n");
|
dev_err(&pdev->dev, "failed to register extcon device\n");
|
||||||
goto err_irq;
|
goto err_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Initialize MUIC register by using platform data or default data */
|
/* Initialize MUIC register by using platform data or default data */
|
||||||
if (pdata->muic_data) {
|
if (pdata && pdata->muic_data) {
|
||||||
init_data = pdata->muic_data->init_data;
|
init_data = pdata->muic_data->init_data;
|
||||||
num_init_data = pdata->muic_data->num_init_data;
|
num_init_data = pdata->muic_data->num_init_data;
|
||||||
} else {
|
} else {
|
||||||
@ -1226,7 +1225,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
|
|||||||
= init_data[i].data;
|
= init_data[i].data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdata->muic_data) {
|
if (pdata && pdata->muic_data) {
|
||||||
struct max77693_muic_platform_data *muic_pdata
|
struct max77693_muic_platform_data *muic_pdata
|
||||||
= pdata->muic_data;
|
= pdata->muic_data;
|
||||||
|
|
||||||
@ -1267,7 +1266,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
|
|||||||
MAX77693_MUIC_REG_ID, &id);
|
MAX77693_MUIC_REG_ID, &id);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "failed to read revision number\n");
|
dev_err(&pdev->dev, "failed to read revision number\n");
|
||||||
goto err_extcon;
|
goto err_irq;
|
||||||
}
|
}
|
||||||
dev_info(info->dev, "device ID : 0x%x\n", id);
|
dev_info(info->dev, "device ID : 0x%x\n", id);
|
||||||
|
|
||||||
@ -1283,12 +1282,11 @@ static int max77693_muic_probe(struct platform_device *pdev)
|
|||||||
* driver should notify cable state to upper layer.
|
* driver should notify cable state to upper layer.
|
||||||
*/
|
*/
|
||||||
INIT_DELAYED_WORK(&info->wq_detcable, max77693_muic_detect_cable_wq);
|
INIT_DELAYED_WORK(&info->wq_detcable, max77693_muic_detect_cable_wq);
|
||||||
schedule_delayed_work(&info->wq_detcable, delay_jiffies);
|
queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
|
||||||
|
delay_jiffies);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
err_extcon:
|
|
||||||
extcon_dev_unregister(info->edev);
|
|
||||||
err_irq:
|
err_irq:
|
||||||
while (--i >= 0)
|
while (--i >= 0)
|
||||||
free_irq(muic_irqs[i].virq, info);
|
free_irq(muic_irqs[i].virq, info);
|
||||||
@ -1304,7 +1302,6 @@ static int max77693_muic_remove(struct platform_device *pdev)
|
|||||||
free_irq(muic_irqs[i].virq, info);
|
free_irq(muic_irqs[i].virq, info);
|
||||||
cancel_work_sync(&info->irq_work);
|
cancel_work_sync(&info->irq_work);
|
||||||
input_unregister_device(info->dock);
|
input_unregister_device(info->dock);
|
||||||
extcon_dev_unregister(info->edev);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -699,23 +699,22 @@ static int max8997_muic_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* External connector */
|
/* External connector */
|
||||||
info->edev = devm_kzalloc(&pdev->dev, sizeof(struct extcon_dev),
|
info->edev = devm_extcon_dev_allocate(&pdev->dev, max8997_extcon_cable);
|
||||||
GFP_KERNEL);
|
if (IS_ERR(info->edev)) {
|
||||||
if (!info->edev) {
|
|
||||||
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
|
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto err_irq;
|
goto err_irq;
|
||||||
}
|
}
|
||||||
info->edev->name = DEV_NAME;
|
info->edev->name = DEV_NAME;
|
||||||
info->edev->dev.parent = &pdev->dev;
|
info->edev->dev.parent = &pdev->dev;
|
||||||
info->edev->supported_cable = max8997_extcon_cable;
|
|
||||||
ret = extcon_dev_register(info->edev);
|
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "failed to register extcon device\n");
|
dev_err(&pdev->dev, "failed to register extcon device\n");
|
||||||
goto err_irq;
|
goto err_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdata->muic_pdata) {
|
if (pdata && pdata->muic_pdata) {
|
||||||
struct max8997_muic_platform_data *muic_pdata
|
struct max8997_muic_platform_data *muic_pdata
|
||||||
= pdata->muic_pdata;
|
= pdata->muic_pdata;
|
||||||
|
|
||||||
@ -770,7 +769,8 @@ static int max8997_muic_probe(struct platform_device *pdev)
|
|||||||
* driver should notify cable state to upper layer.
|
* driver should notify cable state to upper layer.
|
||||||
*/
|
*/
|
||||||
INIT_DELAYED_WORK(&info->wq_detcable, max8997_muic_detect_cable_wq);
|
INIT_DELAYED_WORK(&info->wq_detcable, max8997_muic_detect_cable_wq);
|
||||||
schedule_delayed_work(&info->wq_detcable, delay_jiffies);
|
queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
|
||||||
|
delay_jiffies);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -789,8 +789,6 @@ static int max8997_muic_remove(struct platform_device *pdev)
|
|||||||
free_irq(muic_irqs[i].virq, info);
|
free_irq(muic_irqs[i].virq, info);
|
||||||
cancel_work_sync(&info->irq_work);
|
cancel_work_sync(&info->irq_work);
|
||||||
|
|
||||||
extcon_dev_unregister(info->edev);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/mfd/palmas.h>
|
#include <linux/mfd/palmas.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
@ -56,7 +57,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
|
|||||||
if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) {
|
if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) {
|
||||||
if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) {
|
if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) {
|
||||||
palmas_usb->linkstat = PALMAS_USB_STATE_VBUS;
|
palmas_usb->linkstat = PALMAS_USB_STATE_VBUS;
|
||||||
extcon_set_cable_state(&palmas_usb->edev, "USB", true);
|
extcon_set_cable_state(palmas_usb->edev, "USB", true);
|
||||||
dev_info(palmas_usb->dev, "USB cable is attached\n");
|
dev_info(palmas_usb->dev, "USB cable is attached\n");
|
||||||
} else {
|
} else {
|
||||||
dev_dbg(palmas_usb->dev,
|
dev_dbg(palmas_usb->dev,
|
||||||
@ -65,7 +66,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
|
|||||||
} else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) {
|
} else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) {
|
||||||
if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) {
|
if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) {
|
||||||
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
|
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
|
||||||
extcon_set_cable_state(&palmas_usb->edev, "USB", false);
|
extcon_set_cable_state(palmas_usb->edev, "USB", false);
|
||||||
dev_info(palmas_usb->dev, "USB cable is detached\n");
|
dev_info(palmas_usb->dev, "USB cable is detached\n");
|
||||||
} else {
|
} else {
|
||||||
dev_dbg(palmas_usb->dev,
|
dev_dbg(palmas_usb->dev,
|
||||||
@ -92,7 +93,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
|
|||||||
PALMAS_USB_ID_INT_LATCH_CLR,
|
PALMAS_USB_ID_INT_LATCH_CLR,
|
||||||
PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
|
PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
|
||||||
palmas_usb->linkstat = PALMAS_USB_STATE_ID;
|
palmas_usb->linkstat = PALMAS_USB_STATE_ID;
|
||||||
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true);
|
extcon_set_cable_state(palmas_usb->edev, "USB-HOST", true);
|
||||||
dev_info(palmas_usb->dev, "USB-HOST cable is attached\n");
|
dev_info(palmas_usb->dev, "USB-HOST cable is attached\n");
|
||||||
} else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) &&
|
} else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) &&
|
||||||
(id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) {
|
(id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) {
|
||||||
@ -100,17 +101,17 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
|
|||||||
PALMAS_USB_ID_INT_LATCH_CLR,
|
PALMAS_USB_ID_INT_LATCH_CLR,
|
||||||
PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
|
PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
|
||||||
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
|
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
|
||||||
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false);
|
extcon_set_cable_state(palmas_usb->edev, "USB-HOST", false);
|
||||||
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
|
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
|
||||||
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) &&
|
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) &&
|
||||||
(!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) {
|
(!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) {
|
||||||
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
|
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
|
||||||
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false);
|
extcon_set_cable_state(palmas_usb->edev, "USB-HOST", false);
|
||||||
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
|
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
|
||||||
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) &&
|
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) &&
|
||||||
(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
|
(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
|
||||||
palmas_usb->linkstat = PALMAS_USB_STATE_ID;
|
palmas_usb->linkstat = PALMAS_USB_STATE_ID;
|
||||||
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true);
|
extcon_set_cable_state(palmas_usb->edev, "USB-HOST", true);
|
||||||
dev_info(palmas_usb->dev, " USB-HOST cable is attached\n");
|
dev_info(palmas_usb->dev, " USB-HOST cable is attached\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,13 +187,20 @@ static int palmas_usb_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
platform_set_drvdata(pdev, palmas_usb);
|
platform_set_drvdata(pdev, palmas_usb);
|
||||||
|
|
||||||
palmas_usb->edev.supported_cable = palmas_extcon_cable;
|
palmas_usb->edev = devm_extcon_dev_allocate(&pdev->dev,
|
||||||
palmas_usb->edev.dev.parent = palmas_usb->dev;
|
palmas_extcon_cable);
|
||||||
palmas_usb->edev.mutually_exclusive = mutually_exclusive;
|
if (IS_ERR(palmas_usb->edev)) {
|
||||||
|
dev_err(&pdev->dev, "failed to allocate extcon device\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
palmas_usb->edev->name = kstrdup(node->name, GFP_KERNEL);
|
||||||
|
palmas_usb->edev->dev.parent = palmas_usb->dev;
|
||||||
|
palmas_usb->edev->mutually_exclusive = mutually_exclusive;
|
||||||
|
|
||||||
status = extcon_dev_register(&palmas_usb->edev);
|
status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev);
|
||||||
if (status) {
|
if (status) {
|
||||||
dev_err(&pdev->dev, "failed to register extcon device\n");
|
dev_err(&pdev->dev, "failed to register extcon device\n");
|
||||||
|
kfree(palmas_usb->edev->name);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +214,8 @@ static int palmas_usb_probe(struct platform_device *pdev)
|
|||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
|
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
|
||||||
palmas_usb->id_irq, status);
|
palmas_usb->id_irq, status);
|
||||||
goto fail_extcon;
|
kfree(palmas_usb->edev->name);
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,25 +229,21 @@ static int palmas_usb_probe(struct platform_device *pdev)
|
|||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
|
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
|
||||||
palmas_usb->vbus_irq, status);
|
palmas_usb->vbus_irq, status);
|
||||||
goto fail_extcon;
|
kfree(palmas_usb->edev->name);
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
palmas_enable_irq(palmas_usb);
|
palmas_enable_irq(palmas_usb);
|
||||||
device_set_wakeup_capable(&pdev->dev, true);
|
device_set_wakeup_capable(&pdev->dev, true);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail_extcon:
|
|
||||||
extcon_dev_unregister(&palmas_usb->edev);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int palmas_usb_remove(struct platform_device *pdev)
|
static int palmas_usb_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct palmas_usb *palmas_usb = platform_get_drvdata(pdev);
|
struct palmas_usb *palmas_usb = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
extcon_dev_unregister(&palmas_usb->edev);
|
kfree(palmas_usb->edev->name);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -471,18 +471,26 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl);
|
EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl);
|
||||||
|
|
||||||
|
static void reset_channel_cb(void *arg)
|
||||||
|
{
|
||||||
|
struct vmbus_channel *channel = arg;
|
||||||
|
|
||||||
|
channel->onchannel_callback = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void vmbus_close_internal(struct vmbus_channel *channel)
|
static void vmbus_close_internal(struct vmbus_channel *channel)
|
||||||
{
|
{
|
||||||
struct vmbus_channel_close_channel *msg;
|
struct vmbus_channel_close_channel *msg;
|
||||||
int ret;
|
int ret;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
channel->state = CHANNEL_OPEN_STATE;
|
channel->state = CHANNEL_OPEN_STATE;
|
||||||
channel->sc_creation_callback = NULL;
|
channel->sc_creation_callback = NULL;
|
||||||
/* Stop callback and cancel the timer asap */
|
/* Stop callback and cancel the timer asap */
|
||||||
spin_lock_irqsave(&channel->inbound_lock, flags);
|
if (channel->target_cpu != smp_processor_id())
|
||||||
channel->onchannel_callback = NULL;
|
smp_call_function_single(channel->target_cpu, reset_channel_cb,
|
||||||
spin_unlock_irqrestore(&channel->inbound_lock, flags);
|
channel, true);
|
||||||
|
else
|
||||||
|
reset_channel_cb(channel);
|
||||||
|
|
||||||
/* Send a closing message */
|
/* Send a closing message */
|
||||||
|
|
||||||
@ -674,8 +682,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
|
|||||||
u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset,
|
u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset,
|
||||||
multi_pagebuffer->len);
|
multi_pagebuffer->len);
|
||||||
|
|
||||||
|
if (pfncount > MAX_MULTIPAGE_BUFFER_COUNT)
|
||||||
if ((pfncount < 0) || (pfncount > MAX_MULTIPAGE_BUFFER_COUNT))
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -149,6 +149,7 @@ static struct vmbus_channel *alloc_channel(void)
|
|||||||
spin_lock_init(&channel->sc_lock);
|
spin_lock_init(&channel->sc_lock);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&channel->sc_list);
|
INIT_LIST_HEAD(&channel->sc_list);
|
||||||
|
INIT_LIST_HEAD(&channel->percpu_list);
|
||||||
|
|
||||||
channel->controlwq = create_workqueue("hv_vmbus_ctl");
|
channel->controlwq = create_workqueue("hv_vmbus_ctl");
|
||||||
if (!channel->controlwq) {
|
if (!channel->controlwq) {
|
||||||
@ -188,7 +189,20 @@ static void free_channel(struct vmbus_channel *channel)
|
|||||||
queue_work(vmbus_connection.work_queue, &channel->work);
|
queue_work(vmbus_connection.work_queue, &channel->work);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void percpu_channel_enq(void *arg)
|
||||||
|
{
|
||||||
|
struct vmbus_channel *channel = arg;
|
||||||
|
int cpu = smp_processor_id();
|
||||||
|
|
||||||
|
list_add_tail(&channel->percpu_list, &hv_context.percpu_list[cpu]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void percpu_channel_deq(void *arg)
|
||||||
|
{
|
||||||
|
struct vmbus_channel *channel = arg;
|
||||||
|
|
||||||
|
list_del(&channel->percpu_list);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* vmbus_process_rescind_offer -
|
* vmbus_process_rescind_offer -
|
||||||
@ -210,6 +224,12 @@ static void vmbus_process_rescind_offer(struct work_struct *work)
|
|||||||
msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
|
msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
|
||||||
vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released));
|
vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released));
|
||||||
|
|
||||||
|
if (channel->target_cpu != smp_processor_id())
|
||||||
|
smp_call_function_single(channel->target_cpu,
|
||||||
|
percpu_channel_deq, channel, true);
|
||||||
|
else
|
||||||
|
percpu_channel_deq(channel);
|
||||||
|
|
||||||
if (channel->primary_channel == NULL) {
|
if (channel->primary_channel == NULL) {
|
||||||
spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
|
spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
|
||||||
list_del(&channel->listentry);
|
list_del(&channel->listentry);
|
||||||
@ -245,6 +265,7 @@ static void vmbus_process_offer(struct work_struct *work)
|
|||||||
work);
|
work);
|
||||||
struct vmbus_channel *channel;
|
struct vmbus_channel *channel;
|
||||||
bool fnew = true;
|
bool fnew = true;
|
||||||
|
bool enq = false;
|
||||||
int ret;
|
int ret;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
@ -264,12 +285,22 @@ static void vmbus_process_offer(struct work_struct *work)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fnew)
|
if (fnew) {
|
||||||
list_add_tail(&newchannel->listentry,
|
list_add_tail(&newchannel->listentry,
|
||||||
&vmbus_connection.chn_list);
|
&vmbus_connection.chn_list);
|
||||||
|
enq = true;
|
||||||
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
|
spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
|
||||||
|
|
||||||
|
if (enq) {
|
||||||
|
if (newchannel->target_cpu != smp_processor_id())
|
||||||
|
smp_call_function_single(newchannel->target_cpu,
|
||||||
|
percpu_channel_enq,
|
||||||
|
newchannel, true);
|
||||||
|
else
|
||||||
|
percpu_channel_enq(newchannel);
|
||||||
|
}
|
||||||
if (!fnew) {
|
if (!fnew) {
|
||||||
/*
|
/*
|
||||||
* Check to see if this is a sub-channel.
|
* Check to see if this is a sub-channel.
|
||||||
@ -282,6 +313,14 @@ static void vmbus_process_offer(struct work_struct *work)
|
|||||||
spin_lock_irqsave(&channel->sc_lock, flags);
|
spin_lock_irqsave(&channel->sc_lock, flags);
|
||||||
list_add_tail(&newchannel->sc_list, &channel->sc_list);
|
list_add_tail(&newchannel->sc_list, &channel->sc_list);
|
||||||
spin_unlock_irqrestore(&channel->sc_lock, flags);
|
spin_unlock_irqrestore(&channel->sc_lock, flags);
|
||||||
|
|
||||||
|
if (newchannel->target_cpu != smp_processor_id())
|
||||||
|
smp_call_function_single(newchannel->target_cpu,
|
||||||
|
percpu_channel_enq,
|
||||||
|
newchannel, true);
|
||||||
|
else
|
||||||
|
percpu_channel_enq(newchannel);
|
||||||
|
|
||||||
newchannel->state = CHANNEL_OPEN_STATE;
|
newchannel->state = CHANNEL_OPEN_STATE;
|
||||||
if (channel->sc_creation_callback != NULL)
|
if (channel->sc_creation_callback != NULL)
|
||||||
channel->sc_creation_callback(newchannel);
|
channel->sc_creation_callback(newchannel);
|
||||||
@ -365,7 +404,7 @@ static u32 next_vp;
|
|||||||
* performance critical channels (IDE, SCSI and Network) will be uniformly
|
* performance critical channels (IDE, SCSI and Network) will be uniformly
|
||||||
* distributed across all available CPUs.
|
* distributed across all available CPUs.
|
||||||
*/
|
*/
|
||||||
static u32 get_vp_index(uuid_le *type_guid)
|
static void init_vp_index(struct vmbus_channel *channel, uuid_le *type_guid)
|
||||||
{
|
{
|
||||||
u32 cur_cpu;
|
u32 cur_cpu;
|
||||||
int i;
|
int i;
|
||||||
@ -387,10 +426,13 @@ static u32 get_vp_index(uuid_le *type_guid)
|
|||||||
* Also if the channel is not a performance critical
|
* Also if the channel is not a performance critical
|
||||||
* channel, bind it to cpu 0.
|
* channel, bind it to cpu 0.
|
||||||
*/
|
*/
|
||||||
return 0;
|
channel->target_cpu = 0;
|
||||||
|
channel->target_vp = 0;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
cur_cpu = (++next_vp % max_cpus);
|
cur_cpu = (++next_vp % max_cpus);
|
||||||
return hv_context.vp_index[cur_cpu];
|
channel->target_cpu = cur_cpu;
|
||||||
|
channel->target_vp = hv_context.vp_index[cur_cpu];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -438,7 +480,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
|
|||||||
offer->connection_id;
|
offer->connection_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
newchannel->target_vp = get_vp_index(&offer->offer.if_type);
|
init_vp_index(newchannel, &offer->offer.if_type);
|
||||||
|
|
||||||
memcpy(&newchannel->offermsg, offer,
|
memcpy(&newchannel->offermsg, offer,
|
||||||
sizeof(struct vmbus_channel_offer_channel));
|
sizeof(struct vmbus_channel_offer_channel));
|
||||||
|
@ -224,8 +224,8 @@ int vmbus_connect(void)
|
|||||||
vmbus_connection.int_page = NULL;
|
vmbus_connection.int_page = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
free_pages((unsigned long)vmbus_connection.monitor_pages[0], 1);
|
free_pages((unsigned long)vmbus_connection.monitor_pages[0], 0);
|
||||||
free_pages((unsigned long)vmbus_connection.monitor_pages[1], 1);
|
free_pages((unsigned long)vmbus_connection.monitor_pages[1], 0);
|
||||||
vmbus_connection.monitor_pages[0] = NULL;
|
vmbus_connection.monitor_pages[0] = NULL;
|
||||||
vmbus_connection.monitor_pages[1] = NULL;
|
vmbus_connection.monitor_pages[1] = NULL;
|
||||||
|
|
||||||
@ -234,6 +234,28 @@ int vmbus_connect(void)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Map the given relid to the corresponding channel based on the
|
||||||
|
* per-cpu list of channels that have been affinitized to this CPU.
|
||||||
|
* This will be used in the channel callback path as we can do this
|
||||||
|
* mapping in a lock-free fashion.
|
||||||
|
*/
|
||||||
|
static struct vmbus_channel *pcpu_relid2channel(u32 relid)
|
||||||
|
{
|
||||||
|
struct vmbus_channel *channel;
|
||||||
|
struct vmbus_channel *found_channel = NULL;
|
||||||
|
int cpu = smp_processor_id();
|
||||||
|
struct list_head *pcpu_head = &hv_context.percpu_list[cpu];
|
||||||
|
|
||||||
|
list_for_each_entry(channel, pcpu_head, percpu_list) {
|
||||||
|
if (channel->offermsg.child_relid == relid) {
|
||||||
|
found_channel = channel;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found_channel;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* relid2channel - Get the channel object given its
|
* relid2channel - Get the channel object given its
|
||||||
@ -277,7 +299,6 @@ struct vmbus_channel *relid2channel(u32 relid)
|
|||||||
static void process_chn_event(u32 relid)
|
static void process_chn_event(u32 relid)
|
||||||
{
|
{
|
||||||
struct vmbus_channel *channel;
|
struct vmbus_channel *channel;
|
||||||
unsigned long flags;
|
|
||||||
void *arg;
|
void *arg;
|
||||||
bool read_state;
|
bool read_state;
|
||||||
u32 bytes_to_read;
|
u32 bytes_to_read;
|
||||||
@ -286,7 +307,7 @@ static void process_chn_event(u32 relid)
|
|||||||
* Find the channel based on this relid and invokes the
|
* Find the channel based on this relid and invokes the
|
||||||
* channel callback to process the event
|
* channel callback to process the event
|
||||||
*/
|
*/
|
||||||
channel = relid2channel(relid);
|
channel = pcpu_relid2channel(relid);
|
||||||
|
|
||||||
if (!channel) {
|
if (!channel) {
|
||||||
pr_err("channel not found for relid - %u\n", relid);
|
pr_err("channel not found for relid - %u\n", relid);
|
||||||
@ -296,13 +317,12 @@ static void process_chn_event(u32 relid)
|
|||||||
/*
|
/*
|
||||||
* A channel once created is persistent even when there
|
* A channel once created is persistent even when there
|
||||||
* is no driver handling the device. An unloading driver
|
* is no driver handling the device. An unloading driver
|
||||||
* sets the onchannel_callback to NULL under the
|
* sets the onchannel_callback to NULL on the same CPU
|
||||||
* protection of the channel inbound_lock. Thus, checking
|
* as where this interrupt is handled (in an interrupt context).
|
||||||
* and invoking the driver specific callback takes care of
|
* Thus, checking and invoking the driver specific callback takes
|
||||||
* orderly unloading of the driver.
|
* care of orderly unloading of the driver.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
spin_lock_irqsave(&channel->inbound_lock, flags);
|
|
||||||
if (channel->onchannel_callback != NULL) {
|
if (channel->onchannel_callback != NULL) {
|
||||||
arg = channel->channel_callback_context;
|
arg = channel->channel_callback_context;
|
||||||
read_state = channel->batched_reading;
|
read_state = channel->batched_reading;
|
||||||
@ -327,7 +347,6 @@ static void process_chn_event(u32 relid)
|
|||||||
pr_err("no channel callback for relid - %u\n", relid);
|
pr_err("no channel callback for relid - %u\n", relid);
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&channel->inbound_lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -383,6 +383,8 @@ void hv_synic_init(void *arg)
|
|||||||
*/
|
*/
|
||||||
rdmsrl(HV_X64_MSR_VP_INDEX, vp_index);
|
rdmsrl(HV_X64_MSR_VP_INDEX, vp_index);
|
||||||
hv_context.vp_index[cpu] = (u32)vp_index;
|
hv_context.vp_index[cpu] = (u32)vp_index;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&hv_context.percpu_list[cpu]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
#include <linux/mman.h>
|
#include <linux/mman.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
@ -459,6 +460,11 @@ static bool do_hot_add;
|
|||||||
*/
|
*/
|
||||||
static uint pressure_report_delay = 45;
|
static uint pressure_report_delay = 45;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The last time we posted a pressure report to host.
|
||||||
|
*/
|
||||||
|
static unsigned long last_post_time;
|
||||||
|
|
||||||
module_param(hot_add, bool, (S_IRUGO | S_IWUSR));
|
module_param(hot_add, bool, (S_IRUGO | S_IWUSR));
|
||||||
MODULE_PARM_DESC(hot_add, "If set attempt memory hot_add");
|
MODULE_PARM_DESC(hot_add, "If set attempt memory hot_add");
|
||||||
|
|
||||||
@ -542,6 +548,7 @@ struct hv_dynmem_device {
|
|||||||
|
|
||||||
static struct hv_dynmem_device dm_device;
|
static struct hv_dynmem_device dm_device;
|
||||||
|
|
||||||
|
static void post_status(struct hv_dynmem_device *dm);
|
||||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||||
|
|
||||||
static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size)
|
static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size)
|
||||||
@ -612,7 +619,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
|
|||||||
* have not been "onlined" within the allowed time.
|
* have not been "onlined" within the allowed time.
|
||||||
*/
|
*/
|
||||||
wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ);
|
wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ);
|
||||||
|
post_status(&dm_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -951,11 +958,17 @@ static void post_status(struct hv_dynmem_device *dm)
|
|||||||
{
|
{
|
||||||
struct dm_status status;
|
struct dm_status status;
|
||||||
struct sysinfo val;
|
struct sysinfo val;
|
||||||
|
unsigned long now = jiffies;
|
||||||
|
unsigned long last_post = last_post_time;
|
||||||
|
|
||||||
if (pressure_report_delay > 0) {
|
if (pressure_report_delay > 0) {
|
||||||
--pressure_report_delay;
|
--pressure_report_delay;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!time_after(now, (last_post_time + HZ)))
|
||||||
|
return;
|
||||||
|
|
||||||
si_meminfo(&val);
|
si_meminfo(&val);
|
||||||
memset(&status, 0, sizeof(struct dm_status));
|
memset(&status, 0, sizeof(struct dm_status));
|
||||||
status.hdr.type = DM_STATUS_REPORT;
|
status.hdr.type = DM_STATUS_REPORT;
|
||||||
@ -983,6 +996,14 @@ static void post_status(struct hv_dynmem_device *dm)
|
|||||||
if (status.hdr.trans_id != atomic_read(&trans_id))
|
if (status.hdr.trans_id != atomic_read(&trans_id))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the last post time that we sampled has changed,
|
||||||
|
* we have raced, don't post the status.
|
||||||
|
*/
|
||||||
|
if (last_post != last_post_time)
|
||||||
|
return;
|
||||||
|
|
||||||
|
last_post_time = jiffies;
|
||||||
vmbus_sendpacket(dm->dev->channel, &status,
|
vmbus_sendpacket(dm->dev->channel, &status,
|
||||||
sizeof(struct dm_status),
|
sizeof(struct dm_status),
|
||||||
(unsigned long)NULL,
|
(unsigned long)NULL,
|
||||||
@ -1117,7 +1138,7 @@ static void balloon_up(struct work_struct *dummy)
|
|||||||
|
|
||||||
if (ret == -EAGAIN)
|
if (ret == -EAGAIN)
|
||||||
msleep(20);
|
msleep(20);
|
||||||
|
post_status(&dm_device);
|
||||||
} while (ret == -EAGAIN);
|
} while (ret == -EAGAIN);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
@ -1144,8 +1165,10 @@ static void balloon_down(struct hv_dynmem_device *dm,
|
|||||||
struct dm_unballoon_response resp;
|
struct dm_unballoon_response resp;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < range_count; i++)
|
for (i = 0; i < range_count; i++) {
|
||||||
free_balloon_pages(dm, &range_array[i]);
|
free_balloon_pages(dm, &range_array[i]);
|
||||||
|
post_status(&dm_device);
|
||||||
|
}
|
||||||
|
|
||||||
if (req->more_pages == 1)
|
if (req->more_pages == 1)
|
||||||
return;
|
return;
|
||||||
|
@ -510,6 +510,11 @@ struct hv_context {
|
|||||||
* basis.
|
* basis.
|
||||||
*/
|
*/
|
||||||
struct tasklet_struct *event_dpc[NR_CPUS];
|
struct tasklet_struct *event_dpc[NR_CPUS];
|
||||||
|
/*
|
||||||
|
* To optimize the mapping of relid to channel, maintain
|
||||||
|
* per-cpu list of the channels based on their CPU affinity.
|
||||||
|
*/
|
||||||
|
struct list_head percpu_list[NR_CPUS];
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct hv_context hv_context;
|
extern struct hv_context hv_context;
|
||||||
|
@ -183,14 +183,14 @@ EXPORT_SYMBOL_GPL(mcb_device_register);
|
|||||||
*
|
*
|
||||||
* Allocate a new @mcb_bus.
|
* Allocate a new @mcb_bus.
|
||||||
*/
|
*/
|
||||||
struct mcb_bus *mcb_alloc_bus(void)
|
struct mcb_bus *mcb_alloc_bus(struct device *carrier)
|
||||||
{
|
{
|
||||||
struct mcb_bus *bus;
|
struct mcb_bus *bus;
|
||||||
int bus_nr;
|
int bus_nr;
|
||||||
|
|
||||||
bus = kzalloc(sizeof(struct mcb_bus), GFP_KERNEL);
|
bus = kzalloc(sizeof(struct mcb_bus), GFP_KERNEL);
|
||||||
if (!bus)
|
if (!bus)
|
||||||
return NULL;
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
bus_nr = ida_simple_get(&mcb_ida, 0, 0, GFP_KERNEL);
|
bus_nr = ida_simple_get(&mcb_ida, 0, 0, GFP_KERNEL);
|
||||||
if (bus_nr < 0) {
|
if (bus_nr < 0) {
|
||||||
@ -200,7 +200,7 @@ struct mcb_bus *mcb_alloc_bus(void)
|
|||||||
|
|
||||||
INIT_LIST_HEAD(&bus->children);
|
INIT_LIST_HEAD(&bus->children);
|
||||||
bus->bus_nr = bus_nr;
|
bus->bus_nr = bus_nr;
|
||||||
|
bus->carrier = carrier;
|
||||||
return bus;
|
return bus;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(mcb_alloc_bus);
|
EXPORT_SYMBOL_GPL(mcb_alloc_bus);
|
||||||
@ -378,6 +378,13 @@ void mcb_release_mem(struct resource *mem)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(mcb_release_mem);
|
EXPORT_SYMBOL_GPL(mcb_release_mem);
|
||||||
|
|
||||||
|
static int __mcb_get_irq(struct mcb_device *dev)
|
||||||
|
{
|
||||||
|
struct resource *irq = &dev->irq;
|
||||||
|
|
||||||
|
return irq->start;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mcb_get_irq() - Get device's IRQ number
|
* mcb_get_irq() - Get device's IRQ number
|
||||||
* @dev: The @mcb_device the IRQ is for
|
* @dev: The @mcb_device the IRQ is for
|
||||||
@ -386,9 +393,12 @@ EXPORT_SYMBOL_GPL(mcb_release_mem);
|
|||||||
*/
|
*/
|
||||||
int mcb_get_irq(struct mcb_device *dev)
|
int mcb_get_irq(struct mcb_device *dev)
|
||||||
{
|
{
|
||||||
struct resource *irq = &dev->irq;
|
struct mcb_bus *bus = dev->bus;
|
||||||
|
|
||||||
return irq->start;
|
if (bus->get_irq)
|
||||||
|
return bus->get_irq(dev);
|
||||||
|
|
||||||
|
return __mcb_get_irq(dev);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(mcb_get_irq);
|
EXPORT_SYMBOL_GPL(mcb_get_irq);
|
||||||
|
|
||||||
|
@ -20,6 +20,15 @@ struct priv {
|
|||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int mcb_pci_get_irq(struct mcb_device *mdev)
|
||||||
|
{
|
||||||
|
struct mcb_bus *mbus = mdev->bus;
|
||||||
|
struct device *dev = mbus->carrier;
|
||||||
|
struct pci_dev *pdev = to_pci_dev(dev);
|
||||||
|
|
||||||
|
return pdev->irq;
|
||||||
|
}
|
||||||
|
|
||||||
static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||||
{
|
{
|
||||||
struct priv *priv;
|
struct priv *priv;
|
||||||
@ -67,7 +76,13 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||||||
|
|
||||||
pci_set_drvdata(pdev, priv);
|
pci_set_drvdata(pdev, priv);
|
||||||
|
|
||||||
priv->bus = mcb_alloc_bus();
|
priv->bus = mcb_alloc_bus(&pdev->dev);
|
||||||
|
if (IS_ERR(priv->bus)) {
|
||||||
|
ret = PTR_ERR(priv->bus);
|
||||||
|
goto err_drvdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->bus->get_irq = mcb_pci_get_irq;
|
||||||
|
|
||||||
ret = chameleon_parse_cells(priv->bus, mapbase, priv->base);
|
ret = chameleon_parse_cells(priv->bus, mapbase, priv->base);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -331,15 +331,15 @@ config MFD_88PM860X
|
|||||||
battery-charger under the corresponding menus.
|
battery-charger under the corresponding menus.
|
||||||
|
|
||||||
config MFD_MAX14577
|
config MFD_MAX14577
|
||||||
bool "Maxim Semiconductor MAX14577 MUIC + Charger Support"
|
bool "Maxim Semiconductor MAX14577/77836 MUIC + Charger Support"
|
||||||
depends on I2C=y
|
depends on I2C=y
|
||||||
select MFD_CORE
|
select MFD_CORE
|
||||||
select REGMAP_I2C
|
select REGMAP_I2C
|
||||||
select REGMAP_IRQ
|
select REGMAP_IRQ
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
help
|
help
|
||||||
Say yes here to add support for Maxim Semiconductor MAX14577.
|
Say yes here to add support for Maxim Semiconductor MAX14577 and
|
||||||
This is a Micro-USB IC with Charger controls on chip.
|
MAX77836 Micro-USB ICs with battery charger.
|
||||||
This driver provides common support for accessing the device;
|
This driver provides common support for accessing the device;
|
||||||
additional drivers must be enabled in order to use the functionality
|
additional drivers must be enabled in order to use the functionality
|
||||||
of the device.
|
of the device.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* max14577.c - mfd core driver for the Maxim 14577
|
* max14577.c - mfd core driver for the Maxim 14577/77836
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013 Samsung Electrnoics
|
* Copyright (C) 2014 Samsung Electrnoics
|
||||||
* Chanwoo Choi <cw00.choi@samsung.com>
|
* Chanwoo Choi <cw00.choi@samsung.com>
|
||||||
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||||
*
|
*
|
||||||
@ -21,6 +21,7 @@
|
|||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
#include <linux/mfd/core.h>
|
#include <linux/mfd/core.h>
|
||||||
#include <linux/mfd/max14577.h>
|
#include <linux/mfd/max14577.h>
|
||||||
#include <linux/mfd/max14577-private.h>
|
#include <linux/mfd/max14577-private.h>
|
||||||
@ -37,7 +38,38 @@ static struct mfd_cell max14577_devs[] = {
|
|||||||
{ .name = "max14577-charger", },
|
{ .name = "max14577-charger", },
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool max14577_volatile_reg(struct device *dev, unsigned int reg)
|
static struct mfd_cell max77836_devs[] = {
|
||||||
|
{
|
||||||
|
.name = "max77836-muic",
|
||||||
|
.of_compatible = "maxim,max77836-muic",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "max77836-regulator",
|
||||||
|
.of_compatible = "maxim,max77836-regulator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "max77836-charger",
|
||||||
|
.of_compatible = "maxim,max77836-charger",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "max77836-battery",
|
||||||
|
.of_compatible = "maxim,max77836-battery",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct of_device_id max14577_dt_match[] = {
|
||||||
|
{
|
||||||
|
.compatible = "maxim,max14577",
|
||||||
|
.data = (void *)MAXIM_DEVICE_TYPE_MAX14577,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "maxim,max77836",
|
||||||
|
.data = (void *)MAXIM_DEVICE_TYPE_MAX77836,
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool max14577_muic_volatile_reg(struct device *dev, unsigned int reg)
|
||||||
{
|
{
|
||||||
switch (reg) {
|
switch (reg) {
|
||||||
case MAX14577_REG_INT1 ... MAX14577_REG_STATUS3:
|
case MAX14577_REG_INT1 ... MAX14577_REG_STATUS3:
|
||||||
@ -48,49 +80,221 @@ static bool max14577_volatile_reg(struct device *dev, unsigned int reg)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct regmap_config max14577_regmap_config = {
|
static bool max77836_muic_volatile_reg(struct device *dev, unsigned int reg)
|
||||||
|
{
|
||||||
|
/* Any max14577 volatile registers are also max77836 volatile. */
|
||||||
|
if (max14577_muic_volatile_reg(dev, reg))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case MAX77836_FG_REG_VCELL_MSB ... MAX77836_FG_REG_SOC_LSB:
|
||||||
|
case MAX77836_FG_REG_CRATE_MSB ... MAX77836_FG_REG_CRATE_LSB:
|
||||||
|
case MAX77836_FG_REG_STATUS_H ... MAX77836_FG_REG_STATUS_L:
|
||||||
|
case MAX77836_PMIC_REG_INTSRC:
|
||||||
|
case MAX77836_PMIC_REG_TOPSYS_INT:
|
||||||
|
case MAX77836_PMIC_REG_TOPSYS_STAT:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct regmap_config max14577_muic_regmap_config = {
|
||||||
.reg_bits = 8,
|
.reg_bits = 8,
|
||||||
.val_bits = 8,
|
.val_bits = 8,
|
||||||
.volatile_reg = max14577_volatile_reg,
|
.volatile_reg = max14577_muic_volatile_reg,
|
||||||
.max_register = MAX14577_REG_END,
|
.max_register = MAX14577_REG_END,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct regmap_config max77836_pmic_regmap_config = {
|
||||||
|
.reg_bits = 8,
|
||||||
|
.val_bits = 8,
|
||||||
|
.volatile_reg = max77836_muic_volatile_reg,
|
||||||
|
.max_register = MAX77836_PMIC_REG_END,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct regmap_irq max14577_irqs[] = {
|
static const struct regmap_irq max14577_irqs[] = {
|
||||||
/* INT1 interrupts */
|
/* INT1 interrupts */
|
||||||
{ .reg_offset = 0, .mask = INT1_ADC_MASK, },
|
{ .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, },
|
||||||
{ .reg_offset = 0, .mask = INT1_ADCLOW_MASK, },
|
{ .reg_offset = 0, .mask = MAX14577_INT1_ADCLOW_MASK, },
|
||||||
{ .reg_offset = 0, .mask = INT1_ADCERR_MASK, },
|
{ .reg_offset = 0, .mask = MAX14577_INT1_ADCERR_MASK, },
|
||||||
/* INT2 interrupts */
|
/* INT2 interrupts */
|
||||||
{ .reg_offset = 1, .mask = INT2_CHGTYP_MASK, },
|
{ .reg_offset = 1, .mask = MAX14577_INT2_CHGTYP_MASK, },
|
||||||
{ .reg_offset = 1, .mask = INT2_CHGDETRUN_MASK, },
|
{ .reg_offset = 1, .mask = MAX14577_INT2_CHGDETRUN_MASK, },
|
||||||
{ .reg_offset = 1, .mask = INT2_DCDTMR_MASK, },
|
{ .reg_offset = 1, .mask = MAX14577_INT2_DCDTMR_MASK, },
|
||||||
{ .reg_offset = 1, .mask = INT2_DBCHG_MASK, },
|
{ .reg_offset = 1, .mask = MAX14577_INT2_DBCHG_MASK, },
|
||||||
{ .reg_offset = 1, .mask = INT2_VBVOLT_MASK, },
|
{ .reg_offset = 1, .mask = MAX14577_INT2_VBVOLT_MASK, },
|
||||||
/* INT3 interrupts */
|
/* INT3 interrupts */
|
||||||
{ .reg_offset = 2, .mask = INT3_EOC_MASK, },
|
{ .reg_offset = 2, .mask = MAX14577_INT3_EOC_MASK, },
|
||||||
{ .reg_offset = 2, .mask = INT3_CGMBC_MASK, },
|
{ .reg_offset = 2, .mask = MAX14577_INT3_CGMBC_MASK, },
|
||||||
{ .reg_offset = 2, .mask = INT3_OVP_MASK, },
|
{ .reg_offset = 2, .mask = MAX14577_INT3_OVP_MASK, },
|
||||||
{ .reg_offset = 2, .mask = INT3_MBCCHGERR_MASK, },
|
{ .reg_offset = 2, .mask = MAX14577_INT3_MBCCHGERR_MASK, },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct regmap_irq_chip max14577_irq_chip = {
|
static const struct regmap_irq_chip max14577_irq_chip = {
|
||||||
.name = "max14577",
|
.name = "max14577",
|
||||||
.status_base = MAX14577_REG_INT1,
|
.status_base = MAX14577_REG_INT1,
|
||||||
.mask_base = MAX14577_REG_INTMASK1,
|
.mask_base = MAX14577_REG_INTMASK1,
|
||||||
.mask_invert = 1,
|
.mask_invert = true,
|
||||||
.num_regs = 3,
|
.num_regs = 3,
|
||||||
.irqs = max14577_irqs,
|
.irqs = max14577_irqs,
|
||||||
.num_irqs = ARRAY_SIZE(max14577_irqs),
|
.num_irqs = ARRAY_SIZE(max14577_irqs),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct regmap_irq max77836_muic_irqs[] = {
|
||||||
|
/* INT1 interrupts */
|
||||||
|
{ .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, },
|
||||||
|
{ .reg_offset = 0, .mask = MAX14577_INT1_ADCLOW_MASK, },
|
||||||
|
{ .reg_offset = 0, .mask = MAX14577_INT1_ADCERR_MASK, },
|
||||||
|
{ .reg_offset = 0, .mask = MAX77836_INT1_ADC1K_MASK, },
|
||||||
|
/* INT2 interrupts */
|
||||||
|
{ .reg_offset = 1, .mask = MAX14577_INT2_CHGTYP_MASK, },
|
||||||
|
{ .reg_offset = 1, .mask = MAX14577_INT2_CHGDETRUN_MASK, },
|
||||||
|
{ .reg_offset = 1, .mask = MAX14577_INT2_DCDTMR_MASK, },
|
||||||
|
{ .reg_offset = 1, .mask = MAX14577_INT2_DBCHG_MASK, },
|
||||||
|
{ .reg_offset = 1, .mask = MAX14577_INT2_VBVOLT_MASK, },
|
||||||
|
{ .reg_offset = 1, .mask = MAX77836_INT2_VIDRM_MASK, },
|
||||||
|
/* INT3 interrupts */
|
||||||
|
{ .reg_offset = 2, .mask = MAX14577_INT3_EOC_MASK, },
|
||||||
|
{ .reg_offset = 2, .mask = MAX14577_INT3_CGMBC_MASK, },
|
||||||
|
{ .reg_offset = 2, .mask = MAX14577_INT3_OVP_MASK, },
|
||||||
|
{ .reg_offset = 2, .mask = MAX14577_INT3_MBCCHGERR_MASK, },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct regmap_irq_chip max77836_muic_irq_chip = {
|
||||||
|
.name = "max77836-muic",
|
||||||
|
.status_base = MAX14577_REG_INT1,
|
||||||
|
.mask_base = MAX14577_REG_INTMASK1,
|
||||||
|
.mask_invert = true,
|
||||||
|
.num_regs = 3,
|
||||||
|
.irqs = max77836_muic_irqs,
|
||||||
|
.num_irqs = ARRAY_SIZE(max77836_muic_irqs),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct regmap_irq max77836_pmic_irqs[] = {
|
||||||
|
{ .reg_offset = 0, .mask = MAX77836_TOPSYS_INT_T120C_MASK, },
|
||||||
|
{ .reg_offset = 0, .mask = MAX77836_TOPSYS_INT_T140C_MASK, },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct regmap_irq_chip max77836_pmic_irq_chip = {
|
||||||
|
.name = "max77836-pmic",
|
||||||
|
.status_base = MAX77836_PMIC_REG_TOPSYS_INT,
|
||||||
|
.mask_base = MAX77836_PMIC_REG_TOPSYS_INT_MASK,
|
||||||
|
.mask_invert = false,
|
||||||
|
.num_regs = 1,
|
||||||
|
.irqs = max77836_pmic_irqs,
|
||||||
|
.num_irqs = ARRAY_SIZE(max77836_pmic_irqs),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void max14577_print_dev_type(struct max14577 *max14577)
|
||||||
|
{
|
||||||
|
u8 reg_data, vendor_id, device_id;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = max14577_read_reg(max14577->regmap, MAX14577_REG_DEVICEID,
|
||||||
|
®_data);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(max14577->dev,
|
||||||
|
"Failed to read DEVICEID register: %d\n", ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vendor_id = ((reg_data & DEVID_VENDORID_MASK) >>
|
||||||
|
DEVID_VENDORID_SHIFT);
|
||||||
|
device_id = ((reg_data & DEVID_DEVICEID_MASK) >>
|
||||||
|
DEVID_DEVICEID_SHIFT);
|
||||||
|
|
||||||
|
dev_info(max14577->dev, "Device type: %u (ID: 0x%x, vendor: 0x%x)\n",
|
||||||
|
max14577->dev_type, device_id, vendor_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Max77836 specific initialization code for driver probe.
|
||||||
|
* Adds new I2C dummy device, regmap and regmap IRQ chip.
|
||||||
|
* Unmasks Interrupt Source register.
|
||||||
|
*
|
||||||
|
* On success returns 0.
|
||||||
|
* On failure returns errno and reverts any changes done so far (e.g. remove
|
||||||
|
* I2C dummy device), except masking the INT SRC register.
|
||||||
|
*/
|
||||||
|
static int max77836_init(struct max14577 *max14577)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u8 intsrc_mask;
|
||||||
|
|
||||||
|
max14577->i2c_pmic = i2c_new_dummy(max14577->i2c->adapter,
|
||||||
|
I2C_ADDR_PMIC);
|
||||||
|
if (!max14577->i2c_pmic) {
|
||||||
|
dev_err(max14577->dev, "Failed to register PMIC I2C device\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
i2c_set_clientdata(max14577->i2c_pmic, max14577);
|
||||||
|
|
||||||
|
max14577->regmap_pmic = devm_regmap_init_i2c(max14577->i2c_pmic,
|
||||||
|
&max77836_pmic_regmap_config);
|
||||||
|
if (IS_ERR(max14577->regmap_pmic)) {
|
||||||
|
ret = PTR_ERR(max14577->regmap_pmic);
|
||||||
|
dev_err(max14577->dev, "Failed to allocate PMIC register map: %d\n",
|
||||||
|
ret);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Un-mask MAX77836 Interrupt Source register */
|
||||||
|
ret = max14577_read_reg(max14577->regmap_pmic,
|
||||||
|
MAX77836_PMIC_REG_INTSRC_MASK, &intsrc_mask);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(max14577->dev, "Failed to read PMIC register\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
intsrc_mask &= ~(MAX77836_INTSRC_MASK_TOP_INT_MASK);
|
||||||
|
intsrc_mask &= ~(MAX77836_INTSRC_MASK_MUIC_CHG_INT_MASK);
|
||||||
|
ret = max14577_write_reg(max14577->regmap_pmic,
|
||||||
|
MAX77836_PMIC_REG_INTSRC_MASK, intsrc_mask);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(max14577->dev, "Failed to write PMIC register\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = regmap_add_irq_chip(max14577->regmap_pmic, max14577->irq,
|
||||||
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED,
|
||||||
|
0, &max77836_pmic_irq_chip,
|
||||||
|
&max14577->irq_data_pmic);
|
||||||
|
if (ret != 0) {
|
||||||
|
dev_err(max14577->dev, "Failed to request PMIC IRQ %d: %d\n",
|
||||||
|
max14577->irq, ret);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
i2c_unregister_device(max14577->i2c_pmic);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Max77836 specific de-initialization code for driver remove.
|
||||||
|
*/
|
||||||
|
static void max77836_remove(struct max14577 *max14577)
|
||||||
|
{
|
||||||
|
regmap_del_irq_chip(max14577->irq, max14577->irq_data_pmic);
|
||||||
|
i2c_unregister_device(max14577->i2c_pmic);
|
||||||
|
}
|
||||||
|
|
||||||
static int max14577_i2c_probe(struct i2c_client *i2c,
|
static int max14577_i2c_probe(struct i2c_client *i2c,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct max14577 *max14577;
|
struct max14577 *max14577;
|
||||||
struct max14577_platform_data *pdata = dev_get_platdata(&i2c->dev);
|
struct max14577_platform_data *pdata = dev_get_platdata(&i2c->dev);
|
||||||
struct device_node *np = i2c->dev.of_node;
|
struct device_node *np = i2c->dev.of_node;
|
||||||
u8 reg_data;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
const struct regmap_irq_chip *irq_chip;
|
||||||
|
struct mfd_cell *mfd_devs;
|
||||||
|
unsigned int mfd_devs_size;
|
||||||
|
int irq_flags;
|
||||||
|
|
||||||
if (np) {
|
if (np) {
|
||||||
pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
|
pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
|
||||||
@ -113,7 +317,8 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
|
|||||||
max14577->i2c = i2c;
|
max14577->i2c = i2c;
|
||||||
max14577->irq = i2c->irq;
|
max14577->irq = i2c->irq;
|
||||||
|
|
||||||
max14577->regmap = devm_regmap_init_i2c(i2c, &max14577_regmap_config);
|
max14577->regmap = devm_regmap_init_i2c(i2c,
|
||||||
|
&max14577_muic_regmap_config);
|
||||||
if (IS_ERR(max14577->regmap)) {
|
if (IS_ERR(max14577->regmap)) {
|
||||||
ret = PTR_ERR(max14577->regmap);
|
ret = PTR_ERR(max14577->regmap);
|
||||||
dev_err(max14577->dev, "Failed to allocate register map: %d\n",
|
dev_err(max14577->dev, "Failed to allocate register map: %d\n",
|
||||||
@ -121,23 +326,36 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = max14577_read_reg(max14577->regmap, MAX14577_REG_DEVICEID,
|
if (np) {
|
||||||
®_data);
|
const struct of_device_id *of_id;
|
||||||
if (ret) {
|
|
||||||
dev_err(max14577->dev, "Device not found on this channel: %d\n",
|
of_id = of_match_device(max14577_dt_match, &i2c->dev);
|
||||||
ret);
|
if (of_id)
|
||||||
return ret;
|
max14577->dev_type = (unsigned int)of_id->data;
|
||||||
|
} else {
|
||||||
|
max14577->dev_type = id->driver_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
max14577_print_dev_type(max14577);
|
||||||
|
|
||||||
|
switch (max14577->dev_type) {
|
||||||
|
case MAXIM_DEVICE_TYPE_MAX77836:
|
||||||
|
irq_chip = &max77836_muic_irq_chip;
|
||||||
|
mfd_devs = max77836_devs;
|
||||||
|
mfd_devs_size = ARRAY_SIZE(max77836_devs);
|
||||||
|
irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED;
|
||||||
|
break;
|
||||||
|
case MAXIM_DEVICE_TYPE_MAX14577:
|
||||||
|
default:
|
||||||
|
irq_chip = &max14577_irq_chip;
|
||||||
|
mfd_devs = max14577_devs;
|
||||||
|
mfd_devs_size = ARRAY_SIZE(max14577_devs);
|
||||||
|
irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
max14577->vendor_id = ((reg_data & DEVID_VENDORID_MASK) >>
|
|
||||||
DEVID_VENDORID_SHIFT);
|
|
||||||
max14577->device_id = ((reg_data & DEVID_DEVICEID_MASK) >>
|
|
||||||
DEVID_DEVICEID_SHIFT);
|
|
||||||
dev_info(max14577->dev, "Device ID: 0x%x, vendor: 0x%x\n",
|
|
||||||
max14577->device_id, max14577->vendor_id);
|
|
||||||
|
|
||||||
ret = regmap_add_irq_chip(max14577->regmap, max14577->irq,
|
ret = regmap_add_irq_chip(max14577->regmap, max14577->irq,
|
||||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0,
|
irq_flags, 0, irq_chip,
|
||||||
&max14577_irq_chip,
|
|
||||||
&max14577->irq_data);
|
&max14577->irq_data);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n",
|
dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n",
|
||||||
@ -145,8 +363,15 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = mfd_add_devices(max14577->dev, -1, max14577_devs,
|
/* Max77836 specific initialization code (additional regmap) */
|
||||||
ARRAY_SIZE(max14577_devs), NULL, 0,
|
if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836) {
|
||||||
|
ret = max77836_init(max14577);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_max77836;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mfd_add_devices(max14577->dev, -1, mfd_devs,
|
||||||
|
mfd_devs_size, NULL, 0,
|
||||||
regmap_irq_get_domain(max14577->irq_data));
|
regmap_irq_get_domain(max14577->irq_data));
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_mfd;
|
goto err_mfd;
|
||||||
@ -156,6 +381,9 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_mfd:
|
err_mfd:
|
||||||
|
if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836)
|
||||||
|
max77836_remove(max14577);
|
||||||
|
err_max77836:
|
||||||
regmap_del_irq_chip(max14577->irq, max14577->irq_data);
|
regmap_del_irq_chip(max14577->irq, max14577->irq_data);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -167,12 +395,15 @@ static int max14577_i2c_remove(struct i2c_client *i2c)
|
|||||||
|
|
||||||
mfd_remove_devices(max14577->dev);
|
mfd_remove_devices(max14577->dev);
|
||||||
regmap_del_irq_chip(max14577->irq, max14577->irq_data);
|
regmap_del_irq_chip(max14577->irq, max14577->irq_data);
|
||||||
|
if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836)
|
||||||
|
max77836_remove(max14577);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_device_id max14577_i2c_id[] = {
|
static const struct i2c_device_id max14577_i2c_id[] = {
|
||||||
{ "max14577", 0 },
|
{ "max14577", MAXIM_DEVICE_TYPE_MAX14577, },
|
||||||
|
{ "max77836", MAXIM_DEVICE_TYPE_MAX77836, },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, max14577_i2c_id);
|
MODULE_DEVICE_TABLE(i2c, max14577_i2c_id);
|
||||||
@ -215,11 +446,6 @@ static int max14577_resume(struct device *dev)
|
|||||||
}
|
}
|
||||||
#endif /* CONFIG_PM_SLEEP */
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
static struct of_device_id max14577_dt_match[] = {
|
|
||||||
{ .compatible = "maxim,max14577", },
|
|
||||||
{},
|
|
||||||
};
|
|
||||||
|
|
||||||
static SIMPLE_DEV_PM_OPS(max14577_pm, max14577_suspend, max14577_resume);
|
static SIMPLE_DEV_PM_OPS(max14577_pm, max14577_suspend, max14577_resume);
|
||||||
|
|
||||||
static struct i2c_driver max14577_i2c_driver = {
|
static struct i2c_driver max14577_i2c_driver = {
|
||||||
@ -236,6 +462,9 @@ static struct i2c_driver max14577_i2c_driver = {
|
|||||||
|
|
||||||
static int __init max14577_i2c_init(void)
|
static int __init max14577_i2c_init(void)
|
||||||
{
|
{
|
||||||
|
BUILD_BUG_ON(ARRAY_SIZE(max14577_i2c_id) != MAXIM_DEVICE_TYPE_NUM);
|
||||||
|
BUILD_BUG_ON(ARRAY_SIZE(max14577_dt_match) != MAXIM_DEVICE_TYPE_NUM);
|
||||||
|
|
||||||
return i2c_add_driver(&max14577_i2c_driver);
|
return i2c_add_driver(&max14577_i2c_driver);
|
||||||
}
|
}
|
||||||
subsys_initcall(max14577_i2c_init);
|
subsys_initcall(max14577_i2c_init);
|
||||||
@ -247,5 +476,5 @@ static void __exit max14577_i2c_exit(void)
|
|||||||
module_exit(max14577_i2c_exit);
|
module_exit(max14577_i2c_exit);
|
||||||
|
|
||||||
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>");
|
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>");
|
||||||
MODULE_DESCRIPTION("MAXIM 14577 multi-function core driver");
|
MODULE_DESCRIPTION("Maxim 14577/77836 multi-function core driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
@ -54,6 +54,7 @@ config AD525X_DPOT_SPI
|
|||||||
config ATMEL_PWM
|
config ATMEL_PWM
|
||||||
tristate "Atmel AT32/AT91 PWM support"
|
tristate "Atmel AT32/AT91 PWM support"
|
||||||
depends on HAVE_CLK
|
depends on HAVE_CLK
|
||||||
|
depends on AVR32 || AT91SAM9263 || AT91SAM9RL || AT91SAM9G45
|
||||||
help
|
help
|
||||||
This option enables device driver support for the PWM channels
|
This option enables device driver support for the PWM channels
|
||||||
on certain Atmel processors. Pulse Width Modulation is used for
|
on certain Atmel processors. Pulse Width Modulation is used for
|
||||||
@ -200,7 +201,7 @@ config ICS932S401
|
|||||||
|
|
||||||
config ATMEL_SSC
|
config ATMEL_SSC
|
||||||
tristate "Device driver for Atmel SSC peripheral"
|
tristate "Device driver for Atmel SSC peripheral"
|
||||||
depends on HAS_IOMEM
|
depends on HAS_IOMEM && (AVR32 || ARCH_AT91 || COMPILE_TEST)
|
||||||
---help---
|
---help---
|
||||||
This option enables device driver support for Atmel Synchronized
|
This option enables device driver support for Atmel Synchronized
|
||||||
Serial Communication peripheral (SSC).
|
Serial Communication peripheral (SSC).
|
||||||
@ -468,7 +469,7 @@ config BMP085_SPI
|
|||||||
config PCH_PHUB
|
config PCH_PHUB
|
||||||
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB"
|
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB"
|
||||||
select GENERIC_NET_UTILS
|
select GENERIC_NET_UTILS
|
||||||
depends on PCI
|
depends on PCI && (X86_32 || COMPILE_TEST)
|
||||||
help
|
help
|
||||||
This driver is for PCH(Platform controller Hub) PHUB(Packet Hub) of
|
This driver is for PCH(Platform controller Hub) PHUB(Packet Hub) of
|
||||||
Intel Topcliff which is an IOH(Input/Output Hub) for x86 embedded
|
Intel Topcliff which is an IOH(Input/Output Hub) for x86 embedded
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
@ -366,11 +367,17 @@ static const struct dev_pm_ops charlcd_pm_ops = {
|
|||||||
.resume = charlcd_resume,
|
.resume = charlcd_resume,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id charlcd_match[] = {
|
||||||
|
{ .compatible = "arm,versatile-lcd", },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
static struct platform_driver charlcd_driver = {
|
static struct platform_driver charlcd_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVERNAME,
|
.name = DRIVERNAME,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.pm = &charlcd_pm_ops,
|
.pm = &charlcd_pm_ops,
|
||||||
|
.of_match_table = of_match_ptr(charlcd_match),
|
||||||
},
|
},
|
||||||
.remove = __exit_p(charlcd_remove),
|
.remove = __exit_p(charlcd_remove),
|
||||||
};
|
};
|
||||||
|
@ -85,7 +85,6 @@ static ssize_t ds1682_store(struct device *dev, struct device_attribute *attr,
|
|||||||
{
|
{
|
||||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
char *endp;
|
|
||||||
u64 val;
|
u64 val;
|
||||||
__le32 val_le;
|
__le32 val_le;
|
||||||
int rc;
|
int rc;
|
||||||
@ -93,8 +92,8 @@ static ssize_t ds1682_store(struct device *dev, struct device_attribute *attr,
|
|||||||
dev_dbg(dev, "ds1682_store() called on %s\n", attr->attr.name);
|
dev_dbg(dev, "ds1682_store() called on %s\n", attr->attr.name);
|
||||||
|
|
||||||
/* Decode input */
|
/* Decode input */
|
||||||
val = simple_strtoull(buf, &endp, 0);
|
rc = kstrtoull(buf, 0, &val);
|
||||||
if (buf == endp) {
|
if (rc < 0) {
|
||||||
dev_dbg(dev, "input string not a number\n");
|
dev_dbg(dev, "input string not a number\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -348,7 +348,7 @@ int genwqe_init_debugfs(struct genwqe_dev *cd)
|
|||||||
char name[64];
|
char name[64];
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
sprintf(card_name, "%s%u_card", GENWQE_DEVNAME, cd->card_idx);
|
sprintf(card_name, "%s%d_card", GENWQE_DEVNAME, cd->card_idx);
|
||||||
|
|
||||||
root = debugfs_create_dir(card_name, cd->debugfs_genwqe);
|
root = debugfs_create_dir(card_name, cd->debugfs_genwqe);
|
||||||
if (!root) {
|
if (!root) {
|
||||||
@ -454,7 +454,7 @@ int genwqe_init_debugfs(struct genwqe_dev *cd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < GENWQE_MAX_VFS; i++) {
|
for (i = 0; i < GENWQE_MAX_VFS; i++) {
|
||||||
sprintf(name, "vf%d_jobtimeout_msec", i);
|
sprintf(name, "vf%u_jobtimeout_msec", i);
|
||||||
|
|
||||||
file = debugfs_create_u32(name, 0666, root,
|
file = debugfs_create_u32(name, 0666, root,
|
||||||
&cd->vf_jobtimeout_msec[i]);
|
&cd->vf_jobtimeout_msec[i]);
|
||||||
|
@ -454,7 +454,7 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl,
|
|||||||
*/
|
*/
|
||||||
int genwqe_free_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl)
|
int genwqe_free_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc = 0;
|
||||||
struct pci_dev *pci_dev = cd->pci_dev;
|
struct pci_dev *pci_dev = cd->pci_dev;
|
||||||
|
|
||||||
if (sgl->fpage) {
|
if (sgl->fpage) {
|
||||||
|
@ -111,8 +111,6 @@ int mei_amthif_host_init(struct mei_device *dev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
cl->state = MEI_FILE_CONNECTING;
|
|
||||||
|
|
||||||
ret = mei_cl_connect(cl, NULL);
|
ret = mei_cl_connect(cl, NULL);
|
||||||
|
|
||||||
dev->iamthif_state = MEI_IAMTHIF_IDLE;
|
dev->iamthif_state = MEI_IAMTHIF_IDLE;
|
||||||
|
@ -247,7 +247,7 @@ static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
|
|||||||
return id;
|
return id;
|
||||||
|
|
||||||
if (length > dev->me_clients[id].props.max_msg_length)
|
if (length > dev->me_clients[id].props.max_msg_length)
|
||||||
return -EINVAL;
|
return -EFBIG;
|
||||||
|
|
||||||
cb = mei_io_cb_init(cl, NULL);
|
cb = mei_io_cb_init(cl, NULL);
|
||||||
if (!cb)
|
if (!cb)
|
||||||
@ -427,8 +427,6 @@ int mei_cl_enable_device(struct mei_cl_device *device)
|
|||||||
|
|
||||||
mutex_lock(&dev->device_lock);
|
mutex_lock(&dev->device_lock);
|
||||||
|
|
||||||
cl->state = MEI_FILE_CONNECTING;
|
|
||||||
|
|
||||||
err = mei_cl_connect(cl, NULL);
|
err = mei_cl_connect(cl, NULL);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
mutex_unlock(&dev->device_lock);
|
mutex_unlock(&dev->device_lock);
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#include <linux/mei.h>
|
#include <linux/mei.h>
|
||||||
|
|
||||||
@ -415,6 +416,10 @@ void mei_host_client_init(struct work_struct *work)
|
|||||||
dev->reset_count = 0;
|
dev->reset_count = 0;
|
||||||
|
|
||||||
mutex_unlock(&dev->device_lock);
|
mutex_unlock(&dev->device_lock);
|
||||||
|
|
||||||
|
pm_runtime_mark_last_busy(&dev->pdev->dev);
|
||||||
|
dev_dbg(&dev->pdev->dev, "rpm: autosuspend\n");
|
||||||
|
pm_runtime_autosuspend(&dev->pdev->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -425,6 +430,12 @@ void mei_host_client_init(struct work_struct *work)
|
|||||||
*/
|
*/
|
||||||
bool mei_hbuf_acquire(struct mei_device *dev)
|
bool mei_hbuf_acquire(struct mei_device *dev)
|
||||||
{
|
{
|
||||||
|
if (mei_pg_state(dev) == MEI_PG_ON ||
|
||||||
|
dev->pg_event == MEI_PG_EVENT_WAIT) {
|
||||||
|
dev_dbg(&dev->pdev->dev, "device is in pg\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!dev->hbuf_is_ready) {
|
if (!dev->hbuf_is_ready) {
|
||||||
dev_dbg(&dev->pdev->dev, "hbuf is not ready\n");
|
dev_dbg(&dev->pdev->dev, "hbuf is not ready\n");
|
||||||
return false;
|
return false;
|
||||||
@ -460,9 +471,18 @@ int mei_cl_disconnect(struct mei_cl *cl)
|
|||||||
if (cl->state != MEI_FILE_DISCONNECTING)
|
if (cl->state != MEI_FILE_DISCONNECTING)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
rets = pm_runtime_get(&dev->pdev->dev);
|
||||||
|
if (rets < 0 && rets != -EINPROGRESS) {
|
||||||
|
pm_runtime_put_noidle(&dev->pdev->dev);
|
||||||
|
cl_err(dev, cl, "rpm: get failed %d\n", rets);
|
||||||
|
return rets;
|
||||||
|
}
|
||||||
|
|
||||||
cb = mei_io_cb_init(cl, NULL);
|
cb = mei_io_cb_init(cl, NULL);
|
||||||
if (!cb)
|
if (!cb) {
|
||||||
return -ENOMEM;
|
rets = -ENOMEM;
|
||||||
|
goto free;
|
||||||
|
}
|
||||||
|
|
||||||
cb->fop_type = MEI_FOP_CLOSE;
|
cb->fop_type = MEI_FOP_CLOSE;
|
||||||
if (mei_hbuf_acquire(dev)) {
|
if (mei_hbuf_acquire(dev)) {
|
||||||
@ -494,8 +514,7 @@ int mei_cl_disconnect(struct mei_cl *cl)
|
|||||||
cl_err(dev, cl, "wrong status client disconnect.\n");
|
cl_err(dev, cl, "wrong status client disconnect.\n");
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
cl_dbg(dev, cl, "wait failed disconnect err=%08x\n",
|
cl_dbg(dev, cl, "wait failed disconnect err=%d\n", err);
|
||||||
err);
|
|
||||||
|
|
||||||
cl_err(dev, cl, "failed to disconnect from FW client.\n");
|
cl_err(dev, cl, "failed to disconnect from FW client.\n");
|
||||||
}
|
}
|
||||||
@ -503,6 +522,10 @@ int mei_cl_disconnect(struct mei_cl *cl)
|
|||||||
mei_io_list_flush(&dev->ctrl_rd_list, cl);
|
mei_io_list_flush(&dev->ctrl_rd_list, cl);
|
||||||
mei_io_list_flush(&dev->ctrl_wr_list, cl);
|
mei_io_list_flush(&dev->ctrl_wr_list, cl);
|
||||||
free:
|
free:
|
||||||
|
cl_dbg(dev, cl, "rpm: autosuspend\n");
|
||||||
|
pm_runtime_mark_last_busy(&dev->pdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&dev->pdev->dev);
|
||||||
|
|
||||||
mei_io_cb_free(cb);
|
mei_io_cb_free(cb);
|
||||||
return rets;
|
return rets;
|
||||||
}
|
}
|
||||||
@ -557,6 +580,13 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
|
|||||||
|
|
||||||
dev = cl->dev;
|
dev = cl->dev;
|
||||||
|
|
||||||
|
rets = pm_runtime_get(&dev->pdev->dev);
|
||||||
|
if (rets < 0 && rets != -EINPROGRESS) {
|
||||||
|
pm_runtime_put_noidle(&dev->pdev->dev);
|
||||||
|
cl_err(dev, cl, "rpm: get failed %d\n", rets);
|
||||||
|
return rets;
|
||||||
|
}
|
||||||
|
|
||||||
cb = mei_io_cb_init(cl, file);
|
cb = mei_io_cb_init(cl, file);
|
||||||
if (!cb) {
|
if (!cb) {
|
||||||
rets = -ENOMEM;
|
rets = -ENOMEM;
|
||||||
@ -567,6 +597,7 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
|
|||||||
|
|
||||||
/* run hbuf acquire last so we don't have to undo */
|
/* run hbuf acquire last so we don't have to undo */
|
||||||
if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
|
if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
|
||||||
|
cl->state = MEI_FILE_CONNECTING;
|
||||||
if (mei_hbm_cl_connect_req(dev, cl)) {
|
if (mei_hbm_cl_connect_req(dev, cl)) {
|
||||||
rets = -ENODEV;
|
rets = -ENODEV;
|
||||||
goto out;
|
goto out;
|
||||||
@ -596,6 +627,10 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
|
|||||||
rets = cl->status;
|
rets = cl->status;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
cl_dbg(dev, cl, "rpm: autosuspend\n");
|
||||||
|
pm_runtime_mark_last_busy(&dev->pdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&dev->pdev->dev);
|
||||||
|
|
||||||
mei_io_cb_free(cb);
|
mei_io_cb_free(cb);
|
||||||
return rets;
|
return rets;
|
||||||
}
|
}
|
||||||
@ -713,23 +748,31 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
|
|||||||
return -ENOTTY;
|
return -ENOTTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rets = pm_runtime_get(&dev->pdev->dev);
|
||||||
|
if (rets < 0 && rets != -EINPROGRESS) {
|
||||||
|
pm_runtime_put_noidle(&dev->pdev->dev);
|
||||||
|
cl_err(dev, cl, "rpm: get failed %d\n", rets);
|
||||||
|
return rets;
|
||||||
|
}
|
||||||
|
|
||||||
cb = mei_io_cb_init(cl, NULL);
|
cb = mei_io_cb_init(cl, NULL);
|
||||||
if (!cb)
|
if (!cb) {
|
||||||
return -ENOMEM;
|
rets = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* always allocate at least client max message */
|
/* always allocate at least client max message */
|
||||||
length = max_t(size_t, length, dev->me_clients[i].props.max_msg_length);
|
length = max_t(size_t, length, dev->me_clients[i].props.max_msg_length);
|
||||||
rets = mei_io_cb_alloc_resp_buf(cb, length);
|
rets = mei_io_cb_alloc_resp_buf(cb, length);
|
||||||
if (rets)
|
if (rets)
|
||||||
goto err;
|
goto out;
|
||||||
|
|
||||||
cb->fop_type = MEI_FOP_READ;
|
cb->fop_type = MEI_FOP_READ;
|
||||||
if (mei_hbuf_acquire(dev)) {
|
if (mei_hbuf_acquire(dev)) {
|
||||||
if (mei_hbm_cl_flow_control_req(dev, cl)) {
|
rets = mei_hbm_cl_flow_control_req(dev, cl);
|
||||||
cl_err(dev, cl, "flow control send failed\n");
|
if (rets < 0)
|
||||||
rets = -ENODEV;
|
goto out;
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
list_add_tail(&cb->list, &dev->read_list.list);
|
list_add_tail(&cb->list, &dev->read_list.list);
|
||||||
} else {
|
} else {
|
||||||
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
|
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
|
||||||
@ -737,9 +780,14 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
|
|||||||
|
|
||||||
cl->read_cb = cb;
|
cl->read_cb = cb;
|
||||||
|
|
||||||
return rets;
|
out:
|
||||||
err:
|
cl_dbg(dev, cl, "rpm: autosuspend\n");
|
||||||
mei_io_cb_free(cb);
|
pm_runtime_mark_last_busy(&dev->pdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&dev->pdev->dev);
|
||||||
|
|
||||||
|
if (rets)
|
||||||
|
mei_io_cb_free(cb);
|
||||||
|
|
||||||
return rets;
|
return rets;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -776,7 +824,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||||||
return rets;
|
return rets;
|
||||||
|
|
||||||
if (rets == 0) {
|
if (rets == 0) {
|
||||||
cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
|
cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -856,6 +904,12 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
|
|||||||
|
|
||||||
cl_dbg(dev, cl, "mei_cl_write %d\n", buf->size);
|
cl_dbg(dev, cl, "mei_cl_write %d\n", buf->size);
|
||||||
|
|
||||||
|
rets = pm_runtime_get(&dev->pdev->dev);
|
||||||
|
if (rets < 0 && rets != -EINPROGRESS) {
|
||||||
|
pm_runtime_put_noidle(&dev->pdev->dev);
|
||||||
|
cl_err(dev, cl, "rpm: get failed %d\n", rets);
|
||||||
|
return rets;
|
||||||
|
}
|
||||||
|
|
||||||
cb->fop_type = MEI_FOP_WRITE;
|
cb->fop_type = MEI_FOP_WRITE;
|
||||||
cb->buf_idx = 0;
|
cb->buf_idx = 0;
|
||||||
@ -926,6 +980,10 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
|
|||||||
|
|
||||||
rets = buf->size;
|
rets = buf->size;
|
||||||
err:
|
err:
|
||||||
|
cl_dbg(dev, cl, "rpm: autosuspend\n");
|
||||||
|
pm_runtime_mark_last_busy(&dev->pdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&dev->pdev->dev);
|
||||||
|
|
||||||
return rets;
|
return rets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,10 +14,12 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/export.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <linux/mei.h>
|
#include <linux/mei.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#include "mei_dev.h"
|
#include "mei_dev.h"
|
||||||
#include "hbm.h"
|
#include "hbm.h"
|
||||||
@ -57,6 +59,34 @@ static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_hbm_idle - set hbm to idle state
|
||||||
|
*
|
||||||
|
* @dev: the device structure
|
||||||
|
*/
|
||||||
|
void mei_hbm_idle(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
dev->init_clients_timer = 0;
|
||||||
|
dev->hbm_state = MEI_HBM_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_hbm_reset - reset hbm counters and book keeping data structurs
|
||||||
|
*
|
||||||
|
* @dev: the device structure
|
||||||
|
*/
|
||||||
|
void mei_hbm_reset(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
dev->me_clients_num = 0;
|
||||||
|
dev->me_client_presentation_num = 0;
|
||||||
|
dev->me_client_index = 0;
|
||||||
|
|
||||||
|
kfree(dev->me_clients);
|
||||||
|
dev->me_clients = NULL;
|
||||||
|
|
||||||
|
mei_hbm_idle(dev);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mei_hbm_me_cl_allocate - allocates storage for me clients
|
* mei_hbm_me_cl_allocate - allocates storage for me clients
|
||||||
*
|
*
|
||||||
@ -69,9 +99,7 @@ static int mei_hbm_me_cl_allocate(struct mei_device *dev)
|
|||||||
struct mei_me_client *clients;
|
struct mei_me_client *clients;
|
||||||
int b;
|
int b;
|
||||||
|
|
||||||
dev->me_clients_num = 0;
|
mei_hbm_reset(dev);
|
||||||
dev->me_client_presentation_num = 0;
|
|
||||||
dev->me_client_index = 0;
|
|
||||||
|
|
||||||
/* count how many ME clients we have */
|
/* count how many ME clients we have */
|
||||||
for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX)
|
for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX)
|
||||||
@ -80,9 +108,6 @@ static int mei_hbm_me_cl_allocate(struct mei_device *dev)
|
|||||||
if (dev->me_clients_num == 0)
|
if (dev->me_clients_num == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
kfree(dev->me_clients);
|
|
||||||
dev->me_clients = NULL;
|
|
||||||
|
|
||||||
dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%ld.\n",
|
dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%ld.\n",
|
||||||
dev->me_clients_num * sizeof(struct mei_me_client));
|
dev->me_clients_num * sizeof(struct mei_me_client));
|
||||||
/* allocate storage for ME clients representation */
|
/* allocate storage for ME clients representation */
|
||||||
@ -133,17 +158,6 @@ bool mei_hbm_cl_addr_equal(struct mei_cl *cl, void *buf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_hbm_idle - set hbm to idle state
|
|
||||||
*
|
|
||||||
* @dev: the device structure
|
|
||||||
*/
|
|
||||||
void mei_hbm_idle(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
dev->init_clients_timer = 0;
|
|
||||||
dev->hbm_state = MEI_HBM_IDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mei_hbm_start_wait(struct mei_device *dev)
|
int mei_hbm_start_wait(struct mei_device *dev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@ -289,6 +303,34 @@ static int mei_hbm_prop_req(struct mei_device *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mei_hbm_pg - sends pg command
|
||||||
|
*
|
||||||
|
* @dev: the device structure
|
||||||
|
* @pg_cmd: the pg command code
|
||||||
|
*
|
||||||
|
* This function returns -EIO on write failure
|
||||||
|
*/
|
||||||
|
int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd)
|
||||||
|
{
|
||||||
|
struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
|
||||||
|
struct hbm_power_gate *req;
|
||||||
|
const size_t len = sizeof(struct hbm_power_gate);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mei_hbm_hdr(mei_hdr, len);
|
||||||
|
|
||||||
|
req = (struct hbm_power_gate *)dev->wr_msg.data;
|
||||||
|
memset(req, 0, len);
|
||||||
|
req->hbm_cmd = pg_cmd;
|
||||||
|
|
||||||
|
ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
|
||||||
|
if (ret)
|
||||||
|
dev_err(&dev->pdev->dev, "power gate command write failed.\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(mei_hbm_pg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mei_hbm_stop_req - send stop request message
|
* mei_hbm_stop_req - send stop request message
|
||||||
*
|
*
|
||||||
@ -701,6 +743,27 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
|
|||||||
mei_hbm_cl_flow_control_res(dev, flow_control);
|
mei_hbm_cl_flow_control_res(dev, flow_control);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MEI_PG_ISOLATION_ENTRY_RES_CMD:
|
||||||
|
dev_dbg(&dev->pdev->dev, "power gate isolation entry response received\n");
|
||||||
|
dev->pg_event = MEI_PG_EVENT_RECEIVED;
|
||||||
|
if (waitqueue_active(&dev->wait_pg))
|
||||||
|
wake_up(&dev->wait_pg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MEI_PG_ISOLATION_EXIT_REQ_CMD:
|
||||||
|
dev_dbg(&dev->pdev->dev, "power gate isolation exit request received\n");
|
||||||
|
dev->pg_event = MEI_PG_EVENT_RECEIVED;
|
||||||
|
if (waitqueue_active(&dev->wait_pg))
|
||||||
|
wake_up(&dev->wait_pg);
|
||||||
|
else
|
||||||
|
/*
|
||||||
|
* If the driver is not waiting on this then
|
||||||
|
* this is HW initiated exit from PG.
|
||||||
|
* Start runtime pm resume sequence to exit from PG.
|
||||||
|
*/
|
||||||
|
pm_request_resume(&dev->pdev->dev);
|
||||||
|
break;
|
||||||
|
|
||||||
case HOST_CLIENT_PROPERTIES_RES_CMD:
|
case HOST_CLIENT_PROPERTIES_RES_CMD:
|
||||||
dev_dbg(&dev->pdev->dev, "hbm: properties response: message received.\n");
|
dev_dbg(&dev->pdev->dev, "hbm: properties response: message received.\n");
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void mei_hbm_idle(struct mei_device *dev);
|
void mei_hbm_idle(struct mei_device *dev);
|
||||||
|
void mei_hbm_reset(struct mei_device *dev);
|
||||||
int mei_hbm_start_req(struct mei_device *dev);
|
int mei_hbm_start_req(struct mei_device *dev);
|
||||||
int mei_hbm_start_wait(struct mei_device *dev);
|
int mei_hbm_start_wait(struct mei_device *dev);
|
||||||
int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl);
|
int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl);
|
||||||
@ -57,6 +58,7 @@ int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl);
|
|||||||
int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl);
|
int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl);
|
||||||
int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl);
|
int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl);
|
||||||
bool mei_hbm_version_is_supported(struct mei_device *dev);
|
bool mei_hbm_version_is_supported(struct mei_device *dev);
|
||||||
|
int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd);
|
||||||
|
|
||||||
#endif /* _MEI_HBM_H_ */
|
#endif /* _MEI_HBM_H_ */
|
||||||
|
|
||||||
|
@ -133,6 +133,8 @@
|
|||||||
#define ME_CB_RW 8
|
#define ME_CB_RW 8
|
||||||
/* ME_CSR_HA - ME Control Status Host Access register (read only) */
|
/* ME_CSR_HA - ME Control Status Host Access register (read only) */
|
||||||
#define ME_CSR_HA 0xC
|
#define ME_CSR_HA 0xC
|
||||||
|
/* H_HGC_CSR - PGI register */
|
||||||
|
#define H_HPG_CSR 0x10
|
||||||
|
|
||||||
|
|
||||||
/* register bits of H_CSR (Host Control Status register) */
|
/* register bits of H_CSR (Host Control Status register) */
|
||||||
@ -162,6 +164,8 @@ access to ME_CBD */
|
|||||||
#define ME_CBWP_HRA 0x00FF0000
|
#define ME_CBWP_HRA 0x00FF0000
|
||||||
/* ME CB Read Pointer HRA - host read only access to ME_CBRP */
|
/* ME CB Read Pointer HRA - host read only access to ME_CBRP */
|
||||||
#define ME_CBRP_HRA 0x0000FF00
|
#define ME_CBRP_HRA 0x0000FF00
|
||||||
|
/* ME Power Gate Isolation Capability HRA - host ready only access */
|
||||||
|
#define ME_PGIC_HRA 0x00000040
|
||||||
/* ME Reset HRA - host read only access to ME_RST */
|
/* ME Reset HRA - host read only access to ME_RST */
|
||||||
#define ME_RST_HRA 0x00000010
|
#define ME_RST_HRA 0x00000010
|
||||||
/* ME Ready HRA - host read only access to ME_RDY */
|
/* ME Ready HRA - host read only access to ME_RDY */
|
||||||
@ -173,4 +177,9 @@ access to ME_CBD */
|
|||||||
/* ME Interrupt Enable HRA - host read only access to ME_IE */
|
/* ME Interrupt Enable HRA - host read only access to ME_IE */
|
||||||
#define ME_IE_HRA 0x00000001
|
#define ME_IE_HRA 0x00000001
|
||||||
|
|
||||||
|
|
||||||
|
/* register bits - H_HPG_CSR */
|
||||||
|
#define H_HPG_CSR_PGIHEXR 0x00000001
|
||||||
|
#define H_HPG_CSR_PGI 0x00000002
|
||||||
|
|
||||||
#endif /* _MEI_HW_MEI_REGS_H_ */
|
#endif /* _MEI_HW_MEI_REGS_H_ */
|
||||||
|
@ -109,10 +109,27 @@ static inline void mei_hcsr_set(struct mei_me_hw *hw, u32 hcsr)
|
|||||||
*/
|
*/
|
||||||
static void mei_me_hw_config(struct mei_device *dev)
|
static void mei_me_hw_config(struct mei_device *dev)
|
||||||
{
|
{
|
||||||
|
struct mei_me_hw *hw = to_me_hw(dev);
|
||||||
u32 hcsr = mei_hcsr_read(to_me_hw(dev));
|
u32 hcsr = mei_hcsr_read(to_me_hw(dev));
|
||||||
/* Doesn't change in runtime */
|
/* Doesn't change in runtime */
|
||||||
dev->hbuf_depth = (hcsr & H_CBD) >> 24;
|
dev->hbuf_depth = (hcsr & H_CBD) >> 24;
|
||||||
|
|
||||||
|
hw->pg_state = MEI_PG_OFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_me_pg_state - translate internal pg state
|
||||||
|
* to the mei power gating state
|
||||||
|
*
|
||||||
|
* @hw - me hardware
|
||||||
|
* returns: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise
|
||||||
|
*/
|
||||||
|
static inline enum mei_pg_state mei_me_pg_state(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
struct mei_me_hw *hw = to_me_hw(dev);
|
||||||
|
return hw->pg_state;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mei_clear_interrupts - clear and stop interrupts
|
* mei_clear_interrupts - clear and stop interrupts
|
||||||
*
|
*
|
||||||
@ -164,6 +181,9 @@ static void mei_me_hw_reset_release(struct mei_device *dev)
|
|||||||
hcsr |= H_IG;
|
hcsr |= H_IG;
|
||||||
hcsr &= ~H_RST;
|
hcsr &= ~H_RST;
|
||||||
mei_hcsr_set(hw, hcsr);
|
mei_hcsr_set(hw, hcsr);
|
||||||
|
|
||||||
|
/* complete this write before we set host ready on another CPU */
|
||||||
|
mmiowb();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* mei_me_hw_reset - resets fw via mei csr register.
|
* mei_me_hw_reset - resets fw via mei csr register.
|
||||||
@ -183,8 +203,21 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
|
|||||||
else
|
else
|
||||||
hcsr &= ~H_IE;
|
hcsr &= ~H_IE;
|
||||||
|
|
||||||
|
dev->recvd_hw_ready = false;
|
||||||
mei_me_reg_write(hw, H_CSR, hcsr);
|
mei_me_reg_write(hw, H_CSR, hcsr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Host reads the H_CSR once to ensure that the
|
||||||
|
* posted write to H_CSR completes.
|
||||||
|
*/
|
||||||
|
hcsr = mei_hcsr_read(hw);
|
||||||
|
|
||||||
|
if ((hcsr & H_RST) == 0)
|
||||||
|
dev_warn(&dev->pdev->dev, "H_RST is not set = 0x%08X", hcsr);
|
||||||
|
|
||||||
|
if ((hcsr & H_RDY) == H_RDY)
|
||||||
|
dev_warn(&dev->pdev->dev, "H_RDY is not cleared 0x%08X", hcsr);
|
||||||
|
|
||||||
if (intr_enable == false)
|
if (intr_enable == false)
|
||||||
mei_me_hw_reset_release(dev);
|
mei_me_hw_reset_release(dev);
|
||||||
|
|
||||||
@ -201,6 +234,7 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
|
|||||||
static void mei_me_host_set_ready(struct mei_device *dev)
|
static void mei_me_host_set_ready(struct mei_device *dev)
|
||||||
{
|
{
|
||||||
struct mei_me_hw *hw = to_me_hw(dev);
|
struct mei_me_hw *hw = to_me_hw(dev);
|
||||||
|
hw->host_hw_state = mei_hcsr_read(hw);
|
||||||
hw->host_hw_state |= H_IE | H_IG | H_RDY;
|
hw->host_hw_state |= H_IE | H_IG | H_RDY;
|
||||||
mei_hcsr_set(hw, hw->host_hw_state);
|
mei_hcsr_set(hw, hw->host_hw_state);
|
||||||
}
|
}
|
||||||
@ -233,10 +267,7 @@ static bool mei_me_hw_is_ready(struct mei_device *dev)
|
|||||||
static int mei_me_hw_ready_wait(struct mei_device *dev)
|
static int mei_me_hw_ready_wait(struct mei_device *dev)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
if (mei_me_hw_is_ready(dev))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
dev->recvd_hw_ready = false;
|
|
||||||
mutex_unlock(&dev->device_lock);
|
mutex_unlock(&dev->device_lock);
|
||||||
err = wait_event_interruptible_timeout(dev->wait_hw_ready,
|
err = wait_event_interruptible_timeout(dev->wait_hw_ready,
|
||||||
dev->recvd_hw_ready,
|
dev->recvd_hw_ready,
|
||||||
@ -430,6 +461,144 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_me_pg_enter - write pg enter register to mei device.
|
||||||
|
*
|
||||||
|
* @dev: the device structure
|
||||||
|
*/
|
||||||
|
static void mei_me_pg_enter(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
struct mei_me_hw *hw = to_me_hw(dev);
|
||||||
|
u32 reg = mei_me_reg_read(hw, H_HPG_CSR);
|
||||||
|
reg |= H_HPG_CSR_PGI;
|
||||||
|
mei_me_reg_write(hw, H_HPG_CSR, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_me_pg_enter - write pg enter register to mei device.
|
||||||
|
*
|
||||||
|
* @dev: the device structure
|
||||||
|
*/
|
||||||
|
static void mei_me_pg_exit(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
struct mei_me_hw *hw = to_me_hw(dev);
|
||||||
|
u32 reg = mei_me_reg_read(hw, H_HPG_CSR);
|
||||||
|
|
||||||
|
WARN(!(reg & H_HPG_CSR_PGI), "PGI is not set\n");
|
||||||
|
|
||||||
|
reg |= H_HPG_CSR_PGIHEXR;
|
||||||
|
mei_me_reg_write(hw, H_HPG_CSR, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_me_pg_set_sync - perform pg entry procedure
|
||||||
|
*
|
||||||
|
* @dev: the device structure
|
||||||
|
*
|
||||||
|
* returns 0 on success an error code otherwise
|
||||||
|
*/
|
||||||
|
int mei_me_pg_set_sync(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
struct mei_me_hw *hw = to_me_hw(dev);
|
||||||
|
unsigned long timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dev->pg_event = MEI_PG_EVENT_WAIT;
|
||||||
|
|
||||||
|
ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_ENTRY_REQ_CMD);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
mutex_unlock(&dev->device_lock);
|
||||||
|
wait_event_timeout(dev->wait_pg,
|
||||||
|
dev->pg_event == MEI_PG_EVENT_RECEIVED, timeout);
|
||||||
|
mutex_lock(&dev->device_lock);
|
||||||
|
|
||||||
|
if (dev->pg_event == MEI_PG_EVENT_RECEIVED) {
|
||||||
|
mei_me_pg_enter(dev);
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
ret = -ETIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->pg_event = MEI_PG_EVENT_IDLE;
|
||||||
|
hw->pg_state = MEI_PG_ON;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_me_pg_unset_sync - perform pg exit procedure
|
||||||
|
*
|
||||||
|
* @dev: the device structure
|
||||||
|
*
|
||||||
|
* returns 0 on success an error code otherwise
|
||||||
|
*/
|
||||||
|
int mei_me_pg_unset_sync(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
struct mei_me_hw *hw = to_me_hw(dev);
|
||||||
|
unsigned long timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (dev->pg_event == MEI_PG_EVENT_RECEIVED)
|
||||||
|
goto reply;
|
||||||
|
|
||||||
|
dev->pg_event = MEI_PG_EVENT_WAIT;
|
||||||
|
|
||||||
|
mei_me_pg_exit(dev);
|
||||||
|
|
||||||
|
mutex_unlock(&dev->device_lock);
|
||||||
|
wait_event_timeout(dev->wait_pg,
|
||||||
|
dev->pg_event == MEI_PG_EVENT_RECEIVED, timeout);
|
||||||
|
mutex_lock(&dev->device_lock);
|
||||||
|
|
||||||
|
reply:
|
||||||
|
if (dev->pg_event == MEI_PG_EVENT_RECEIVED)
|
||||||
|
ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_EXIT_RES_CMD);
|
||||||
|
else
|
||||||
|
ret = -ETIME;
|
||||||
|
|
||||||
|
dev->pg_event = MEI_PG_EVENT_IDLE;
|
||||||
|
hw->pg_state = MEI_PG_OFF;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_me_pg_is_enabled - detect if PG is supported by HW
|
||||||
|
*
|
||||||
|
* @dev: the device structure
|
||||||
|
*
|
||||||
|
* returns: true is pg supported, false otherwise
|
||||||
|
*/
|
||||||
|
static bool mei_me_pg_is_enabled(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
struct mei_me_hw *hw = to_me_hw(dev);
|
||||||
|
u32 reg = mei_me_reg_read(hw, ME_CSR_HA);
|
||||||
|
|
||||||
|
if ((reg & ME_PGIC_HRA) == 0)
|
||||||
|
goto notsupported;
|
||||||
|
|
||||||
|
if (dev->version.major_version < HBM_MAJOR_VERSION_PGI)
|
||||||
|
goto notsupported;
|
||||||
|
|
||||||
|
if (dev->version.major_version == HBM_MAJOR_VERSION_PGI &&
|
||||||
|
dev->version.minor_version < HBM_MINOR_VERSION_PGI)
|
||||||
|
goto notsupported;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
notsupported:
|
||||||
|
dev_dbg(&dev->pdev->dev, "pg: not supported: HGP = %d hbm version %d.%d ?= %d.%d\n",
|
||||||
|
!!(reg & ME_PGIC_HRA),
|
||||||
|
dev->version.major_version,
|
||||||
|
dev->version.minor_version,
|
||||||
|
HBM_MAJOR_VERSION_PGI,
|
||||||
|
HBM_MINOR_VERSION_PGI);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mei_me_irq_quick_handler - The ISR of the MEI device
|
* mei_me_irq_quick_handler - The ISR of the MEI device
|
||||||
*
|
*
|
||||||
@ -491,14 +660,13 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
|
|||||||
/* check if we need to start the dev */
|
/* check if we need to start the dev */
|
||||||
if (!mei_host_is_ready(dev)) {
|
if (!mei_host_is_ready(dev)) {
|
||||||
if (mei_hw_is_ready(dev)) {
|
if (mei_hw_is_ready(dev)) {
|
||||||
|
mei_me_hw_reset_release(dev);
|
||||||
dev_dbg(&dev->pdev->dev, "we need to start the dev.\n");
|
dev_dbg(&dev->pdev->dev, "we need to start the dev.\n");
|
||||||
|
|
||||||
dev->recvd_hw_ready = true;
|
dev->recvd_hw_ready = true;
|
||||||
wake_up_interruptible(&dev->wait_hw_ready);
|
wake_up_interruptible(&dev->wait_hw_ready);
|
||||||
} else {
|
} else {
|
||||||
|
dev_dbg(&dev->pdev->dev, "Spurious Interrupt\n");
|
||||||
dev_dbg(&dev->pdev->dev, "Reset Completed.\n");
|
|
||||||
mei_me_hw_reset_release(dev);
|
|
||||||
}
|
}
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
@ -524,9 +692,15 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
|
|||||||
|
|
||||||
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
|
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
|
||||||
|
|
||||||
rets = mei_irq_write_handler(dev, &complete_list);
|
/*
|
||||||
|
* During PG handshake only allowed write is the replay to the
|
||||||
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
|
* PG exit message, so block calling write function
|
||||||
|
* if the pg state is not idle
|
||||||
|
*/
|
||||||
|
if (dev->pg_event == MEI_PG_EVENT_IDLE) {
|
||||||
|
rets = mei_irq_write_handler(dev, &complete_list);
|
||||||
|
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
|
||||||
|
}
|
||||||
|
|
||||||
mei_irq_compl_handler(dev, &complete_list);
|
mei_irq_compl_handler(dev, &complete_list);
|
||||||
|
|
||||||
@ -535,8 +709,65 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
|
|||||||
mutex_unlock(&dev->device_lock);
|
mutex_unlock(&dev->device_lock);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_me_fw_status - retrieve fw status from the pci config space
|
||||||
|
*
|
||||||
|
* @dev: the device structure
|
||||||
|
* @fw_status: fw status registers storage
|
||||||
|
*
|
||||||
|
* returns 0 on success an error code otherwise
|
||||||
|
*/
|
||||||
|
static int mei_me_fw_status(struct mei_device *dev,
|
||||||
|
struct mei_fw_status *fw_status)
|
||||||
|
{
|
||||||
|
const u32 pci_cfg_reg[] = {PCI_CFG_HFS_1, PCI_CFG_HFS_2};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!fw_status)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
switch (dev->pdev->device) {
|
||||||
|
case MEI_DEV_ID_IBXPK_1:
|
||||||
|
case MEI_DEV_ID_IBXPK_2:
|
||||||
|
case MEI_DEV_ID_CPT_1:
|
||||||
|
case MEI_DEV_ID_PBG_1:
|
||||||
|
case MEI_DEV_ID_PPT_1:
|
||||||
|
case MEI_DEV_ID_PPT_2:
|
||||||
|
case MEI_DEV_ID_PPT_3:
|
||||||
|
case MEI_DEV_ID_LPT_H:
|
||||||
|
case MEI_DEV_ID_LPT_W:
|
||||||
|
case MEI_DEV_ID_LPT_LP:
|
||||||
|
case MEI_DEV_ID_LPT_HR:
|
||||||
|
case MEI_DEV_ID_WPT_LP:
|
||||||
|
fw_status->count = 2;
|
||||||
|
break;
|
||||||
|
case MEI_DEV_ID_ICH10_1:
|
||||||
|
case MEI_DEV_ID_ICH10_2:
|
||||||
|
case MEI_DEV_ID_ICH10_3:
|
||||||
|
case MEI_DEV_ID_ICH10_4:
|
||||||
|
fw_status->count = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fw_status->count = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < fw_status->count && i < MEI_FW_STATUS_MAX; i++) {
|
||||||
|
int ret;
|
||||||
|
ret = pci_read_config_dword(dev->pdev,
|
||||||
|
pci_cfg_reg[i], &fw_status->status[i]);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct mei_hw_ops mei_me_hw_ops = {
|
static const struct mei_hw_ops mei_me_hw_ops = {
|
||||||
|
|
||||||
|
.pg_state = mei_me_pg_state,
|
||||||
|
|
||||||
|
.fw_status = mei_me_fw_status,
|
||||||
.host_is_ready = mei_me_host_is_ready,
|
.host_is_ready = mei_me_host_is_ready,
|
||||||
|
|
||||||
.hw_is_ready = mei_me_hw_is_ready,
|
.hw_is_ready = mei_me_hw_is_ready,
|
||||||
@ -544,6 +775,8 @@ static const struct mei_hw_ops mei_me_hw_ops = {
|
|||||||
.hw_config = mei_me_hw_config,
|
.hw_config = mei_me_hw_config,
|
||||||
.hw_start = mei_me_hw_start,
|
.hw_start = mei_me_hw_start,
|
||||||
|
|
||||||
|
.pg_is_enabled = mei_me_pg_is_enabled,
|
||||||
|
|
||||||
.intr_clear = mei_me_intr_clear,
|
.intr_clear = mei_me_intr_clear,
|
||||||
.intr_enable = mei_me_intr_enable,
|
.intr_enable = mei_me_intr_enable,
|
||||||
.intr_disable = mei_me_intr_disable,
|
.intr_disable = mei_me_intr_disable,
|
||||||
@ -559,14 +792,81 @@ static const struct mei_hw_ops mei_me_hw_ops = {
|
|||||||
.read = mei_me_read_slots
|
.read = mei_me_read_slots
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool mei_me_fw_type_nm(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
u32 reg;
|
||||||
|
pci_read_config_dword(pdev, PCI_CFG_HFS_2, ®);
|
||||||
|
/* make sure that bit 9 (NM) is up and bit 10 (DM) is down */
|
||||||
|
return (reg & 0x600) == 0x200;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MEI_CFG_FW_NM \
|
||||||
|
.quirk_probe = mei_me_fw_type_nm
|
||||||
|
|
||||||
|
static bool mei_me_fw_type_sps(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
u32 reg;
|
||||||
|
/* Read ME FW Status check for SPS Firmware */
|
||||||
|
pci_read_config_dword(pdev, PCI_CFG_HFS_1, ®);
|
||||||
|
/* if bits [19:16] = 15, running SPS Firmware */
|
||||||
|
return (reg & 0xf0000) == 0xf0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MEI_CFG_FW_SPS \
|
||||||
|
.quirk_probe = mei_me_fw_type_sps
|
||||||
|
|
||||||
|
|
||||||
|
#define MEI_CFG_LEGACY_HFS \
|
||||||
|
.fw_status.count = 0
|
||||||
|
|
||||||
|
#define MEI_CFG_ICH_HFS \
|
||||||
|
.fw_status.count = 1, \
|
||||||
|
.fw_status.status[0] = PCI_CFG_HFS_1
|
||||||
|
|
||||||
|
#define MEI_CFG_PCH_HFS \
|
||||||
|
.fw_status.count = 2, \
|
||||||
|
.fw_status.status[0] = PCI_CFG_HFS_1, \
|
||||||
|
.fw_status.status[1] = PCI_CFG_HFS_2
|
||||||
|
|
||||||
|
|
||||||
|
/* ICH Legacy devices */
|
||||||
|
const struct mei_cfg mei_me_legacy_cfg = {
|
||||||
|
MEI_CFG_LEGACY_HFS,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ICH devices */
|
||||||
|
const struct mei_cfg mei_me_ich_cfg = {
|
||||||
|
MEI_CFG_ICH_HFS,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* PCH devices */
|
||||||
|
const struct mei_cfg mei_me_pch_cfg = {
|
||||||
|
MEI_CFG_PCH_HFS,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* PCH Cougar Point and Patsburg with quirk for Node Manager exclusion */
|
||||||
|
const struct mei_cfg mei_me_pch_cpt_pbg_cfg = {
|
||||||
|
MEI_CFG_PCH_HFS,
|
||||||
|
MEI_CFG_FW_NM,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* PCH Lynx Point with quirk for SPS Firmware exclusion */
|
||||||
|
const struct mei_cfg mei_me_lpt_cfg = {
|
||||||
|
MEI_CFG_PCH_HFS,
|
||||||
|
MEI_CFG_FW_SPS,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mei_me_dev_init - allocates and initializes the mei device structure
|
* mei_me_dev_init - allocates and initializes the mei device structure
|
||||||
*
|
*
|
||||||
* @pdev: The pci device structure
|
* @pdev: The pci device structure
|
||||||
|
* @cfg: per device generation config
|
||||||
*
|
*
|
||||||
* returns The mei_device_device pointer on success, NULL on failure.
|
* returns The mei_device_device pointer on success, NULL on failure.
|
||||||
*/
|
*/
|
||||||
struct mei_device *mei_me_dev_init(struct pci_dev *pdev)
|
struct mei_device *mei_me_dev_init(struct pci_dev *pdev,
|
||||||
|
const struct mei_cfg *cfg)
|
||||||
{
|
{
|
||||||
struct mei_device *dev;
|
struct mei_device *dev;
|
||||||
|
|
||||||
@ -575,7 +875,7 @@ struct mei_device *mei_me_dev_init(struct pci_dev *pdev)
|
|||||||
if (!dev)
|
if (!dev)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
mei_device_init(dev);
|
mei_device_init(dev, cfg);
|
||||||
|
|
||||||
dev->ops = &mei_me_hw_ops;
|
dev->ops = &mei_me_hw_ops;
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
#include "mei_dev.h"
|
#include "mei_dev.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
|
|
||||||
|
#define MEI_ME_RPM_TIMEOUT 500 /* ms */
|
||||||
|
|
||||||
struct mei_me_hw {
|
struct mei_me_hw {
|
||||||
void __iomem *mem_addr;
|
void __iomem *mem_addr;
|
||||||
/*
|
/*
|
||||||
@ -31,11 +33,22 @@ struct mei_me_hw {
|
|||||||
*/
|
*/
|
||||||
u32 host_hw_state;
|
u32 host_hw_state;
|
||||||
u32 me_hw_state;
|
u32 me_hw_state;
|
||||||
|
enum mei_pg_state pg_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_me_hw(dev) (struct mei_me_hw *)((dev)->hw)
|
#define to_me_hw(dev) (struct mei_me_hw *)((dev)->hw)
|
||||||
|
|
||||||
struct mei_device *mei_me_dev_init(struct pci_dev *pdev);
|
extern const struct mei_cfg mei_me_legacy_cfg;
|
||||||
|
extern const struct mei_cfg mei_me_ich_cfg;
|
||||||
|
extern const struct mei_cfg mei_me_pch_cfg;
|
||||||
|
extern const struct mei_cfg mei_me_pch_cpt_pbg_cfg;
|
||||||
|
extern const struct mei_cfg mei_me_lpt_cfg;
|
||||||
|
|
||||||
|
struct mei_device *mei_me_dev_init(struct pci_dev *pdev,
|
||||||
|
const struct mei_cfg *cfg);
|
||||||
|
|
||||||
|
int mei_me_pg_set_sync(struct mei_device *dev);
|
||||||
|
int mei_me_pg_unset_sync(struct mei_device *dev);
|
||||||
|
|
||||||
irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id);
|
irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id);
|
||||||
irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id);
|
irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id);
|
||||||
|
@ -89,7 +89,7 @@ enum {
|
|||||||
# define PCI_CFG_TXE_FW_STS0_ERR_CODE_MSK 0x0000F000
|
# define PCI_CFG_TXE_FW_STS0_ERR_CODE_MSK 0x0000F000
|
||||||
# define PCI_CFG_TXE_FW_STS0_OP_MODE_MSK 0x000F0000
|
# define PCI_CFG_TXE_FW_STS0_OP_MODE_MSK 0x000F0000
|
||||||
# define PCI_CFG_TXE_FW_STS0_RST_CNT_MSK 0x00F00000
|
# define PCI_CFG_TXE_FW_STS0_RST_CNT_MSK 0x00F00000
|
||||||
|
#define PCI_CFG_TXE_FW_STS1 0x48
|
||||||
|
|
||||||
#define IPC_BASE_ADDR 0x80400 /* SeC IPC Base Address */
|
#define IPC_BASE_ADDR 0x80400 /* SeC IPC Base Address */
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req)
|
|||||||
dev_dbg(&dev->pdev->dev, "Aliveness current=%d request=%d\n",
|
dev_dbg(&dev->pdev->dev, "Aliveness current=%d request=%d\n",
|
||||||
hw->aliveness, req);
|
hw->aliveness, req);
|
||||||
if (do_req) {
|
if (do_req) {
|
||||||
hw->recvd_aliveness = false;
|
dev->pg_event = MEI_PG_EVENT_WAIT;
|
||||||
mei_txe_br_reg_write(hw, SICR_HOST_ALIVENESS_REQ_REG, req);
|
mei_txe_br_reg_write(hw, SICR_HOST_ALIVENESS_REQ_REG, req);
|
||||||
}
|
}
|
||||||
return do_req;
|
return do_req;
|
||||||
@ -213,6 +213,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected)
|
|||||||
do {
|
do {
|
||||||
hw->aliveness = mei_txe_aliveness_get(dev);
|
hw->aliveness = mei_txe_aliveness_get(dev);
|
||||||
if (hw->aliveness == expected) {
|
if (hw->aliveness == expected) {
|
||||||
|
dev->pg_event = MEI_PG_EVENT_IDLE;
|
||||||
dev_dbg(&dev->pdev->dev,
|
dev_dbg(&dev->pdev->dev,
|
||||||
"aliveness settled after %d msecs\n", t);
|
"aliveness settled after %d msecs\n", t);
|
||||||
return t;
|
return t;
|
||||||
@ -223,6 +224,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected)
|
|||||||
t += MSEC_PER_SEC / 5;
|
t += MSEC_PER_SEC / 5;
|
||||||
} while (t < SEC_ALIVENESS_WAIT_TIMEOUT);
|
} while (t < SEC_ALIVENESS_WAIT_TIMEOUT);
|
||||||
|
|
||||||
|
dev->pg_event = MEI_PG_EVENT_IDLE;
|
||||||
dev_err(&dev->pdev->dev, "aliveness timed out\n");
|
dev_err(&dev->pdev->dev, "aliveness timed out\n");
|
||||||
return -ETIME;
|
return -ETIME;
|
||||||
}
|
}
|
||||||
@ -249,19 +251,22 @@ static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
mutex_unlock(&dev->device_lock);
|
mutex_unlock(&dev->device_lock);
|
||||||
err = wait_event_timeout(hw->wait_aliveness,
|
err = wait_event_timeout(hw->wait_aliveness_resp,
|
||||||
hw->recvd_aliveness, timeout);
|
dev->pg_event == MEI_PG_EVENT_RECEIVED, timeout);
|
||||||
mutex_lock(&dev->device_lock);
|
mutex_lock(&dev->device_lock);
|
||||||
|
|
||||||
hw->aliveness = mei_txe_aliveness_get(dev);
|
hw->aliveness = mei_txe_aliveness_get(dev);
|
||||||
ret = hw->aliveness == expected ? 0 : -ETIME;
|
ret = hw->aliveness == expected ? 0 : -ETIME;
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_err(&dev->pdev->dev, "aliveness timed out");
|
dev_warn(&dev->pdev->dev, "aliveness timed out = %ld aliveness = %d event = %d\n",
|
||||||
|
err, hw->aliveness, dev->pg_event);
|
||||||
else
|
else
|
||||||
dev_dbg(&dev->pdev->dev, "aliveness settled after %d msecs\n",
|
dev_dbg(&dev->pdev->dev, "aliveness settled after = %d msec aliveness = %d event = %d\n",
|
||||||
jiffies_to_msecs(timeout - err));
|
jiffies_to_msecs(timeout - err),
|
||||||
hw->recvd_aliveness = false;
|
hw->aliveness, dev->pg_event);
|
||||||
|
|
||||||
|
dev->pg_event = MEI_PG_EVENT_IDLE;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,6 +284,32 @@ int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_txe_pg_is_enabled - detect if PG is supported by HW
|
||||||
|
*
|
||||||
|
* @dev: the device structure
|
||||||
|
*
|
||||||
|
* returns: true is pg supported, false otherwise
|
||||||
|
*/
|
||||||
|
static bool mei_txe_pg_is_enabled(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_txe_pg_state - translate aliveness register value
|
||||||
|
* to the mei power gating state
|
||||||
|
*
|
||||||
|
* @dev: the device structure
|
||||||
|
*
|
||||||
|
* returns: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise
|
||||||
|
*/
|
||||||
|
static inline enum mei_pg_state mei_txe_pg_state(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
struct mei_txe_hw *hw = to_txe_hw(dev);
|
||||||
|
return hw->aliveness ? MEI_PG_OFF : MEI_PG_ON;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mei_txe_input_ready_interrupt_enable - sets the Input Ready Interrupt
|
* mei_txe_input_ready_interrupt_enable - sets the Input Ready Interrupt
|
||||||
*
|
*
|
||||||
@ -589,7 +620,10 @@ static int mei_txe_write(struct mei_device *dev,
|
|||||||
mei_txe_input_ready_interrupt_enable(dev);
|
mei_txe_input_ready_interrupt_enable(dev);
|
||||||
|
|
||||||
if (!mei_txe_is_input_ready(dev)) {
|
if (!mei_txe_is_input_ready(dev)) {
|
||||||
dev_err(&dev->pdev->dev, "Input is not ready");
|
struct mei_fw_status fw_status;
|
||||||
|
mei_fw_status(dev, &fw_status);
|
||||||
|
dev_err(&dev->pdev->dev, "Input is not ready " FW_STS_FMT "\n",
|
||||||
|
FW_STS_PRM(fw_status));
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -960,9 +994,9 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id)
|
|||||||
/* Clear the interrupt cause */
|
/* Clear the interrupt cause */
|
||||||
dev_dbg(&dev->pdev->dev,
|
dev_dbg(&dev->pdev->dev,
|
||||||
"Aliveness Interrupt: Status: %d\n", hw->aliveness);
|
"Aliveness Interrupt: Status: %d\n", hw->aliveness);
|
||||||
hw->recvd_aliveness = true;
|
dev->pg_event = MEI_PG_EVENT_RECEIVED;
|
||||||
if (waitqueue_active(&hw->wait_aliveness))
|
if (waitqueue_active(&hw->wait_aliveness_resp))
|
||||||
wake_up(&hw->wait_aliveness);
|
wake_up(&hw->wait_aliveness_resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1008,15 +1042,51 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id)
|
|||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_txe_fw_status - retrieve fw status from the pci config space
|
||||||
|
*
|
||||||
|
* @dev: the device structure
|
||||||
|
* @fw_status: fw status registers storage
|
||||||
|
*
|
||||||
|
* returns: 0 on success an error code otherwise
|
||||||
|
*/
|
||||||
|
static int mei_txe_fw_status(struct mei_device *dev,
|
||||||
|
struct mei_fw_status *fw_status)
|
||||||
|
{
|
||||||
|
const u32 pci_cfg_reg[] = {PCI_CFG_TXE_FW_STS0, PCI_CFG_TXE_FW_STS1};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!fw_status)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
fw_status->count = 2;
|
||||||
|
|
||||||
|
for (i = 0; i < fw_status->count && i < MEI_FW_STATUS_MAX; i++) {
|
||||||
|
int ret;
|
||||||
|
ret = pci_read_config_dword(dev->pdev,
|
||||||
|
pci_cfg_reg[i], &fw_status->status[i]);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct mei_hw_ops mei_txe_hw_ops = {
|
static const struct mei_hw_ops mei_txe_hw_ops = {
|
||||||
|
|
||||||
|
.fw_status = mei_txe_fw_status,
|
||||||
.host_is_ready = mei_txe_host_is_ready,
|
.host_is_ready = mei_txe_host_is_ready,
|
||||||
|
|
||||||
|
.pg_state = mei_txe_pg_state,
|
||||||
|
|
||||||
.hw_is_ready = mei_txe_hw_is_ready,
|
.hw_is_ready = mei_txe_hw_is_ready,
|
||||||
.hw_reset = mei_txe_hw_reset,
|
.hw_reset = mei_txe_hw_reset,
|
||||||
.hw_config = mei_txe_hw_config,
|
.hw_config = mei_txe_hw_config,
|
||||||
.hw_start = mei_txe_hw_start,
|
.hw_start = mei_txe_hw_start,
|
||||||
|
|
||||||
|
.pg_is_enabled = mei_txe_pg_is_enabled,
|
||||||
|
|
||||||
.intr_clear = mei_txe_intr_clear,
|
.intr_clear = mei_txe_intr_clear,
|
||||||
.intr_enable = mei_txe_intr_enable,
|
.intr_enable = mei_txe_intr_enable,
|
||||||
.intr_disable = mei_txe_intr_disable,
|
.intr_disable = mei_txe_intr_disable,
|
||||||
@ -1034,14 +1104,27 @@ static const struct mei_hw_ops mei_txe_hw_ops = {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define MEI_CFG_TXE_FW_STS \
|
||||||
|
.fw_status.count = 2, \
|
||||||
|
.fw_status.status[0] = PCI_CFG_TXE_FW_STS0, \
|
||||||
|
.fw_status.status[1] = PCI_CFG_TXE_FW_STS1
|
||||||
|
|
||||||
|
const struct mei_cfg mei_txe_cfg = {
|
||||||
|
MEI_CFG_TXE_FW_STS,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mei_txe_dev_init - allocates and initializes txe hardware specific structure
|
* mei_txe_dev_init - allocates and initializes txe hardware specific structure
|
||||||
*
|
*
|
||||||
* @pdev - pci device
|
* @pdev - pci device
|
||||||
|
* @cfg - per device generation config
|
||||||
|
*
|
||||||
* returns struct mei_device * on success or NULL;
|
* returns struct mei_device * on success or NULL;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
struct mei_device *mei_txe_dev_init(struct pci_dev *pdev)
|
struct mei_device *mei_txe_dev_init(struct pci_dev *pdev,
|
||||||
|
const struct mei_cfg *cfg)
|
||||||
{
|
{
|
||||||
struct mei_device *dev;
|
struct mei_device *dev;
|
||||||
struct mei_txe_hw *hw;
|
struct mei_txe_hw *hw;
|
||||||
@ -1051,11 +1134,11 @@ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev)
|
|||||||
if (!dev)
|
if (!dev)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
mei_device_init(dev);
|
mei_device_init(dev, cfg);
|
||||||
|
|
||||||
hw = to_txe_hw(dev);
|
hw = to_txe_hw(dev);
|
||||||
|
|
||||||
init_waitqueue_head(&hw->wait_aliveness);
|
init_waitqueue_head(&hw->wait_aliveness_resp);
|
||||||
|
|
||||||
dev->ops = &mei_txe_hw_ops;
|
dev->ops = &mei_txe_hw_ops;
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
#include "hw.h"
|
#include "hw.h"
|
||||||
#include "hw-txe-regs.h"
|
#include "hw-txe-regs.h"
|
||||||
|
|
||||||
|
#define MEI_TXI_RPM_TIMEOUT 500 /* ms */
|
||||||
|
|
||||||
/* Flatten Hierarchy interrupt cause */
|
/* Flatten Hierarchy interrupt cause */
|
||||||
#define TXE_INTR_READINESS_BIT 0 /* HISR_INT_0_STS */
|
#define TXE_INTR_READINESS_BIT 0 /* HISR_INT_0_STS */
|
||||||
#define TXE_INTR_READINESS HISR_INT_0_STS
|
#define TXE_INTR_READINESS HISR_INT_0_STS
|
||||||
@ -35,12 +37,11 @@
|
|||||||
/**
|
/**
|
||||||
* struct mei_txe_hw - txe hardware specifics
|
* struct mei_txe_hw - txe hardware specifics
|
||||||
*
|
*
|
||||||
* @mem_addr: SeC and BRIDGE bars
|
* @mem_addr: SeC and BRIDGE bars
|
||||||
* @aliveness: aliveness (power gating) state of the hardware
|
* @aliveness: aliveness (power gating) state of the hardware
|
||||||
* @readiness: readiness state of the hardware
|
* @readiness: readiness state of the hardware
|
||||||
* @wait_aliveness: aliveness wait queue
|
* @wait_aliveness_resp: aliveness wait queue
|
||||||
* @recvd_aliveness: aliveness interrupt was recived
|
* @intr_cause: translated interrupt cause
|
||||||
* @intr_cause: translated interrupt cause
|
|
||||||
*/
|
*/
|
||||||
struct mei_txe_hw {
|
struct mei_txe_hw {
|
||||||
void __iomem *mem_addr[NUM_OF_MEM_BARS];
|
void __iomem *mem_addr[NUM_OF_MEM_BARS];
|
||||||
@ -48,8 +49,7 @@ struct mei_txe_hw {
|
|||||||
u32 readiness;
|
u32 readiness;
|
||||||
u32 slots;
|
u32 slots;
|
||||||
|
|
||||||
wait_queue_head_t wait_aliveness;
|
wait_queue_head_t wait_aliveness_resp;
|
||||||
bool recvd_aliveness;
|
|
||||||
|
|
||||||
unsigned long intr_cause;
|
unsigned long intr_cause;
|
||||||
};
|
};
|
||||||
@ -61,7 +61,10 @@ static inline struct mei_device *hw_txe_to_mei(struct mei_txe_hw *hw)
|
|||||||
return container_of((void *)hw, struct mei_device, hw);
|
return container_of((void *)hw, struct mei_device, hw);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct mei_device *mei_txe_dev_init(struct pci_dev *pdev);
|
extern const struct mei_cfg mei_txe_cfg;
|
||||||
|
|
||||||
|
struct mei_device *mei_txe_dev_init(struct pci_dev *pdev,
|
||||||
|
const struct mei_cfg *cfg);
|
||||||
|
|
||||||
irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id);
|
irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id);
|
||||||
irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id);
|
irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id);
|
||||||
|
@ -31,14 +31,21 @@
|
|||||||
#define MEI_IAMTHIF_STALL_TIMER 12 /* HPS */
|
#define MEI_IAMTHIF_STALL_TIMER 12 /* HPS */
|
||||||
#define MEI_IAMTHIF_READ_TIMER 10 /* HPS */
|
#define MEI_IAMTHIF_READ_TIMER 10 /* HPS */
|
||||||
|
|
||||||
|
#define MEI_PGI_TIMEOUT 1 /* PG Isolation time response 1 sec */
|
||||||
#define MEI_HBM_TIMEOUT 1 /* 1 second */
|
#define MEI_HBM_TIMEOUT 1 /* 1 second */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MEI Version
|
* MEI Version
|
||||||
*/
|
*/
|
||||||
#define HBM_MINOR_VERSION 0
|
#define HBM_MINOR_VERSION 1
|
||||||
#define HBM_MAJOR_VERSION 1
|
#define HBM_MAJOR_VERSION 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MEI version with PGI support
|
||||||
|
*/
|
||||||
|
#define HBM_MINOR_VERSION_PGI 1
|
||||||
|
#define HBM_MAJOR_VERSION_PGI 1
|
||||||
|
|
||||||
/* Host bus message command opcode */
|
/* Host bus message command opcode */
|
||||||
#define MEI_HBM_CMD_OP_MSK 0x7f
|
#define MEI_HBM_CMD_OP_MSK 0x7f
|
||||||
/* Host bus message command RESPONSE */
|
/* Host bus message command RESPONSE */
|
||||||
@ -69,6 +76,11 @@
|
|||||||
|
|
||||||
#define MEI_FLOW_CONTROL_CMD 0x08
|
#define MEI_FLOW_CONTROL_CMD 0x08
|
||||||
|
|
||||||
|
#define MEI_PG_ISOLATION_ENTRY_REQ_CMD 0x0a
|
||||||
|
#define MEI_PG_ISOLATION_ENTRY_RES_CMD 0x8a
|
||||||
|
#define MEI_PG_ISOLATION_EXIT_REQ_CMD 0x0b
|
||||||
|
#define MEI_PG_ISOLATION_EXIT_RES_CMD 0x8b
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MEI Stop Reason
|
* MEI Stop Reason
|
||||||
* used by hbm_host_stop_request.reason
|
* used by hbm_host_stop_request.reason
|
||||||
@ -207,6 +219,17 @@ struct hbm_props_response {
|
|||||||
struct mei_client_properties client_properties;
|
struct mei_client_properties client_properties;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct hbm_power_gate - power gate request/response
|
||||||
|
*
|
||||||
|
* @hbm_cmd - bus message command header
|
||||||
|
* @reserved[3]
|
||||||
|
*/
|
||||||
|
struct hbm_power_gate {
|
||||||
|
u8 hbm_cmd;
|
||||||
|
u8 reserved[3];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct hbm_client_connect_request - connect/disconnect request
|
* struct hbm_client_connect_request - connect/disconnect request
|
||||||
*
|
*
|
||||||
|
@ -74,9 +74,13 @@ int mei_reset(struct mei_device *dev)
|
|||||||
if (state != MEI_DEV_INITIALIZING &&
|
if (state != MEI_DEV_INITIALIZING &&
|
||||||
state != MEI_DEV_DISABLED &&
|
state != MEI_DEV_DISABLED &&
|
||||||
state != MEI_DEV_POWER_DOWN &&
|
state != MEI_DEV_POWER_DOWN &&
|
||||||
state != MEI_DEV_POWER_UP)
|
state != MEI_DEV_POWER_UP) {
|
||||||
dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
|
struct mei_fw_status fw_status;
|
||||||
mei_dev_state_str(state));
|
mei_fw_status(dev, &fw_status);
|
||||||
|
dev_warn(&dev->pdev->dev,
|
||||||
|
"unexpected reset: dev_state = %s " FW_STS_FMT "\n",
|
||||||
|
mei_dev_state_str(state), FW_STS_PRM(fw_status));
|
||||||
|
}
|
||||||
|
|
||||||
/* we're already in reset, cancel the init timer
|
/* we're already in reset, cancel the init timer
|
||||||
* if the reset was called due the hbm protocol error
|
* if the reset was called due the hbm protocol error
|
||||||
@ -118,8 +122,8 @@ int mei_reset(struct mei_device *dev)
|
|||||||
mei_amthif_reset_params(dev);
|
mei_amthif_reset_params(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mei_hbm_reset(dev);
|
||||||
|
|
||||||
dev->me_clients_num = 0;
|
|
||||||
dev->rd_msg_hdr = 0;
|
dev->rd_msg_hdr = 0;
|
||||||
dev->wd_pending = false;
|
dev->wd_pending = false;
|
||||||
|
|
||||||
@ -303,15 +307,58 @@ void mei_stop(struct mei_device *dev)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(mei_stop);
|
EXPORT_SYMBOL_GPL(mei_stop);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_write_is_idle - check if the write queues are idle
|
||||||
|
*
|
||||||
|
* @dev: the device structure
|
||||||
|
*
|
||||||
|
* returns true of there is no pending write
|
||||||
|
*/
|
||||||
|
bool mei_write_is_idle(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
bool idle = (dev->dev_state == MEI_DEV_ENABLED &&
|
||||||
|
list_empty(&dev->ctrl_wr_list.list) &&
|
||||||
|
list_empty(&dev->write_list.list));
|
||||||
|
|
||||||
|
dev_dbg(&dev->pdev->dev, "write pg: is idle[%d] state=%s ctrl=%d write=%d\n",
|
||||||
|
idle,
|
||||||
|
mei_dev_state_str(dev->dev_state),
|
||||||
|
list_empty(&dev->ctrl_wr_list.list),
|
||||||
|
list_empty(&dev->write_list.list));
|
||||||
|
|
||||||
void mei_device_init(struct mei_device *dev)
|
return idle;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(mei_write_is_idle);
|
||||||
|
|
||||||
|
int mei_fw_status(struct mei_device *dev, struct mei_fw_status *fw_status)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
const struct mei_fw_status *fw_src = &dev->cfg->fw_status;
|
||||||
|
|
||||||
|
if (!fw_status)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
fw_status->count = fw_src->count;
|
||||||
|
for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) {
|
||||||
|
int ret;
|
||||||
|
ret = pci_read_config_dword(dev->pdev,
|
||||||
|
fw_src->status[i], &fw_status->status[i]);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(mei_fw_status);
|
||||||
|
|
||||||
|
void mei_device_init(struct mei_device *dev, const struct mei_cfg *cfg)
|
||||||
{
|
{
|
||||||
/* setup our list array */
|
/* setup our list array */
|
||||||
INIT_LIST_HEAD(&dev->file_list);
|
INIT_LIST_HEAD(&dev->file_list);
|
||||||
INIT_LIST_HEAD(&dev->device_list);
|
INIT_LIST_HEAD(&dev->device_list);
|
||||||
mutex_init(&dev->device_lock);
|
mutex_init(&dev->device_lock);
|
||||||
init_waitqueue_head(&dev->wait_hw_ready);
|
init_waitqueue_head(&dev->wait_hw_ready);
|
||||||
|
init_waitqueue_head(&dev->wait_pg);
|
||||||
init_waitqueue_head(&dev->wait_recvd_msg);
|
init_waitqueue_head(&dev->wait_recvd_msg);
|
||||||
init_waitqueue_head(&dev->wait_stop_wd);
|
init_waitqueue_head(&dev->wait_stop_wd);
|
||||||
dev->dev_state = MEI_DEV_INITIALIZING;
|
dev->dev_state = MEI_DEV_INITIALIZING;
|
||||||
@ -340,6 +387,9 @@ void mei_device_init(struct mei_device *dev)
|
|||||||
* 0: Reserved for MEI Bus Message communications
|
* 0: Reserved for MEI Bus Message communications
|
||||||
*/
|
*/
|
||||||
bitmap_set(dev->host_clients_map, 0, 1);
|
bitmap_set(dev->host_clients_map, 0, 1);
|
||||||
|
|
||||||
|
dev->pg_event = MEI_PG_EVENT_IDLE;
|
||||||
|
dev->cfg = cfg;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(mei_device_init);
|
EXPORT_SYMBOL_GPL(mei_device_init);
|
||||||
|
|
||||||
|
@ -467,7 +467,6 @@ static int mei_ioctl_connect_client(struct file *file,
|
|||||||
}
|
}
|
||||||
|
|
||||||
cl->me_client_id = dev->me_clients[i].client_id;
|
cl->me_client_id = dev->me_clients[i].client_id;
|
||||||
cl->state = MEI_FILE_CONNECTING;
|
|
||||||
|
|
||||||
dev_dbg(&dev->pdev->dev, "Connect to FW Client ID = %d\n",
|
dev_dbg(&dev->pdev->dev, "Connect to FW Client ID = %d\n",
|
||||||
cl->me_client_id);
|
cl->me_client_id);
|
||||||
|
@ -153,6 +153,20 @@ struct mei_msg_data {
|
|||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Maximum number of processed FW status registers */
|
||||||
|
#define MEI_FW_STATUS_MAX 2
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct mei_fw_status - storage of FW status data
|
||||||
|
*
|
||||||
|
* @count - number of actually available elements in array
|
||||||
|
* @status - FW status registers
|
||||||
|
*/
|
||||||
|
struct mei_fw_status {
|
||||||
|
int count;
|
||||||
|
u32 status[MEI_FW_STATUS_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct mei_me_client - representation of me (fw) client
|
* struct mei_me_client - representation of me (fw) client
|
||||||
*
|
*
|
||||||
@ -213,6 +227,7 @@ struct mei_cl {
|
|||||||
|
|
||||||
/** struct mei_hw_ops
|
/** struct mei_hw_ops
|
||||||
*
|
*
|
||||||
|
* @fw_status - read FW status from PCI config space
|
||||||
* @host_is_ready - query for host readiness
|
* @host_is_ready - query for host readiness
|
||||||
|
|
||||||
* @hw_is_ready - query if hw is ready
|
* @hw_is_ready - query if hw is ready
|
||||||
@ -220,6 +235,9 @@ struct mei_cl {
|
|||||||
* @hw_start - start hw after reset
|
* @hw_start - start hw after reset
|
||||||
* @hw_config - configure hw
|
* @hw_config - configure hw
|
||||||
|
|
||||||
|
* @pg_state - power gating state of the device
|
||||||
|
* @pg_is_enabled - is power gating enabled
|
||||||
|
|
||||||
* @intr_clear - clear pending interrupts
|
* @intr_clear - clear pending interrupts
|
||||||
* @intr_enable - enable interrupts
|
* @intr_enable - enable interrupts
|
||||||
* @intr_disable - disable interrupts
|
* @intr_disable - disable interrupts
|
||||||
@ -237,6 +255,8 @@ struct mei_cl {
|
|||||||
*/
|
*/
|
||||||
struct mei_hw_ops {
|
struct mei_hw_ops {
|
||||||
|
|
||||||
|
int (*fw_status)(struct mei_device *dev,
|
||||||
|
struct mei_fw_status *fw_status);
|
||||||
bool (*host_is_ready)(struct mei_device *dev);
|
bool (*host_is_ready)(struct mei_device *dev);
|
||||||
|
|
||||||
bool (*hw_is_ready)(struct mei_device *dev);
|
bool (*hw_is_ready)(struct mei_device *dev);
|
||||||
@ -244,6 +264,9 @@ struct mei_hw_ops {
|
|||||||
int (*hw_start)(struct mei_device *dev);
|
int (*hw_start)(struct mei_device *dev);
|
||||||
void (*hw_config)(struct mei_device *dev);
|
void (*hw_config)(struct mei_device *dev);
|
||||||
|
|
||||||
|
enum mei_pg_state (*pg_state)(struct mei_device *dev);
|
||||||
|
bool (*pg_is_enabled)(struct mei_device *dev);
|
||||||
|
|
||||||
void (*intr_clear)(struct mei_device *dev);
|
void (*intr_clear)(struct mei_device *dev);
|
||||||
void (*intr_enable)(struct mei_device *dev);
|
void (*intr_enable)(struct mei_device *dev);
|
||||||
void (*intr_disable)(struct mei_device *dev);
|
void (*intr_disable)(struct mei_device *dev);
|
||||||
@ -331,16 +354,61 @@ struct mei_cl_device {
|
|||||||
void *priv_data;
|
void *priv_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum mei_pg_event - power gating transition events
|
||||||
|
*
|
||||||
|
* @MEI_PG_EVENT_IDLE: the driver is not in power gating transition
|
||||||
|
* @MEI_PG_EVENT_WAIT: the driver is waiting for a pg event to complete
|
||||||
|
* @MEI_PG_EVENT_RECEIVED: the driver received pg event
|
||||||
|
*/
|
||||||
|
enum mei_pg_event {
|
||||||
|
MEI_PG_EVENT_IDLE,
|
||||||
|
MEI_PG_EVENT_WAIT,
|
||||||
|
MEI_PG_EVENT_RECEIVED,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum mei_pg_state - device internal power gating state
|
||||||
|
*
|
||||||
|
* @MEI_PG_OFF: device is not power gated - it is active
|
||||||
|
* @MEI_PG_ON: device is power gated - it is in lower power state
|
||||||
|
*/
|
||||||
|
enum mei_pg_state {
|
||||||
|
MEI_PG_OFF = 0,
|
||||||
|
MEI_PG_ON = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mei_cfg
|
||||||
|
*
|
||||||
|
* @fw_status - FW status
|
||||||
|
* @quirk_probe - device exclusion quirk
|
||||||
|
*/
|
||||||
|
struct mei_cfg {
|
||||||
|
const struct mei_fw_status fw_status;
|
||||||
|
bool (*quirk_probe)(struct pci_dev *pdev);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#define MEI_PCI_DEVICE(dev, cfg) \
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \
|
||||||
|
.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \
|
||||||
|
.driver_data = (kernel_ulong_t)&(cfg)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct mei_device - MEI private device struct
|
* struct mei_device - MEI private device struct
|
||||||
|
|
||||||
* @reset_count - limits the number of consecutive resets
|
* @reset_count - limits the number of consecutive resets
|
||||||
* @hbm_state - state of host bus message protocol
|
* @hbm_state - state of host bus message protocol
|
||||||
|
* @pg_event - power gating event
|
||||||
* @mem_addr - mem mapped base register address
|
* @mem_addr - mem mapped base register address
|
||||||
|
|
||||||
* @hbuf_depth - depth of hardware host/write buffer is slots
|
* @hbuf_depth - depth of hardware host/write buffer is slots
|
||||||
* @hbuf_is_ready - query if the host host/write buffer is ready
|
* @hbuf_is_ready - query if the host host/write buffer is ready
|
||||||
* @wr_msg - the buffer for hbm control messages
|
* @wr_msg - the buffer for hbm control messages
|
||||||
|
* @cfg - per device generation config and ops
|
||||||
*/
|
*/
|
||||||
struct mei_device {
|
struct mei_device {
|
||||||
struct pci_dev *pdev; /* pointer to pci device struct */
|
struct pci_dev *pdev; /* pointer to pci device struct */
|
||||||
@ -371,6 +439,7 @@ struct mei_device {
|
|||||||
* waiting queue for receive message from FW
|
* waiting queue for receive message from FW
|
||||||
*/
|
*/
|
||||||
wait_queue_head_t wait_hw_ready;
|
wait_queue_head_t wait_hw_ready;
|
||||||
|
wait_queue_head_t wait_pg;
|
||||||
wait_queue_head_t wait_recvd_msg;
|
wait_queue_head_t wait_recvd_msg;
|
||||||
wait_queue_head_t wait_stop_wd;
|
wait_queue_head_t wait_stop_wd;
|
||||||
|
|
||||||
@ -382,6 +451,14 @@ struct mei_device {
|
|||||||
enum mei_hbm_state hbm_state;
|
enum mei_hbm_state hbm_state;
|
||||||
u16 init_clients_timer;
|
u16 init_clients_timer;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Power Gating support
|
||||||
|
*/
|
||||||
|
enum mei_pg_event pg_event;
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
struct dev_pm_domain pg_domain;
|
||||||
|
#endif /* CONFIG_PM_RUNTIME */
|
||||||
|
|
||||||
unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE]; /* control messages */
|
unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE]; /* control messages */
|
||||||
u32 rd_msg_hdr;
|
u32 rd_msg_hdr;
|
||||||
|
|
||||||
@ -442,6 +519,7 @@ struct mei_device {
|
|||||||
|
|
||||||
|
|
||||||
const struct mei_hw_ops *ops;
|
const struct mei_hw_ops *ops;
|
||||||
|
const struct mei_cfg *cfg;
|
||||||
char hw[0] __aligned(sizeof(void *));
|
char hw[0] __aligned(sizeof(void *));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -474,7 +552,7 @@ static inline u32 mei_slots2data(int slots)
|
|||||||
/*
|
/*
|
||||||
* mei init function prototypes
|
* mei init function prototypes
|
||||||
*/
|
*/
|
||||||
void mei_device_init(struct mei_device *dev);
|
void mei_device_init(struct mei_device *dev, const struct mei_cfg *cfg);
|
||||||
int mei_reset(struct mei_device *dev);
|
int mei_reset(struct mei_device *dev);
|
||||||
int mei_start(struct mei_device *dev);
|
int mei_start(struct mei_device *dev);
|
||||||
int mei_restart(struct mei_device *dev);
|
int mei_restart(struct mei_device *dev);
|
||||||
@ -553,10 +631,22 @@ void mei_watchdog_unregister(struct mei_device *dev);
|
|||||||
* Register Access Function
|
* Register Access Function
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
static inline void mei_hw_config(struct mei_device *dev)
|
static inline void mei_hw_config(struct mei_device *dev)
|
||||||
{
|
{
|
||||||
dev->ops->hw_config(dev);
|
dev->ops->hw_config(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline enum mei_pg_state mei_pg_state(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
return dev->ops->pg_state(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool mei_pg_is_enabled(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
return dev->ops->pg_is_enabled(dev);
|
||||||
|
}
|
||||||
|
|
||||||
static inline int mei_hw_reset(struct mei_device *dev, bool enable)
|
static inline int mei_hw_reset(struct mei_device *dev, bool enable)
|
||||||
{
|
{
|
||||||
return dev->ops->hw_reset(dev, enable);
|
return dev->ops->hw_reset(dev, enable);
|
||||||
@ -629,8 +719,17 @@ static inline int mei_count_full_read_slots(struct mei_device *dev)
|
|||||||
return dev->ops->rdbuf_full_slots(dev);
|
return dev->ops->rdbuf_full_slots(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mei_fw_status(struct mei_device *dev, struct mei_fw_status *fw_status);
|
||||||
|
|
||||||
|
#define FW_STS_FMT "%08X %08X"
|
||||||
|
#define FW_STS_PRM(fw_status) \
|
||||||
|
(fw_status).count > 0 ? (fw_status).status[0] : 0xDEADBEEF, \
|
||||||
|
(fw_status).count > 1 ? (fw_status).status[1] : 0xDEADBEEF
|
||||||
|
|
||||||
bool mei_hbuf_acquire(struct mei_device *dev);
|
bool mei_hbuf_acquire(struct mei_device *dev);
|
||||||
|
|
||||||
|
bool mei_write_is_idle(struct mei_device *dev);
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||||
int mei_dbgfs_register(struct mei_device *dev, const char *name);
|
int mei_dbgfs_register(struct mei_device *dev, const char *name);
|
||||||
void mei_dbgfs_deregister(struct mei_device *dev);
|
void mei_dbgfs_deregister(struct mei_device *dev);
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/miscdevice.h>
|
#include <linux/miscdevice.h>
|
||||||
|
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#include <linux/mei.h>
|
#include <linux/mei.h>
|
||||||
|
|
||||||
#include "mei_dev.h"
|
#include "mei_dev.h"
|
||||||
@ -42,42 +44,44 @@
|
|||||||
|
|
||||||
/* mei_pci_tbl - PCI Device ID Table */
|
/* mei_pci_tbl - PCI Device ID Table */
|
||||||
static const struct pci_device_id mei_me_pci_tbl[] = {
|
static const struct pci_device_id mei_me_pci_tbl[] = {
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_82946GZ, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G35)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_82G35, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82Q965)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_82Q965, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G965)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_82G965, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GM965)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_82GM965, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GME965)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_82GME965, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q35)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q35, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82G33)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82G33, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q33)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q33, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82X38)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82X38, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_3200)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_3200, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_6)},
|
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_7)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_6, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_8)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_7, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_9)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_8, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_10)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_9, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_1)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_10, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_2)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_1, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_3)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_2, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_4)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_3, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_1)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_4, mei_me_legacy_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_2)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_1, mei_me_ich_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_3)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_2, mei_me_ich_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_4)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_3, mei_me_ich_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_1)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_4, mei_me_ich_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_2)},
|
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_CPT_1)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_1, mei_me_pch_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PBG_1)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_2, mei_me_pch_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_1)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_CPT_1, mei_me_pch_cpt_pbg_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_PBG_1, mei_me_pch_cpt_pbg_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_PPT_1, mei_me_pch_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_H)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_PPT_2, mei_me_pch_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_W)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_PPT_3, mei_me_pch_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_H, mei_me_lpt_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_HR)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_W, mei_me_lpt_cfg)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_WPT_LP)},
|
{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_LP, mei_me_pch_cfg)},
|
||||||
|
{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_HR, mei_me_lpt_cfg)},
|
||||||
|
{MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP, mei_me_pch_cfg)},
|
||||||
|
|
||||||
/* required last entry */
|
/* required last entry */
|
||||||
{0, }
|
{0, }
|
||||||
@ -85,44 +89,33 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
|
|||||||
|
|
||||||
MODULE_DEVICE_TABLE(pci, mei_me_pci_tbl);
|
MODULE_DEVICE_TABLE(pci, mei_me_pci_tbl);
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
static inline void mei_me_set_pm_domain(struct mei_device *dev);
|
||||||
|
static inline void mei_me_unset_pm_domain(struct mei_device *dev);
|
||||||
|
#else
|
||||||
|
static inline void mei_me_set_pm_domain(struct mei_device *dev) {}
|
||||||
|
static inline void mei_me_unset_pm_domain(struct mei_device *dev) {}
|
||||||
|
#endif /* CONFIG_PM_RUNTIME */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mei_quirk_probe - probe for devices that doesn't valid ME interface
|
* mei_quirk_probe - probe for devices that doesn't valid ME interface
|
||||||
*
|
*
|
||||||
* @pdev: PCI device structure
|
* @pdev: PCI device structure
|
||||||
* @ent: entry into pci_device_table
|
* @cfg: per generation config
|
||||||
*
|
*
|
||||||
* returns true if ME Interface is valid, false otherwise
|
* returns true if ME Interface is valid, false otherwise
|
||||||
*/
|
*/
|
||||||
static bool mei_me_quirk_probe(struct pci_dev *pdev,
|
static bool mei_me_quirk_probe(struct pci_dev *pdev,
|
||||||
const struct pci_device_id *ent)
|
const struct mei_cfg *cfg)
|
||||||
{
|
{
|
||||||
u32 reg;
|
if (cfg->quirk_probe && cfg->quirk_probe(pdev)) {
|
||||||
/* Cougar Point || Patsburg */
|
dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n");
|
||||||
if (ent->device == MEI_DEV_ID_CPT_1 ||
|
return false;
|
||||||
ent->device == MEI_DEV_ID_PBG_1) {
|
|
||||||
pci_read_config_dword(pdev, PCI_CFG_HFS_2, ®);
|
|
||||||
/* make sure that bit 9 (NM) is up and bit 10 (DM) is down */
|
|
||||||
if ((reg & 0x600) == 0x200)
|
|
||||||
goto no_mei;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Lynx Point */
|
|
||||||
if (ent->device == MEI_DEV_ID_LPT_H ||
|
|
||||||
ent->device == MEI_DEV_ID_LPT_W ||
|
|
||||||
ent->device == MEI_DEV_ID_LPT_HR) {
|
|
||||||
/* Read ME FW Status check for SPS Firmware */
|
|
||||||
pci_read_config_dword(pdev, PCI_CFG_HFS_1, ®);
|
|
||||||
/* if bits [19:16] = 15, running SPS Firmware */
|
|
||||||
if ((reg & 0xf0000) == 0xf0000)
|
|
||||||
goto no_mei;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
no_mei:
|
|
||||||
dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mei_probe - Device Initialization Routine
|
* mei_probe - Device Initialization Routine
|
||||||
*
|
*
|
||||||
@ -133,15 +126,14 @@ static bool mei_me_quirk_probe(struct pci_dev *pdev,
|
|||||||
*/
|
*/
|
||||||
static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||||
{
|
{
|
||||||
|
const struct mei_cfg *cfg = (struct mei_cfg *)(ent->driver_data);
|
||||||
struct mei_device *dev;
|
struct mei_device *dev;
|
||||||
struct mei_me_hw *hw;
|
struct mei_me_hw *hw;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
|
||||||
if (!mei_me_quirk_probe(pdev, ent)) {
|
if (!mei_me_quirk_probe(pdev, cfg))
|
||||||
err = -ENODEV;
|
return -ENODEV;
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* enable pci dev */
|
/* enable pci dev */
|
||||||
err = pci_enable_device(pdev);
|
err = pci_enable_device(pdev);
|
||||||
@ -173,7 +165,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||||||
|
|
||||||
|
|
||||||
/* allocates and initializes the mei dev structure */
|
/* allocates and initializes the mei dev structure */
|
||||||
dev = mei_me_dev_init(pdev);
|
dev = mei_me_dev_init(pdev, cfg);
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto release_regions;
|
goto release_regions;
|
||||||
@ -212,6 +204,9 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||||||
goto release_irq;
|
goto release_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_ME_RPM_TIMEOUT);
|
||||||
|
pm_runtime_use_autosuspend(&pdev->dev);
|
||||||
|
|
||||||
err = mei_register(dev);
|
err = mei_register(dev);
|
||||||
if (err)
|
if (err)
|
||||||
goto release_irq;
|
goto release_irq;
|
||||||
@ -220,6 +215,17 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||||||
|
|
||||||
schedule_delayed_work(&dev->timer_work, HZ);
|
schedule_delayed_work(&dev->timer_work, HZ);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For not wake-able HW runtime pm framework
|
||||||
|
* can't be used on pci device level.
|
||||||
|
* Use domain runtime pm callbacks instead.
|
||||||
|
*/
|
||||||
|
if (!pci_dev_run_wake(pdev))
|
||||||
|
mei_me_set_pm_domain(dev);
|
||||||
|
|
||||||
|
if (mei_pg_is_enabled(dev))
|
||||||
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "initialization successful.\n");
|
dev_dbg(&pdev->dev, "initialization successful.\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -259,12 +265,18 @@ static void mei_me_remove(struct pci_dev *pdev)
|
|||||||
if (!dev)
|
if (!dev)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (mei_pg_is_enabled(dev))
|
||||||
|
pm_runtime_get_noresume(&pdev->dev);
|
||||||
|
|
||||||
hw = to_me_hw(dev);
|
hw = to_me_hw(dev);
|
||||||
|
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "stop\n");
|
dev_dbg(&pdev->dev, "stop\n");
|
||||||
mei_stop(dev);
|
mei_stop(dev);
|
||||||
|
|
||||||
|
if (!pci_dev_run_wake(pdev))
|
||||||
|
mei_me_unset_pm_domain(dev);
|
||||||
|
|
||||||
/* disable interrupts */
|
/* disable interrupts */
|
||||||
mei_disable_interrupts(dev);
|
mei_disable_interrupts(dev);
|
||||||
|
|
||||||
@ -343,12 +355,120 @@ static int mei_me_pci_resume(struct device *device)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
static int mei_me_pm_runtime_idle(struct device *device)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev = to_pci_dev(device);
|
||||||
|
struct mei_device *dev;
|
||||||
|
|
||||||
|
dev_dbg(&pdev->dev, "rpm: me: runtime_idle\n");
|
||||||
|
|
||||||
|
dev = pci_get_drvdata(pdev);
|
||||||
|
if (!dev)
|
||||||
|
return -ENODEV;
|
||||||
|
if (mei_write_is_idle(dev))
|
||||||
|
pm_schedule_suspend(device, MEI_ME_RPM_TIMEOUT * 2);
|
||||||
|
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mei_me_pm_runtime_suspend(struct device *device)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev = to_pci_dev(device);
|
||||||
|
struct mei_device *dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dev_dbg(&pdev->dev, "rpm: me: runtime suspend\n");
|
||||||
|
|
||||||
|
dev = pci_get_drvdata(pdev);
|
||||||
|
if (!dev)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
mutex_lock(&dev->device_lock);
|
||||||
|
|
||||||
|
if (mei_write_is_idle(dev))
|
||||||
|
ret = mei_me_pg_set_sync(dev);
|
||||||
|
else
|
||||||
|
ret = -EAGAIN;
|
||||||
|
|
||||||
|
mutex_unlock(&dev->device_lock);
|
||||||
|
|
||||||
|
dev_dbg(&pdev->dev, "rpm: me: runtime suspend ret=%d\n", ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mei_me_pm_runtime_resume(struct device *device)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev = to_pci_dev(device);
|
||||||
|
struct mei_device *dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dev_dbg(&pdev->dev, "rpm: me: runtime resume\n");
|
||||||
|
|
||||||
|
dev = pci_get_drvdata(pdev);
|
||||||
|
if (!dev)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
mutex_lock(&dev->device_lock);
|
||||||
|
|
||||||
|
ret = mei_me_pg_unset_sync(dev);
|
||||||
|
|
||||||
|
mutex_unlock(&dev->device_lock);
|
||||||
|
|
||||||
|
dev_dbg(&pdev->dev, "rpm: me: runtime resume ret = %d\n", ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_me_set_pm_domain - fill and set pm domian stucture for device
|
||||||
|
*
|
||||||
|
* @dev: mei_device
|
||||||
|
*/
|
||||||
|
static inline void mei_me_set_pm_domain(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev = dev->pdev;
|
||||||
|
|
||||||
|
if (pdev->dev.bus && pdev->dev.bus->pm) {
|
||||||
|
dev->pg_domain.ops = *pdev->dev.bus->pm;
|
||||||
|
|
||||||
|
dev->pg_domain.ops.runtime_suspend = mei_me_pm_runtime_suspend;
|
||||||
|
dev->pg_domain.ops.runtime_resume = mei_me_pm_runtime_resume;
|
||||||
|
dev->pg_domain.ops.runtime_idle = mei_me_pm_runtime_idle;
|
||||||
|
|
||||||
|
pdev->dev.pm_domain = &dev->pg_domain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_me_unset_pm_domain - clean pm domian stucture for device
|
||||||
|
*
|
||||||
|
* @dev: mei_device
|
||||||
|
*/
|
||||||
|
static inline void mei_me_unset_pm_domain(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
/* stop using pm callbacks if any */
|
||||||
|
dev->pdev->dev.pm_domain = NULL;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PM_RUNTIME */
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static const struct dev_pm_ops mei_me_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(mei_me_pci_suspend,
|
||||||
|
mei_me_pci_resume)
|
||||||
|
SET_RUNTIME_PM_OPS(
|
||||||
|
mei_me_pm_runtime_suspend,
|
||||||
|
mei_me_pm_runtime_resume,
|
||||||
|
mei_me_pm_runtime_idle)
|
||||||
|
};
|
||||||
|
|
||||||
static SIMPLE_DEV_PM_OPS(mei_me_pm_ops, mei_me_pci_suspend, mei_me_pci_resume);
|
|
||||||
#define MEI_ME_PM_OPS (&mei_me_pm_ops)
|
#define MEI_ME_PM_OPS (&mei_me_pm_ops)
|
||||||
#else
|
#else
|
||||||
#define MEI_ME_PM_OPS NULL
|
#define MEI_ME_PM_OPS NULL
|
||||||
#endif /* CONFIG_PM_SLEEP */
|
#endif /* CONFIG_PM */
|
||||||
/*
|
/*
|
||||||
* PCI driver structure
|
* PCI driver structure
|
||||||
*/
|
*/
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <linux/jiffies.h>
|
#include <linux/jiffies.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#include <linux/mei.h>
|
#include <linux/mei.h>
|
||||||
|
|
||||||
@ -35,11 +36,18 @@
|
|||||||
#include "hw-txe.h"
|
#include "hw-txe.h"
|
||||||
|
|
||||||
static const struct pci_device_id mei_txe_pci_tbl[] = {
|
static const struct pci_device_id mei_txe_pci_tbl[] = {
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0F18)}, /* Baytrail */
|
{MEI_PCI_DEVICE(0x0F18, mei_txe_cfg)}, /* Baytrail */
|
||||||
{0, }
|
{0, }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl);
|
MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl);
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
static inline void mei_txe_set_pm_domain(struct mei_device *dev);
|
||||||
|
static inline void mei_txe_unset_pm_domain(struct mei_device *dev);
|
||||||
|
#else
|
||||||
|
static inline void mei_txe_set_pm_domain(struct mei_device *dev) {}
|
||||||
|
static inline void mei_txe_unset_pm_domain(struct mei_device *dev) {}
|
||||||
|
#endif /* CONFIG_PM_RUNTIME */
|
||||||
|
|
||||||
static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw)
|
static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw)
|
||||||
{
|
{
|
||||||
@ -61,6 +69,7 @@ static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw)
|
|||||||
*/
|
*/
|
||||||
static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||||
{
|
{
|
||||||
|
const struct mei_cfg *cfg = (struct mei_cfg *)(ent->driver_data);
|
||||||
struct mei_device *dev;
|
struct mei_device *dev;
|
||||||
struct mei_txe_hw *hw;
|
struct mei_txe_hw *hw;
|
||||||
int err;
|
int err;
|
||||||
@ -91,7 +100,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* allocates and initializes the mei dev structure */
|
/* allocates and initializes the mei dev structure */
|
||||||
dev = mei_txe_dev_init(pdev);
|
dev = mei_txe_dev_init(pdev, cfg);
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto release_regions;
|
goto release_regions;
|
||||||
@ -137,12 +146,25 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||||||
goto release_irq;
|
goto release_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_TXI_RPM_TIMEOUT);
|
||||||
|
pm_runtime_use_autosuspend(&pdev->dev);
|
||||||
|
|
||||||
err = mei_register(dev);
|
err = mei_register(dev);
|
||||||
if (err)
|
if (err)
|
||||||
goto release_irq;
|
goto release_irq;
|
||||||
|
|
||||||
pci_set_drvdata(pdev, dev);
|
pci_set_drvdata(pdev, dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For not wake-able HW runtime pm framework
|
||||||
|
* can't be used on pci device level.
|
||||||
|
* Use domain runtime pm callbacks instead.
|
||||||
|
*/
|
||||||
|
if (!pci_dev_run_wake(pdev))
|
||||||
|
mei_txe_set_pm_domain(dev);
|
||||||
|
|
||||||
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
release_irq:
|
release_irq:
|
||||||
@ -187,10 +209,15 @@ static void mei_txe_remove(struct pci_dev *pdev)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pm_runtime_get_noresume(&pdev->dev);
|
||||||
|
|
||||||
hw = to_txe_hw(dev);
|
hw = to_txe_hw(dev);
|
||||||
|
|
||||||
mei_stop(dev);
|
mei_stop(dev);
|
||||||
|
|
||||||
|
if (!pci_dev_run_wake(pdev))
|
||||||
|
mei_txe_unset_pm_domain(dev);
|
||||||
|
|
||||||
/* disable interrupts */
|
/* disable interrupts */
|
||||||
mei_disable_interrupts(dev);
|
mei_disable_interrupts(dev);
|
||||||
free_irq(pdev->irq, dev);
|
free_irq(pdev->irq, dev);
|
||||||
@ -265,15 +292,131 @@ static int mei_txe_pci_resume(struct device *device)
|
|||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
static SIMPLE_DEV_PM_OPS(mei_txe_pm_ops,
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
mei_txe_pci_suspend,
|
static int mei_txe_pm_runtime_idle(struct device *device)
|
||||||
mei_txe_pci_resume);
|
{
|
||||||
|
struct pci_dev *pdev = to_pci_dev(device);
|
||||||
|
struct mei_device *dev;
|
||||||
|
|
||||||
|
dev_dbg(&pdev->dev, "rpm: txe: runtime_idle\n");
|
||||||
|
|
||||||
|
dev = pci_get_drvdata(pdev);
|
||||||
|
if (!dev)
|
||||||
|
return -ENODEV;
|
||||||
|
if (mei_write_is_idle(dev))
|
||||||
|
pm_schedule_suspend(device, MEI_TXI_RPM_TIMEOUT * 2);
|
||||||
|
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
static int mei_txe_pm_runtime_suspend(struct device *device)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev = to_pci_dev(device);
|
||||||
|
struct mei_device *dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dev_dbg(&pdev->dev, "rpm: txe: runtime suspend\n");
|
||||||
|
|
||||||
|
dev = pci_get_drvdata(pdev);
|
||||||
|
if (!dev)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
mutex_lock(&dev->device_lock);
|
||||||
|
|
||||||
|
if (mei_write_is_idle(dev))
|
||||||
|
ret = mei_txe_aliveness_set_sync(dev, 0);
|
||||||
|
else
|
||||||
|
ret = -EAGAIN;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If everything is okay we're about to enter PCI low
|
||||||
|
* power state (D3) therefor we need to disable the
|
||||||
|
* interrupts towards host.
|
||||||
|
* However if device is not wakeable we do not enter
|
||||||
|
* D-low state and we need to keep the interrupt kicking
|
||||||
|
*/
|
||||||
|
if (!ret && pci_dev_run_wake(pdev))
|
||||||
|
mei_disable_interrupts(dev);
|
||||||
|
|
||||||
|
dev_dbg(&pdev->dev, "rpm: txe: runtime suspend ret=%d\n", ret);
|
||||||
|
|
||||||
|
mutex_unlock(&dev->device_lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mei_txe_pm_runtime_resume(struct device *device)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev = to_pci_dev(device);
|
||||||
|
struct mei_device *dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dev_dbg(&pdev->dev, "rpm: txe: runtime resume\n");
|
||||||
|
|
||||||
|
dev = pci_get_drvdata(pdev);
|
||||||
|
if (!dev)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
mutex_lock(&dev->device_lock);
|
||||||
|
|
||||||
|
mei_enable_interrupts(dev);
|
||||||
|
|
||||||
|
ret = mei_txe_aliveness_set_sync(dev, 1);
|
||||||
|
|
||||||
|
mutex_unlock(&dev->device_lock);
|
||||||
|
|
||||||
|
dev_dbg(&pdev->dev, "rpm: txe: runtime resume ret = %d\n", ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_txe_set_pm_domain - fill and set pm domian stucture for device
|
||||||
|
*
|
||||||
|
* @dev: mei_device
|
||||||
|
*/
|
||||||
|
static inline void mei_txe_set_pm_domain(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev = dev->pdev;
|
||||||
|
|
||||||
|
if (pdev->dev.bus && pdev->dev.bus->pm) {
|
||||||
|
dev->pg_domain.ops = *pdev->dev.bus->pm;
|
||||||
|
|
||||||
|
dev->pg_domain.ops.runtime_suspend = mei_txe_pm_runtime_suspend;
|
||||||
|
dev->pg_domain.ops.runtime_resume = mei_txe_pm_runtime_resume;
|
||||||
|
dev->pg_domain.ops.runtime_idle = mei_txe_pm_runtime_idle;
|
||||||
|
|
||||||
|
pdev->dev.pm_domain = &dev->pg_domain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_txe_unset_pm_domain - clean pm domian stucture for device
|
||||||
|
*
|
||||||
|
* @dev: mei_device
|
||||||
|
*/
|
||||||
|
static inline void mei_txe_unset_pm_domain(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
/* stop using pm callbacks if any */
|
||||||
|
dev->pdev->dev.pm_domain = NULL;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PM_RUNTIME */
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static const struct dev_pm_ops mei_txe_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(mei_txe_pci_suspend,
|
||||||
|
mei_txe_pci_resume)
|
||||||
|
SET_RUNTIME_PM_OPS(
|
||||||
|
mei_txe_pm_runtime_suspend,
|
||||||
|
mei_txe_pm_runtime_resume,
|
||||||
|
mei_txe_pm_runtime_idle)
|
||||||
|
};
|
||||||
|
|
||||||
#define MEI_TXE_PM_OPS (&mei_txe_pm_ops)
|
#define MEI_TXE_PM_OPS (&mei_txe_pm_ops)
|
||||||
#else
|
#else
|
||||||
#define MEI_TXE_PM_OPS NULL
|
#define MEI_TXE_PM_OPS NULL
|
||||||
#endif /* CONFIG_PM_SLEEP */
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PCI driver structure
|
* PCI driver structure
|
||||||
*/
|
*/
|
||||||
|
@ -84,8 +84,6 @@ int mei_wd_host_init(struct mei_device *dev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
cl->state = MEI_FILE_CONNECTING;
|
|
||||||
|
|
||||||
ret = mei_cl_connect(cl, NULL);
|
ret = mei_cl_connect(cl, NULL);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -266,11 +266,12 @@ config REGULATOR_LP8788
|
|||||||
This driver supports LP8788 voltage regulator chip.
|
This driver supports LP8788 voltage regulator chip.
|
||||||
|
|
||||||
config REGULATOR_MAX14577
|
config REGULATOR_MAX14577
|
||||||
tristate "Maxim 14577 regulator"
|
tristate "Maxim 14577/77836 regulator"
|
||||||
depends on MFD_MAX14577
|
depends on MFD_MAX14577
|
||||||
help
|
help
|
||||||
This driver controls a Maxim 14577 regulator via I2C bus.
|
This driver controls a Maxim MAX14577/77836 regulator via I2C bus.
|
||||||
The regulators include safeout LDO and current regulator 'CHARGER'.
|
The MAX14577 regulators include safeout LDO and charger current
|
||||||
|
regulator. The MAX77836 has two additional LDOs.
|
||||||
|
|
||||||
config REGULATOR_MAX1586
|
config REGULATOR_MAX1586
|
||||||
tristate "Maxim 1586/1587 voltage regulator"
|
tristate "Maxim 1586/1587 voltage regulator"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* max14577.c - Regulator driver for the Maxim 14577
|
* max14577.c - Regulator driver for the Maxim 14577/77836
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013,2014 Samsung Electronics
|
* Copyright (C) 2013,2014 Samsung Electronics
|
||||||
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||||
@ -22,6 +22,42 @@
|
|||||||
#include <linux/mfd/max14577-private.h>
|
#include <linux/mfd/max14577-private.h>
|
||||||
#include <linux/regulator/of_regulator.h>
|
#include <linux/regulator/of_regulator.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Valid limits of current for max14577 and max77836 chargers.
|
||||||
|
* They must correspond to MBCICHWRCL and MBCICHWRCH fields in CHGCTRL4
|
||||||
|
* register for given chipset.
|
||||||
|
*/
|
||||||
|
struct maxim_charger_current {
|
||||||
|
/* Minimal current, set in CHGCTRL4/MBCICHWRCL, uA */
|
||||||
|
unsigned int min;
|
||||||
|
/*
|
||||||
|
* Minimal current when high setting is active,
|
||||||
|
* set in CHGCTRL4/MBCICHWRCH, uA
|
||||||
|
*/
|
||||||
|
unsigned int high_start;
|
||||||
|
/* Value of one step in high setting, uA */
|
||||||
|
unsigned int high_step;
|
||||||
|
/* Maximum current of high setting, uA */
|
||||||
|
unsigned int max;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Table of valid charger currents for different Maxim chipsets */
|
||||||
|
static const struct maxim_charger_current maxim_charger_currents[] = {
|
||||||
|
[MAXIM_DEVICE_TYPE_UNKNOWN] = { 0, 0, 0, 0 },
|
||||||
|
[MAXIM_DEVICE_TYPE_MAX14577] = {
|
||||||
|
.min = MAX14577_REGULATOR_CURRENT_LIMIT_MIN,
|
||||||
|
.high_start = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START,
|
||||||
|
.high_step = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP,
|
||||||
|
.max = MAX14577_REGULATOR_CURRENT_LIMIT_MAX,
|
||||||
|
},
|
||||||
|
[MAXIM_DEVICE_TYPE_MAX77836] = {
|
||||||
|
.min = MAX77836_REGULATOR_CURRENT_LIMIT_MIN,
|
||||||
|
.high_start = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START,
|
||||||
|
.high_step = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP,
|
||||||
|
.max = MAX77836_REGULATOR_CURRENT_LIMIT_MAX,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
static int max14577_reg_is_enabled(struct regulator_dev *rdev)
|
static int max14577_reg_is_enabled(struct regulator_dev *rdev)
|
||||||
{
|
{
|
||||||
int rid = rdev_get_id(rdev);
|
int rid = rdev_get_id(rdev);
|
||||||
@ -47,6 +83,9 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev)
|
|||||||
{
|
{
|
||||||
u8 reg_data;
|
u8 reg_data;
|
||||||
struct regmap *rmap = rdev->regmap;
|
struct regmap *rmap = rdev->regmap;
|
||||||
|
struct max14577 *max14577 = rdev_get_drvdata(rdev);
|
||||||
|
const struct maxim_charger_current *limits =
|
||||||
|
&maxim_charger_currents[max14577->dev_type];
|
||||||
|
|
||||||
if (rdev_get_id(rdev) != MAX14577_CHARGER)
|
if (rdev_get_id(rdev) != MAX14577_CHARGER)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -54,12 +93,11 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev)
|
|||||||
max14577_read_reg(rmap, MAX14577_CHG_REG_CHG_CTRL4, ®_data);
|
max14577_read_reg(rmap, MAX14577_CHG_REG_CHG_CTRL4, ®_data);
|
||||||
|
|
||||||
if ((reg_data & CHGCTRL4_MBCICHWRCL_MASK) == 0)
|
if ((reg_data & CHGCTRL4_MBCICHWRCL_MASK) == 0)
|
||||||
return MAX14577_REGULATOR_CURRENT_LIMIT_MIN;
|
return limits->min;
|
||||||
|
|
||||||
reg_data = ((reg_data & CHGCTRL4_MBCICHWRCH_MASK) >>
|
reg_data = ((reg_data & CHGCTRL4_MBCICHWRCH_MASK) >>
|
||||||
CHGCTRL4_MBCICHWRCH_SHIFT);
|
CHGCTRL4_MBCICHWRCH_SHIFT);
|
||||||
return MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START +
|
return limits->high_start + reg_data * limits->high_step;
|
||||||
reg_data * MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int max14577_reg_set_current_limit(struct regulator_dev *rdev,
|
static int max14577_reg_set_current_limit(struct regulator_dev *rdev,
|
||||||
@ -67,33 +105,39 @@ static int max14577_reg_set_current_limit(struct regulator_dev *rdev,
|
|||||||
{
|
{
|
||||||
int i, current_bits = 0xf;
|
int i, current_bits = 0xf;
|
||||||
u8 reg_data;
|
u8 reg_data;
|
||||||
|
struct max14577 *max14577 = rdev_get_drvdata(rdev);
|
||||||
|
const struct maxim_charger_current *limits =
|
||||||
|
&maxim_charger_currents[max14577->dev_type];
|
||||||
|
|
||||||
if (rdev_get_id(rdev) != MAX14577_CHARGER)
|
if (rdev_get_id(rdev) != MAX14577_CHARGER)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (min_uA > MAX14577_REGULATOR_CURRENT_LIMIT_MAX ||
|
if (min_uA > limits->max || max_uA < limits->min)
|
||||||
max_uA < MAX14577_REGULATOR_CURRENT_LIMIT_MIN)
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (max_uA < MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START) {
|
if (max_uA < limits->high_start) {
|
||||||
/* Less than 200 mA, so set 90mA (turn only Low Bit off) */
|
/*
|
||||||
|
* Less than high_start,
|
||||||
|
* so set the minimal current (turn only Low Bit off)
|
||||||
|
*/
|
||||||
u8 reg_data = 0x0 << CHGCTRL4_MBCICHWRCL_SHIFT;
|
u8 reg_data = 0x0 << CHGCTRL4_MBCICHWRCL_SHIFT;
|
||||||
return max14577_update_reg(rdev->regmap,
|
return max14577_update_reg(rdev->regmap,
|
||||||
MAX14577_CHG_REG_CHG_CTRL4,
|
MAX14577_CHG_REG_CHG_CTRL4,
|
||||||
CHGCTRL4_MBCICHWRCL_MASK, reg_data);
|
CHGCTRL4_MBCICHWRCL_MASK, reg_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* max_uA is in range: <LIMIT_HIGH_START, inifinite>, so search for
|
/*
|
||||||
* valid current starting from LIMIT_MAX. */
|
* max_uA is in range: <high_start, inifinite>, so search for
|
||||||
for (i = MAX14577_REGULATOR_CURRENT_LIMIT_MAX;
|
* valid current starting from maximum current.
|
||||||
i >= MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START;
|
*/
|
||||||
i -= MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP) {
|
for (i = limits->max; i >= limits->high_start; i -= limits->high_step) {
|
||||||
if (i <= max_uA)
|
if (i <= max_uA)
|
||||||
break;
|
break;
|
||||||
current_bits--;
|
current_bits--;
|
||||||
}
|
}
|
||||||
BUG_ON(current_bits < 0); /* Cannot happen */
|
BUG_ON(current_bits < 0); /* Cannot happen */
|
||||||
/* Turn Low Bit on (use range 200mA-950 mA) */
|
|
||||||
|
/* Turn Low Bit on (use range high_start-max)... */
|
||||||
reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT;
|
reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT;
|
||||||
/* and set proper High Bits */
|
/* and set proper High Bits */
|
||||||
reg_data |= current_bits << CHGCTRL4_MBCICHWRCH_SHIFT;
|
reg_data |= current_bits << CHGCTRL4_MBCICHWRCH_SHIFT;
|
||||||
@ -118,7 +162,7 @@ static struct regulator_ops max14577_charger_ops = {
|
|||||||
.set_current_limit = max14577_reg_set_current_limit,
|
.set_current_limit = max14577_reg_set_current_limit,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct regulator_desc supported_regulators[] = {
|
static const struct regulator_desc max14577_supported_regulators[] = {
|
||||||
[MAX14577_SAFEOUT] = {
|
[MAX14577_SAFEOUT] = {
|
||||||
.name = "SAFEOUT",
|
.name = "SAFEOUT",
|
||||||
.id = MAX14577_SAFEOUT,
|
.id = MAX14577_SAFEOUT,
|
||||||
@ -141,16 +185,88 @@ static const struct regulator_desc supported_regulators[] = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct regulator_ops max77836_ldo_ops = {
|
||||||
|
.is_enabled = regulator_is_enabled_regmap,
|
||||||
|
.enable = regulator_enable_regmap,
|
||||||
|
.disable = regulator_disable_regmap,
|
||||||
|
.list_voltage = regulator_list_voltage_linear,
|
||||||
|
.map_voltage = regulator_map_voltage_linear,
|
||||||
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||||
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||||
|
/* TODO: add .set_suspend_mode */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct regulator_desc max77836_supported_regulators[] = {
|
||||||
|
[MAX14577_SAFEOUT] = {
|
||||||
|
.name = "SAFEOUT",
|
||||||
|
.id = MAX14577_SAFEOUT,
|
||||||
|
.ops = &max14577_safeout_ops,
|
||||||
|
.type = REGULATOR_VOLTAGE,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.n_voltages = 1,
|
||||||
|
.min_uV = MAX14577_REGULATOR_SAFEOUT_VOLTAGE,
|
||||||
|
.enable_reg = MAX14577_REG_CONTROL2,
|
||||||
|
.enable_mask = CTRL2_SFOUTORD_MASK,
|
||||||
|
},
|
||||||
|
[MAX14577_CHARGER] = {
|
||||||
|
.name = "CHARGER",
|
||||||
|
.id = MAX14577_CHARGER,
|
||||||
|
.ops = &max14577_charger_ops,
|
||||||
|
.type = REGULATOR_CURRENT,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.enable_reg = MAX14577_CHG_REG_CHG_CTRL2,
|
||||||
|
.enable_mask = CHGCTRL2_MBCHOSTEN_MASK,
|
||||||
|
},
|
||||||
|
[MAX77836_LDO1] = {
|
||||||
|
.name = "LDO1",
|
||||||
|
.id = MAX77836_LDO1,
|
||||||
|
.ops = &max77836_ldo_ops,
|
||||||
|
.type = REGULATOR_VOLTAGE,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.n_voltages = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM,
|
||||||
|
.min_uV = MAX77836_REGULATOR_LDO_VOLTAGE_MIN,
|
||||||
|
.uV_step = MAX77836_REGULATOR_LDO_VOLTAGE_STEP,
|
||||||
|
.enable_reg = MAX77836_LDO_REG_CNFG1_LDO1,
|
||||||
|
.enable_mask = MAX77836_CNFG1_LDO_PWRMD_MASK,
|
||||||
|
.vsel_reg = MAX77836_LDO_REG_CNFG1_LDO1,
|
||||||
|
.vsel_mask = MAX77836_CNFG1_LDO_TV_MASK,
|
||||||
|
},
|
||||||
|
[MAX77836_LDO2] = {
|
||||||
|
.name = "LDO2",
|
||||||
|
.id = MAX77836_LDO2,
|
||||||
|
.ops = &max77836_ldo_ops,
|
||||||
|
.type = REGULATOR_VOLTAGE,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.n_voltages = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM,
|
||||||
|
.min_uV = MAX77836_REGULATOR_LDO_VOLTAGE_MIN,
|
||||||
|
.uV_step = MAX77836_REGULATOR_LDO_VOLTAGE_STEP,
|
||||||
|
.enable_reg = MAX77836_LDO_REG_CNFG1_LDO2,
|
||||||
|
.enable_mask = MAX77836_CNFG1_LDO_PWRMD_MASK,
|
||||||
|
.vsel_reg = MAX77836_LDO_REG_CNFG1_LDO2,
|
||||||
|
.vsel_mask = MAX77836_CNFG1_LDO_TV_MASK,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
static struct of_regulator_match max14577_regulator_matches[] = {
|
static struct of_regulator_match max14577_regulator_matches[] = {
|
||||||
{ .name = "SAFEOUT", },
|
{ .name = "SAFEOUT", },
|
||||||
{ .name = "CHARGER", },
|
{ .name = "CHARGER", },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev)
|
static struct of_regulator_match max77836_regulator_matches[] = {
|
||||||
|
{ .name = "SAFEOUT", },
|
||||||
|
{ .name = "CHARGER", },
|
||||||
|
{ .name = "LDO1", },
|
||||||
|
{ .name = "LDO2", },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev,
|
||||||
|
enum maxim_device_type dev_type)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct device_node *np;
|
struct device_node *np;
|
||||||
|
struct of_regulator_match *regulator_matches;
|
||||||
|
unsigned int regulator_matches_size;
|
||||||
|
|
||||||
np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators");
|
np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators");
|
||||||
if (!np) {
|
if (!np) {
|
||||||
@ -158,8 +274,19 @@ static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = of_regulator_match(&pdev->dev, np, max14577_regulator_matches,
|
switch (dev_type) {
|
||||||
MAX14577_REG_MAX);
|
case MAXIM_DEVICE_TYPE_MAX77836:
|
||||||
|
regulator_matches = max77836_regulator_matches;
|
||||||
|
regulator_matches_size = ARRAY_SIZE(max77836_regulator_matches);
|
||||||
|
break;
|
||||||
|
case MAXIM_DEVICE_TYPE_MAX14577:
|
||||||
|
default:
|
||||||
|
regulator_matches = max14577_regulator_matches;
|
||||||
|
regulator_matches_size = ARRAY_SIZE(max14577_regulator_matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = of_regulator_match(&pdev->dev, np, regulator_matches,
|
||||||
|
regulator_matches_size);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret);
|
dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret);
|
||||||
else
|
else
|
||||||
@ -170,31 +297,74 @@ static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct regulator_init_data *match_init_data(int index)
|
static inline struct regulator_init_data *match_init_data(int index,
|
||||||
|
enum maxim_device_type dev_type)
|
||||||
{
|
{
|
||||||
return max14577_regulator_matches[index].init_data;
|
switch (dev_type) {
|
||||||
|
case MAXIM_DEVICE_TYPE_MAX77836:
|
||||||
|
return max77836_regulator_matches[index].init_data;
|
||||||
|
|
||||||
|
case MAXIM_DEVICE_TYPE_MAX14577:
|
||||||
|
default:
|
||||||
|
return max14577_regulator_matches[index].init_data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct device_node *match_of_node(int index)
|
static inline struct device_node *match_of_node(int index,
|
||||||
|
enum maxim_device_type dev_type)
|
||||||
{
|
{
|
||||||
return max14577_regulator_matches[index].of_node;
|
switch (dev_type) {
|
||||||
|
case MAXIM_DEVICE_TYPE_MAX77836:
|
||||||
|
return max77836_regulator_matches[index].of_node;
|
||||||
|
|
||||||
|
case MAXIM_DEVICE_TYPE_MAX14577:
|
||||||
|
default:
|
||||||
|
return max14577_regulator_matches[index].of_node;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#else /* CONFIG_OF */
|
#else /* CONFIG_OF */
|
||||||
static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev)
|
static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev,
|
||||||
|
enum maxim_device_type dev_type)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
static inline struct regulator_init_data *match_init_data(int index)
|
static inline struct regulator_init_data *match_init_data(int index,
|
||||||
|
enum maxim_device_type dev_type)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct device_node *match_of_node(int index)
|
static inline struct device_node *match_of_node(int index,
|
||||||
|
enum maxim_device_type dev_type)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_OF */
|
#endif /* CONFIG_OF */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers for regulators of max77836 use different I2C slave addresses so
|
||||||
|
* different regmaps must be used for them.
|
||||||
|
*
|
||||||
|
* Returns proper regmap for accessing regulator passed by id.
|
||||||
|
*/
|
||||||
|
static struct regmap *max14577_get_regmap(struct max14577 *max14577,
|
||||||
|
int reg_id)
|
||||||
|
{
|
||||||
|
switch (max14577->dev_type) {
|
||||||
|
case MAXIM_DEVICE_TYPE_MAX77836:
|
||||||
|
switch (reg_id) {
|
||||||
|
case MAX77836_SAFEOUT ... MAX77836_CHARGER:
|
||||||
|
return max14577->regmap;
|
||||||
|
default:
|
||||||
|
/* MAX77836_LDO1 ... MAX77836_LDO2 */
|
||||||
|
return max14577->regmap_pmic;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MAXIM_DEVICE_TYPE_MAX14577:
|
||||||
|
default:
|
||||||
|
return max14577->regmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int max14577_regulator_probe(struct platform_device *pdev)
|
static int max14577_regulator_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
@ -202,15 +372,29 @@ static int max14577_regulator_probe(struct platform_device *pdev)
|
|||||||
struct max14577_platform_data *pdata = dev_get_platdata(max14577->dev);
|
struct max14577_platform_data *pdata = dev_get_platdata(max14577->dev);
|
||||||
int i, ret;
|
int i, ret;
|
||||||
struct regulator_config config = {};
|
struct regulator_config config = {};
|
||||||
|
const struct regulator_desc *supported_regulators;
|
||||||
|
unsigned int supported_regulators_size;
|
||||||
|
enum maxim_device_type dev_type = max14577->dev_type;
|
||||||
|
|
||||||
ret = max14577_regulator_dt_parse_pdata(pdev);
|
ret = max14577_regulator_dt_parse_pdata(pdev, dev_type);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
config.dev = &pdev->dev;
|
switch (dev_type) {
|
||||||
config.regmap = max14577->regmap;
|
case MAXIM_DEVICE_TYPE_MAX77836:
|
||||||
|
supported_regulators = max77836_supported_regulators;
|
||||||
|
supported_regulators_size = ARRAY_SIZE(max77836_supported_regulators);
|
||||||
|
break;
|
||||||
|
case MAXIM_DEVICE_TYPE_MAX14577:
|
||||||
|
default:
|
||||||
|
supported_regulators = max14577_supported_regulators;
|
||||||
|
supported_regulators_size = ARRAY_SIZE(max14577_supported_regulators);
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(supported_regulators); i++) {
|
config.dev = &pdev->dev;
|
||||||
|
config.driver_data = max14577;
|
||||||
|
|
||||||
|
for (i = 0; i < supported_regulators_size; i++) {
|
||||||
struct regulator_dev *regulator;
|
struct regulator_dev *regulator;
|
||||||
/*
|
/*
|
||||||
* Index of supported_regulators[] is also the id and must
|
* Index of supported_regulators[] is also the id and must
|
||||||
@ -220,17 +404,19 @@ static int max14577_regulator_probe(struct platform_device *pdev)
|
|||||||
config.init_data = pdata->regulators[i].initdata;
|
config.init_data = pdata->regulators[i].initdata;
|
||||||
config.of_node = pdata->regulators[i].of_node;
|
config.of_node = pdata->regulators[i].of_node;
|
||||||
} else {
|
} else {
|
||||||
config.init_data = match_init_data(i);
|
config.init_data = match_init_data(i, dev_type);
|
||||||
config.of_node = match_of_node(i);
|
config.of_node = match_of_node(i, dev_type);
|
||||||
}
|
}
|
||||||
|
config.regmap = max14577_get_regmap(max14577,
|
||||||
|
supported_regulators[i].id);
|
||||||
|
|
||||||
regulator = devm_regulator_register(&pdev->dev,
|
regulator = devm_regulator_register(&pdev->dev,
|
||||||
&supported_regulators[i], &config);
|
&supported_regulators[i], &config);
|
||||||
if (IS_ERR(regulator)) {
|
if (IS_ERR(regulator)) {
|
||||||
ret = PTR_ERR(regulator);
|
ret = PTR_ERR(regulator);
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"Regulator init failed for ID %d with error: %d\n",
|
"Regulator init failed for %d/%s with error: %d\n",
|
||||||
i, ret);
|
i, supported_regulators[i].name, ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -238,20 +424,41 @@ static int max14577_regulator_probe(struct platform_device *pdev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct platform_device_id max14577_regulator_id[] = {
|
||||||
|
{ "max14577-regulator", MAXIM_DEVICE_TYPE_MAX14577, },
|
||||||
|
{ "max77836-regulator", MAXIM_DEVICE_TYPE_MAX77836, },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(platform, max14577_regulator_id);
|
||||||
|
|
||||||
static struct platform_driver max14577_regulator_driver = {
|
static struct platform_driver max14577_regulator_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.name = "max14577-regulator",
|
.name = "max14577-regulator",
|
||||||
},
|
},
|
||||||
.probe = max14577_regulator_probe,
|
.probe = max14577_regulator_probe,
|
||||||
|
.id_table = max14577_regulator_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init max14577_regulator_init(void)
|
static int __init max14577_regulator_init(void)
|
||||||
{
|
{
|
||||||
|
/* Check for valid values for charger */
|
||||||
BUILD_BUG_ON(MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START +
|
BUILD_BUG_ON(MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START +
|
||||||
MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf !=
|
MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf !=
|
||||||
MAX14577_REGULATOR_CURRENT_LIMIT_MAX);
|
MAX14577_REGULATOR_CURRENT_LIMIT_MAX);
|
||||||
BUILD_BUG_ON(ARRAY_SIZE(supported_regulators) != MAX14577_REG_MAX);
|
BUILD_BUG_ON(MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START +
|
||||||
|
MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf !=
|
||||||
|
MAX77836_REGULATOR_CURRENT_LIMIT_MAX);
|
||||||
|
/* Valid charger current values must be provided for each chipset */
|
||||||
|
BUILD_BUG_ON(ARRAY_SIZE(maxim_charger_currents) != MAXIM_DEVICE_TYPE_NUM);
|
||||||
|
|
||||||
|
BUILD_BUG_ON(ARRAY_SIZE(max14577_supported_regulators) != MAX14577_REGULATOR_NUM);
|
||||||
|
BUILD_BUG_ON(ARRAY_SIZE(max77836_supported_regulators) != MAX77836_REGULATOR_NUM);
|
||||||
|
|
||||||
|
BUILD_BUG_ON(MAX77836_REGULATOR_LDO_VOLTAGE_MIN +
|
||||||
|
(MAX77836_REGULATOR_LDO_VOLTAGE_STEP *
|
||||||
|
(MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM - 1)) !=
|
||||||
|
MAX77836_REGULATOR_LDO_VOLTAGE_MAX);
|
||||||
|
|
||||||
return platform_driver_register(&max14577_regulator_driver);
|
return platform_driver_register(&max14577_regulator_driver);
|
||||||
}
|
}
|
||||||
@ -264,6 +471,6 @@ static void __exit max14577_regulator_exit(void)
|
|||||||
module_exit(max14577_regulator_exit);
|
module_exit(max14577_regulator_exit);
|
||||||
|
|
||||||
MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
|
MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
|
||||||
MODULE_DESCRIPTION("MAXIM 14577 regulator driver");
|
MODULE_DESCRIPTION("Maxim 14577/77836 regulator driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_ALIAS("platform:max14577-regulator");
|
MODULE_ALIAS("platform:max14577-regulator");
|
||||||
|
@ -655,7 +655,7 @@ static int uio_mmap_physical(struct vm_area_struct *vma)
|
|||||||
|
|
||||||
if (mem->addr & ~PAGE_MASK)
|
if (mem->addr & ~PAGE_MASK)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
if (vma->vm_end - vma->vm_start > mem->size)
|
if (vma->vm_end - vma->vm_start > PAGE_ALIGN(mem->size))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
vma->vm_ops = &uio_physical_vm_ops;
|
vma->vm_ops = &uio_physical_vm_ops;
|
||||||
|
@ -204,7 +204,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
|
|||||||
ret = platform_get_irq(pdev, 0);
|
ret = platform_get_irq(pdev, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "failed to get IRQ\n");
|
dev_err(&pdev->dev, "failed to get IRQ\n");
|
||||||
goto bad0;
|
goto bad1;
|
||||||
}
|
}
|
||||||
uioinfo->irq = ret;
|
uioinfo->irq = ret;
|
||||||
}
|
}
|
||||||
@ -275,6 +275,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
|
|||||||
ret = uio_register_device(&pdev->dev, priv->uioinfo);
|
ret = uio_register_device(&pdev->dev, priv->uioinfo);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "unable to register uio device\n");
|
dev_err(&pdev->dev, "unable to register uio device\n");
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
goto bad1;
|
goto bad1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +283,6 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
bad1:
|
bad1:
|
||||||
kfree(priv);
|
kfree(priv);
|
||||||
pm_runtime_disable(&pdev->dev);
|
|
||||||
bad0:
|
bad0:
|
||||||
/* kfree uioinfo for OF */
|
/* kfree uioinfo for OF */
|
||||||
if (pdev->dev.of_node)
|
if (pdev->dev.of_node)
|
||||||
|
@ -1078,6 +1078,8 @@ static void w1_search_process(struct w1_master *dev, u8 search_type)
|
|||||||
* w1_process_callbacks() - execute each dev->async_list callback entry
|
* w1_process_callbacks() - execute each dev->async_list callback entry
|
||||||
* @dev: w1_master device
|
* @dev: w1_master device
|
||||||
*
|
*
|
||||||
|
* The w1 master list_mutex must be held.
|
||||||
|
*
|
||||||
* Return: 1 if there were commands to executed 0 otherwise
|
* Return: 1 if there were commands to executed 0 otherwise
|
||||||
*/
|
*/
|
||||||
int w1_process_callbacks(struct w1_master *dev)
|
int w1_process_callbacks(struct w1_master *dev)
|
||||||
|
@ -203,7 +203,6 @@ enum w1_master_flags {
|
|||||||
* @search_id: allows continuing a search
|
* @search_id: allows continuing a search
|
||||||
* @refcnt: reference count
|
* @refcnt: reference count
|
||||||
* @priv: private data storage
|
* @priv: private data storage
|
||||||
* @priv_size: size allocated
|
|
||||||
* @enable_pullup: allows a strong pullup
|
* @enable_pullup: allows a strong pullup
|
||||||
* @pullup_duration: time for the next strong pullup
|
* @pullup_duration: time for the next strong pullup
|
||||||
* @flags: one of w1_master_flags
|
* @flags: one of w1_master_flags
|
||||||
@ -214,7 +213,6 @@ enum w1_master_flags {
|
|||||||
* @dev: sysfs device
|
* @dev: sysfs device
|
||||||
* @bus_master: io operations available
|
* @bus_master: io operations available
|
||||||
* @seq: sequence number used for netlink broadcasts
|
* @seq: sequence number used for netlink broadcasts
|
||||||
* @portid: destination for the current netlink command
|
|
||||||
*/
|
*/
|
||||||
struct w1_master
|
struct w1_master
|
||||||
{
|
{
|
||||||
@ -241,7 +239,6 @@ struct w1_master
|
|||||||
atomic_t refcnt;
|
atomic_t refcnt;
|
||||||
|
|
||||||
void *priv;
|
void *priv;
|
||||||
int priv_size;
|
|
||||||
|
|
||||||
/** 5V strong pullup enabled flag, 1 enabled, zero disabled. */
|
/** 5V strong pullup enabled flag, 1 enabled, zero disabled. */
|
||||||
int enable_pullup;
|
int enable_pullup;
|
||||||
@ -260,11 +257,6 @@ struct w1_master
|
|||||||
struct w1_bus_master *bus_master;
|
struct w1_bus_master *bus_master;
|
||||||
|
|
||||||
u32 seq;
|
u32 seq;
|
||||||
/* port id to send netlink responses to. The value is temporarily
|
|
||||||
* stored here while processing a message, set after locking the
|
|
||||||
* mutex, zero before unlocking the mutex.
|
|
||||||
*/
|
|
||||||
u32 portid;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -219,9 +219,13 @@ void __w1_remove_master_device(struct w1_master *dev)
|
|||||||
|
|
||||||
if (msleep_interruptible(1000))
|
if (msleep_interruptible(1000))
|
||||||
flush_signals(current);
|
flush_signals(current);
|
||||||
|
mutex_lock(&dev->list_mutex);
|
||||||
w1_process_callbacks(dev);
|
w1_process_callbacks(dev);
|
||||||
|
mutex_unlock(&dev->list_mutex);
|
||||||
}
|
}
|
||||||
|
mutex_lock(&dev->list_mutex);
|
||||||
w1_process_callbacks(dev);
|
w1_process_callbacks(dev);
|
||||||
|
mutex_unlock(&dev->list_mutex);
|
||||||
|
|
||||||
memset(&msg, 0, sizeof(msg));
|
memset(&msg, 0, sizeof(msg));
|
||||||
msg.id.mst.id = dev->id;
|
msg.id.mst.id = dev->id;
|
||||||
|
@ -29,51 +29,247 @@
|
|||||||
#include "w1_netlink.h"
|
#include "w1_netlink.h"
|
||||||
|
|
||||||
#if defined(CONFIG_W1_CON) && (defined(CONFIG_CONNECTOR) || (defined(CONFIG_CONNECTOR_MODULE) && defined(CONFIG_W1_MODULE)))
|
#if defined(CONFIG_W1_CON) && (defined(CONFIG_CONNECTOR) || (defined(CONFIG_CONNECTOR_MODULE) && defined(CONFIG_W1_MODULE)))
|
||||||
|
|
||||||
|
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||||
|
|
||||||
|
/* Bundle together everything required to process a request in one memory
|
||||||
|
* allocation.
|
||||||
|
*/
|
||||||
|
struct w1_cb_block {
|
||||||
|
atomic_t refcnt;
|
||||||
|
u32 portid; /* Sending process port ID */
|
||||||
|
/* maximum value for first_cn->len */
|
||||||
|
u16 maxlen;
|
||||||
|
/* pointers to building up the reply message */
|
||||||
|
struct cn_msg *first_cn; /* fixed once the structure is populated */
|
||||||
|
struct cn_msg *cn; /* advances as cn_msg is appeneded */
|
||||||
|
struct w1_netlink_msg *msg; /* advances as w1_netlink_msg is appened */
|
||||||
|
struct w1_netlink_cmd *cmd; /* advances as cmds are appened */
|
||||||
|
struct w1_netlink_msg *cur_msg; /* currently message being processed */
|
||||||
|
/* copy of the original request follows */
|
||||||
|
struct cn_msg request_cn;
|
||||||
|
/* followed by variable length:
|
||||||
|
* cn_msg, data (w1_netlink_msg and w1_netlink_cmd)
|
||||||
|
* one or more struct w1_cb_node
|
||||||
|
* reply first_cn, data (w1_netlink_msg and w1_netlink_cmd)
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
struct w1_cb_node {
|
||||||
|
struct w1_async_cmd async;
|
||||||
|
/* pointers within w1_cb_block and cn data */
|
||||||
|
struct w1_cb_block *block;
|
||||||
|
struct w1_netlink_msg *msg;
|
||||||
|
struct w1_slave *sl;
|
||||||
|
struct w1_master *dev;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* w1_reply_len() - calculate current reply length, compare to maxlen
|
||||||
|
* @block: block to calculate
|
||||||
|
*
|
||||||
|
* Calculates the current message length including possible multiple
|
||||||
|
* cn_msg and data, excludes the first sizeof(struct cn_msg). Direclty
|
||||||
|
* compariable to maxlen and usable to send the message.
|
||||||
|
*/
|
||||||
|
static u16 w1_reply_len(struct w1_cb_block *block)
|
||||||
|
{
|
||||||
|
if (!block->cn)
|
||||||
|
return 0;
|
||||||
|
return (u8 *)block->cn - (u8 *)block->first_cn + block->cn->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void w1_unref_block(struct w1_cb_block *block)
|
||||||
|
{
|
||||||
|
if (atomic_sub_return(1, &block->refcnt) == 0) {
|
||||||
|
u16 len = w1_reply_len(block);
|
||||||
|
if (len) {
|
||||||
|
cn_netlink_send_mult(block->first_cn, len,
|
||||||
|
block->portid, 0, GFP_KERNEL);
|
||||||
|
}
|
||||||
|
kfree(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* w1_reply_make_space() - send message if needed to make space
|
||||||
|
* @block: block to make space on
|
||||||
|
* @space: how many bytes requested
|
||||||
|
*
|
||||||
|
* Verify there is enough room left for the caller to add "space" bytes to the
|
||||||
|
* message, if there isn't send the message and reset.
|
||||||
|
*/
|
||||||
|
static void w1_reply_make_space(struct w1_cb_block *block, u16 space)
|
||||||
|
{
|
||||||
|
u16 len = w1_reply_len(block);
|
||||||
|
if (len + space >= block->maxlen) {
|
||||||
|
cn_netlink_send_mult(block->first_cn, len, block->portid, 0, GFP_KERNEL);
|
||||||
|
block->first_cn->len = 0;
|
||||||
|
block->cn = NULL;
|
||||||
|
block->msg = NULL;
|
||||||
|
block->cmd = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Early send when replies aren't bundled. */
|
||||||
|
static void w1_netlink_check_send(struct w1_cb_block *block)
|
||||||
|
{
|
||||||
|
if (!(block->request_cn.flags & W1_CN_BUNDLE) && block->cn)
|
||||||
|
w1_reply_make_space(block, block->maxlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* w1_netlink_setup_msg() - prepare to write block->msg
|
||||||
|
* @block: block to operate on
|
||||||
|
* @ack: determines if cn can be reused
|
||||||
|
*
|
||||||
|
* block->cn will be setup with the correct ack, advancing if needed
|
||||||
|
* block->cn->len does not include space for block->msg
|
||||||
|
* block->msg advances but remains uninitialized
|
||||||
|
*/
|
||||||
|
static void w1_netlink_setup_msg(struct w1_cb_block *block, u32 ack)
|
||||||
|
{
|
||||||
|
if (block->cn && block->cn->ack == ack) {
|
||||||
|
block->msg = (struct w1_netlink_msg *)(block->cn->data + block->cn->len);
|
||||||
|
} else {
|
||||||
|
/* advance or set to data */
|
||||||
|
if (block->cn)
|
||||||
|
block->cn = (struct cn_msg *)(block->cn->data +
|
||||||
|
block->cn->len);
|
||||||
|
else
|
||||||
|
block->cn = block->first_cn;
|
||||||
|
|
||||||
|
memcpy(block->cn, &block->request_cn, sizeof(*block->cn));
|
||||||
|
block->cn->len = 0;
|
||||||
|
block->cn->ack = ack;
|
||||||
|
block->msg = (struct w1_netlink_msg *)block->cn->data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append cmd to msg, include cmd->data as well. This is because
|
||||||
|
* any following data goes with the command and in the case of a read is
|
||||||
|
* the results.
|
||||||
|
*/
|
||||||
|
static void w1_netlink_queue_cmd(struct w1_cb_block *block,
|
||||||
|
struct w1_netlink_cmd *cmd)
|
||||||
|
{
|
||||||
|
u32 space;
|
||||||
|
w1_reply_make_space(block, sizeof(struct cn_msg) +
|
||||||
|
sizeof(struct w1_netlink_msg) + sizeof(*cmd) + cmd->len);
|
||||||
|
|
||||||
|
/* There's a status message sent after each command, so no point
|
||||||
|
* in trying to bundle this cmd after an existing one, because
|
||||||
|
* there won't be one. Allocate and copy over a new cn_msg.
|
||||||
|
*/
|
||||||
|
w1_netlink_setup_msg(block, block->request_cn.seq + 1);
|
||||||
|
memcpy(block->msg, block->cur_msg, sizeof(*block->msg));
|
||||||
|
block->cn->len += sizeof(*block->msg);
|
||||||
|
block->msg->len = 0;
|
||||||
|
block->cmd = (struct w1_netlink_cmd *)(block->msg->data);
|
||||||
|
|
||||||
|
space = sizeof(*cmd) + cmd->len;
|
||||||
|
if (block->cmd != cmd)
|
||||||
|
memcpy(block->cmd, cmd, space);
|
||||||
|
block->cn->len += space;
|
||||||
|
block->msg->len += space;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append req_msg and req_cmd, no other commands and no data from req_cmd are
|
||||||
|
* copied.
|
||||||
|
*/
|
||||||
|
static void w1_netlink_queue_status(struct w1_cb_block *block,
|
||||||
|
struct w1_netlink_msg *req_msg, struct w1_netlink_cmd *req_cmd,
|
||||||
|
int error)
|
||||||
|
{
|
||||||
|
u16 space = sizeof(struct cn_msg) + sizeof(*req_msg) + sizeof(*req_cmd);
|
||||||
|
w1_reply_make_space(block, space);
|
||||||
|
w1_netlink_setup_msg(block, block->request_cn.ack);
|
||||||
|
|
||||||
|
memcpy(block->msg, req_msg, sizeof(*req_msg));
|
||||||
|
block->cn->len += sizeof(*req_msg);
|
||||||
|
block->msg->len = 0;
|
||||||
|
block->msg->status = (u8)-error;
|
||||||
|
if (req_cmd) {
|
||||||
|
struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)block->msg->data;
|
||||||
|
memcpy(cmd, req_cmd, sizeof(*cmd));
|
||||||
|
block->cn->len += sizeof(*cmd);
|
||||||
|
block->msg->len += sizeof(*cmd);
|
||||||
|
cmd->len = 0;
|
||||||
|
}
|
||||||
|
w1_netlink_check_send(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* w1_netlink_send_error() - sends the error message now
|
||||||
|
* @cn: original cn_msg
|
||||||
|
* @msg: original w1_netlink_msg
|
||||||
|
* @portid: where to send it
|
||||||
|
* @error: error status
|
||||||
|
*
|
||||||
|
* Use when a block isn't available to queue the message to and cn, msg
|
||||||
|
* might not be contiguous.
|
||||||
|
*/
|
||||||
|
static void w1_netlink_send_error(struct cn_msg *cn, struct w1_netlink_msg *msg,
|
||||||
|
int portid, int error)
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
struct cn_msg cn;
|
||||||
|
struct w1_netlink_msg msg;
|
||||||
|
} packet;
|
||||||
|
memcpy(&packet.cn, cn, sizeof(packet.cn));
|
||||||
|
memcpy(&packet.msg, msg, sizeof(packet.msg));
|
||||||
|
packet.cn.len = sizeof(packet.msg);
|
||||||
|
packet.msg.len = 0;
|
||||||
|
packet.msg.status = (u8)-error;
|
||||||
|
cn_netlink_send(&packet.cn, portid, 0, GFP_KERNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* w1_netlink_send() - sends w1 netlink notifications
|
||||||
|
* @dev: w1_master the even is associated with or for
|
||||||
|
* @msg: w1_netlink_msg message to be sent
|
||||||
|
*
|
||||||
|
* This are notifications generated from the kernel.
|
||||||
|
*/
|
||||||
void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
|
void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
|
||||||
{
|
{
|
||||||
char buf[sizeof(struct cn_msg) + sizeof(struct w1_netlink_msg)];
|
struct {
|
||||||
struct cn_msg *m = (struct cn_msg *)buf;
|
struct cn_msg cn;
|
||||||
struct w1_netlink_msg *w = (struct w1_netlink_msg *)(m+1);
|
struct w1_netlink_msg msg;
|
||||||
|
} packet;
|
||||||
|
memset(&packet, 0, sizeof(packet));
|
||||||
|
|
||||||
memset(buf, 0, sizeof(buf));
|
packet.cn.id.idx = CN_W1_IDX;
|
||||||
|
packet.cn.id.val = CN_W1_VAL;
|
||||||
|
|
||||||
m->id.idx = CN_W1_IDX;
|
packet.cn.seq = dev->seq++;
|
||||||
m->id.val = CN_W1_VAL;
|
packet.cn.len = sizeof(*msg);
|
||||||
|
|
||||||
m->seq = dev->seq++;
|
memcpy(&packet.msg, msg, sizeof(*msg));
|
||||||
m->len = sizeof(struct w1_netlink_msg);
|
packet.msg.len = 0;
|
||||||
|
|
||||||
memcpy(w, msg, sizeof(struct w1_netlink_msg));
|
cn_netlink_send(&packet.cn, 0, 0, GFP_KERNEL);
|
||||||
|
|
||||||
cn_netlink_send(m, dev->portid, 0, GFP_KERNEL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void w1_send_slave(struct w1_master *dev, u64 rn)
|
static void w1_send_slave(struct w1_master *dev, u64 rn)
|
||||||
{
|
{
|
||||||
struct cn_msg *msg = dev->priv;
|
struct w1_cb_block *block = dev->priv;
|
||||||
struct w1_netlink_msg *hdr = (struct w1_netlink_msg *)(msg + 1);
|
struct w1_netlink_cmd *cache_cmd = block->cmd;
|
||||||
struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1);
|
|
||||||
int avail;
|
|
||||||
u64 *data;
|
u64 *data;
|
||||||
|
|
||||||
avail = dev->priv_size - cmd->len;
|
w1_reply_make_space(block, sizeof(*data));
|
||||||
|
|
||||||
if (avail < 8) {
|
/* Add cmd back if the packet was sent */
|
||||||
msg->ack++;
|
if (!block->cmd) {
|
||||||
cn_netlink_send(msg, dev->portid, 0, GFP_KERNEL);
|
cache_cmd->len = 0;
|
||||||
|
w1_netlink_queue_cmd(block, cache_cmd);
|
||||||
msg->len = sizeof(struct w1_netlink_msg) +
|
|
||||||
sizeof(struct w1_netlink_cmd);
|
|
||||||
hdr->len = sizeof(struct w1_netlink_cmd);
|
|
||||||
cmd->len = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data = (void *)(cmd + 1) + cmd->len;
|
data = (u64 *)(block->cmd->data + block->cmd->len);
|
||||||
|
|
||||||
*data = rn;
|
*data = rn;
|
||||||
cmd->len += 8;
|
block->cn->len += sizeof(*data);
|
||||||
hdr->len += 8;
|
block->msg->len += sizeof(*data);
|
||||||
msg->len += 8;
|
block->cmd->len += sizeof(*data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void w1_found_send_slave(struct w1_master *dev, u64 rn)
|
static void w1_found_send_slave(struct w1_master *dev, u64 rn)
|
||||||
@ -85,40 +281,15 @@ static void w1_found_send_slave(struct w1_master *dev, u64 rn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Get the current slave list, or search (with or without alarm) */
|
/* Get the current slave list, or search (with or without alarm) */
|
||||||
static int w1_get_slaves(struct w1_master *dev,
|
static int w1_get_slaves(struct w1_master *dev, struct w1_netlink_cmd *req_cmd)
|
||||||
struct cn_msg *req_msg, struct w1_netlink_msg *req_hdr,
|
|
||||||
struct w1_netlink_cmd *req_cmd)
|
|
||||||
{
|
{
|
||||||
struct cn_msg *msg;
|
|
||||||
struct w1_netlink_msg *hdr;
|
|
||||||
struct w1_netlink_cmd *cmd;
|
|
||||||
struct w1_slave *sl;
|
struct w1_slave *sl;
|
||||||
|
|
||||||
msg = kzalloc(PAGE_SIZE, GFP_KERNEL);
|
req_cmd->len = 0;
|
||||||
if (!msg)
|
w1_netlink_queue_cmd(dev->priv, req_cmd);
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
msg->id = req_msg->id;
|
|
||||||
msg->seq = req_msg->seq;
|
|
||||||
msg->ack = 0;
|
|
||||||
msg->len = sizeof(struct w1_netlink_msg) +
|
|
||||||
sizeof(struct w1_netlink_cmd);
|
|
||||||
|
|
||||||
hdr = (struct w1_netlink_msg *)(msg + 1);
|
|
||||||
cmd = (struct w1_netlink_cmd *)(hdr + 1);
|
|
||||||
|
|
||||||
hdr->type = W1_MASTER_CMD;
|
|
||||||
hdr->id = req_hdr->id;
|
|
||||||
hdr->len = sizeof(struct w1_netlink_cmd);
|
|
||||||
|
|
||||||
cmd->cmd = req_cmd->cmd;
|
|
||||||
cmd->len = 0;
|
|
||||||
|
|
||||||
dev->priv = msg;
|
|
||||||
dev->priv_size = PAGE_SIZE - msg->len - sizeof(struct cn_msg);
|
|
||||||
|
|
||||||
if (req_cmd->cmd == W1_CMD_LIST_SLAVES) {
|
if (req_cmd->cmd == W1_CMD_LIST_SLAVES) {
|
||||||
__u64 rn;
|
u64 rn;
|
||||||
mutex_lock(&dev->list_mutex);
|
mutex_lock(&dev->list_mutex);
|
||||||
list_for_each_entry(sl, &dev->slist, w1_slave_entry) {
|
list_for_each_entry(sl, &dev->slist, w1_slave_entry) {
|
||||||
memcpy(&rn, &sl->reg_num, sizeof(rn));
|
memcpy(&rn, &sl->reg_num, sizeof(rn));
|
||||||
@ -126,73 +297,26 @@ static int w1_get_slaves(struct w1_master *dev,
|
|||||||
}
|
}
|
||||||
mutex_unlock(&dev->list_mutex);
|
mutex_unlock(&dev->list_mutex);
|
||||||
} else {
|
} else {
|
||||||
w1_search_process_cb(dev, cmd->cmd == W1_CMD_ALARM_SEARCH ?
|
w1_search_process_cb(dev, req_cmd->cmd == W1_CMD_ALARM_SEARCH ?
|
||||||
W1_ALARM_SEARCH : W1_SEARCH, w1_found_send_slave);
|
W1_ALARM_SEARCH : W1_SEARCH, w1_found_send_slave);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg->ack = 0;
|
|
||||||
cn_netlink_send(msg, dev->portid, 0, GFP_KERNEL);
|
|
||||||
|
|
||||||
dev->priv = NULL;
|
|
||||||
dev->priv_size = 0;
|
|
||||||
|
|
||||||
kfree(msg);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int w1_send_read_reply(struct cn_msg *msg, struct w1_netlink_msg *hdr,
|
static int w1_process_command_io(struct w1_master *dev,
|
||||||
struct w1_netlink_cmd *cmd, u32 portid)
|
struct w1_netlink_cmd *cmd)
|
||||||
{
|
|
||||||
void *data;
|
|
||||||
struct w1_netlink_msg *h;
|
|
||||||
struct w1_netlink_cmd *c;
|
|
||||||
struct cn_msg *cm;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
data = kzalloc(sizeof(struct cn_msg) +
|
|
||||||
sizeof(struct w1_netlink_msg) +
|
|
||||||
sizeof(struct w1_netlink_cmd) +
|
|
||||||
cmd->len, GFP_KERNEL);
|
|
||||||
if (!data)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
cm = (struct cn_msg *)(data);
|
|
||||||
h = (struct w1_netlink_msg *)(cm + 1);
|
|
||||||
c = (struct w1_netlink_cmd *)(h + 1);
|
|
||||||
|
|
||||||
memcpy(cm, msg, sizeof(struct cn_msg));
|
|
||||||
memcpy(h, hdr, sizeof(struct w1_netlink_msg));
|
|
||||||
memcpy(c, cmd, sizeof(struct w1_netlink_cmd));
|
|
||||||
|
|
||||||
cm->ack = msg->seq+1;
|
|
||||||
cm->len = sizeof(struct w1_netlink_msg) +
|
|
||||||
sizeof(struct w1_netlink_cmd) + cmd->len;
|
|
||||||
|
|
||||||
h->len = sizeof(struct w1_netlink_cmd) + cmd->len;
|
|
||||||
|
|
||||||
memcpy(c->data, cmd->data, c->len);
|
|
||||||
|
|
||||||
err = cn_netlink_send(cm, portid, 0, GFP_KERNEL);
|
|
||||||
|
|
||||||
kfree(data);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int w1_process_command_io(struct w1_master *dev, struct cn_msg *msg,
|
|
||||||
struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd)
|
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
switch (cmd->cmd) {
|
switch (cmd->cmd) {
|
||||||
case W1_CMD_TOUCH:
|
case W1_CMD_TOUCH:
|
||||||
w1_touch_block(dev, cmd->data, cmd->len);
|
w1_touch_block(dev, cmd->data, cmd->len);
|
||||||
w1_send_read_reply(msg, hdr, cmd, dev->portid);
|
w1_netlink_queue_cmd(dev->priv, cmd);
|
||||||
break;
|
break;
|
||||||
case W1_CMD_READ:
|
case W1_CMD_READ:
|
||||||
w1_read_block(dev, cmd->data, cmd->len);
|
w1_read_block(dev, cmd->data, cmd->len);
|
||||||
w1_send_read_reply(msg, hdr, cmd, dev->portid);
|
w1_netlink_queue_cmd(dev->priv, cmd);
|
||||||
break;
|
break;
|
||||||
case W1_CMD_WRITE:
|
case W1_CMD_WRITE:
|
||||||
w1_write_block(dev, cmd->data, cmd->len);
|
w1_write_block(dev, cmd->data, cmd->len);
|
||||||
@ -206,14 +330,13 @@ static int w1_process_command_io(struct w1_master *dev, struct cn_msg *msg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int w1_process_command_addremove(struct w1_master *dev,
|
static int w1_process_command_addremove(struct w1_master *dev,
|
||||||
struct cn_msg *msg, struct w1_netlink_msg *hdr,
|
|
||||||
struct w1_netlink_cmd *cmd)
|
struct w1_netlink_cmd *cmd)
|
||||||
{
|
{
|
||||||
struct w1_slave *sl;
|
struct w1_slave *sl;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
struct w1_reg_num *id;
|
struct w1_reg_num *id;
|
||||||
|
|
||||||
if (cmd->len != 8)
|
if (cmd->len != sizeof(*id))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
id = (struct w1_reg_num *)cmd->data;
|
id = (struct w1_reg_num *)cmd->data;
|
||||||
@ -241,7 +364,6 @@ static int w1_process_command_addremove(struct w1_master *dev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int w1_process_command_master(struct w1_master *dev,
|
static int w1_process_command_master(struct w1_master *dev,
|
||||||
struct cn_msg *req_msg, struct w1_netlink_msg *req_hdr,
|
|
||||||
struct w1_netlink_cmd *req_cmd)
|
struct w1_netlink_cmd *req_cmd)
|
||||||
{
|
{
|
||||||
int err = -EINVAL;
|
int err = -EINVAL;
|
||||||
@ -254,13 +376,13 @@ static int w1_process_command_master(struct w1_master *dev,
|
|||||||
case W1_CMD_ALARM_SEARCH:
|
case W1_CMD_ALARM_SEARCH:
|
||||||
case W1_CMD_LIST_SLAVES:
|
case W1_CMD_LIST_SLAVES:
|
||||||
mutex_unlock(&dev->bus_mutex);
|
mutex_unlock(&dev->bus_mutex);
|
||||||
err = w1_get_slaves(dev, req_msg, req_hdr, req_cmd);
|
err = w1_get_slaves(dev, req_cmd);
|
||||||
mutex_lock(&dev->bus_mutex);
|
mutex_lock(&dev->bus_mutex);
|
||||||
break;
|
break;
|
||||||
case W1_CMD_READ:
|
case W1_CMD_READ:
|
||||||
case W1_CMD_WRITE:
|
case W1_CMD_WRITE:
|
||||||
case W1_CMD_TOUCH:
|
case W1_CMD_TOUCH:
|
||||||
err = w1_process_command_io(dev, req_msg, req_hdr, req_cmd);
|
err = w1_process_command_io(dev, req_cmd);
|
||||||
break;
|
break;
|
||||||
case W1_CMD_RESET:
|
case W1_CMD_RESET:
|
||||||
err = w1_reset_bus(dev);
|
err = w1_reset_bus(dev);
|
||||||
@ -269,8 +391,7 @@ static int w1_process_command_master(struct w1_master *dev,
|
|||||||
case W1_CMD_SLAVE_REMOVE:
|
case W1_CMD_SLAVE_REMOVE:
|
||||||
mutex_unlock(&dev->bus_mutex);
|
mutex_unlock(&dev->bus_mutex);
|
||||||
mutex_lock(&dev->mutex);
|
mutex_lock(&dev->mutex);
|
||||||
err = w1_process_command_addremove(dev, req_msg, req_hdr,
|
err = w1_process_command_addremove(dev, req_cmd);
|
||||||
req_cmd);
|
|
||||||
mutex_unlock(&dev->mutex);
|
mutex_unlock(&dev->mutex);
|
||||||
mutex_lock(&dev->bus_mutex);
|
mutex_lock(&dev->bus_mutex);
|
||||||
break;
|
break;
|
||||||
@ -282,22 +403,21 @@ static int w1_process_command_master(struct w1_master *dev,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int w1_process_command_slave(struct w1_slave *sl, struct cn_msg *msg,
|
static int w1_process_command_slave(struct w1_slave *sl,
|
||||||
struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd)
|
struct w1_netlink_cmd *cmd)
|
||||||
{
|
{
|
||||||
dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.\n",
|
dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.\n",
|
||||||
__func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id,
|
__func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id,
|
||||||
sl->reg_num.crc, cmd->cmd, cmd->len);
|
sl->reg_num.crc, cmd->cmd, cmd->len);
|
||||||
|
|
||||||
return w1_process_command_io(sl->master, msg, hdr, cmd);
|
return w1_process_command_io(sl->master, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int w1_process_command_root(struct cn_msg *msg,
|
static int w1_process_command_root(struct cn_msg *req_cn, u32 portid)
|
||||||
struct w1_netlink_msg *mcmd, u32 portid)
|
|
||||||
{
|
{
|
||||||
struct w1_master *m;
|
struct w1_master *dev;
|
||||||
struct cn_msg *cn;
|
struct cn_msg *cn;
|
||||||
struct w1_netlink_msg *w;
|
struct w1_netlink_msg *msg;
|
||||||
u32 *id;
|
u32 *id;
|
||||||
|
|
||||||
cn = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
cn = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||||
@ -307,32 +427,30 @@ static int w1_process_command_root(struct cn_msg *msg,
|
|||||||
cn->id.idx = CN_W1_IDX;
|
cn->id.idx = CN_W1_IDX;
|
||||||
cn->id.val = CN_W1_VAL;
|
cn->id.val = CN_W1_VAL;
|
||||||
|
|
||||||
cn->seq = msg->seq;
|
cn->seq = req_cn->seq;
|
||||||
cn->ack = 1;
|
cn->ack = req_cn->seq + 1;
|
||||||
cn->len = sizeof(struct w1_netlink_msg);
|
cn->len = sizeof(struct w1_netlink_msg);
|
||||||
w = (struct w1_netlink_msg *)(cn + 1);
|
msg = (struct w1_netlink_msg *)cn->data;
|
||||||
|
|
||||||
w->type = W1_LIST_MASTERS;
|
msg->type = W1_LIST_MASTERS;
|
||||||
w->status = 0;
|
msg->status = 0;
|
||||||
w->len = 0;
|
msg->len = 0;
|
||||||
id = (u32 *)(w + 1);
|
id = (u32 *)msg->data;
|
||||||
|
|
||||||
mutex_lock(&w1_mlock);
|
mutex_lock(&w1_mlock);
|
||||||
list_for_each_entry(m, &w1_masters, w1_master_entry) {
|
list_for_each_entry(dev, &w1_masters, w1_master_entry) {
|
||||||
if (cn->len + sizeof(*id) > PAGE_SIZE - sizeof(struct cn_msg)) {
|
if (cn->len + sizeof(*id) > PAGE_SIZE - sizeof(struct cn_msg)) {
|
||||||
cn_netlink_send(cn, portid, 0, GFP_KERNEL);
|
cn_netlink_send(cn, portid, 0, GFP_KERNEL);
|
||||||
cn->ack++;
|
|
||||||
cn->len = sizeof(struct w1_netlink_msg);
|
cn->len = sizeof(struct w1_netlink_msg);
|
||||||
w->len = 0;
|
msg->len = 0;
|
||||||
id = (u32 *)(w + 1);
|
id = (u32 *)msg->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
*id = m->id;
|
*id = dev->id;
|
||||||
w->len += sizeof(*id);
|
msg->len += sizeof(*id);
|
||||||
cn->len += sizeof(*id);
|
cn->len += sizeof(*id);
|
||||||
id++;
|
id++;
|
||||||
}
|
}
|
||||||
cn->ack = 0;
|
|
||||||
cn_netlink_send(cn, portid, 0, GFP_KERNEL);
|
cn_netlink_send(cn, portid, 0, GFP_KERNEL);
|
||||||
mutex_unlock(&w1_mlock);
|
mutex_unlock(&w1_mlock);
|
||||||
|
|
||||||
@ -340,100 +458,44 @@ static int w1_process_command_root(struct cn_msg *msg,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int w1_netlink_send_error(struct cn_msg *rcmsg, struct w1_netlink_msg *rmsg,
|
|
||||||
struct w1_netlink_cmd *rcmd, int portid, int error)
|
|
||||||
{
|
|
||||||
struct cn_msg *cmsg;
|
|
||||||
struct w1_netlink_msg *msg;
|
|
||||||
struct w1_netlink_cmd *cmd;
|
|
||||||
|
|
||||||
cmsg = kzalloc(sizeof(*msg) + sizeof(*cmd) + sizeof(*cmsg), GFP_KERNEL);
|
|
||||||
if (!cmsg)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
msg = (struct w1_netlink_msg *)(cmsg + 1);
|
|
||||||
cmd = (struct w1_netlink_cmd *)(msg + 1);
|
|
||||||
|
|
||||||
memcpy(cmsg, rcmsg, sizeof(*cmsg));
|
|
||||||
cmsg->len = sizeof(*msg);
|
|
||||||
|
|
||||||
memcpy(msg, rmsg, sizeof(*msg));
|
|
||||||
msg->len = 0;
|
|
||||||
msg->status = (short)-error;
|
|
||||||
|
|
||||||
if (rcmd) {
|
|
||||||
memcpy(cmd, rcmd, sizeof(*cmd));
|
|
||||||
cmd->len = 0;
|
|
||||||
msg->len += sizeof(*cmd);
|
|
||||||
cmsg->len += sizeof(*cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
error = cn_netlink_send(cmsg, portid, 0, GFP_KERNEL);
|
|
||||||
kfree(cmsg);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bundle together a reference count, the full message, and broken out
|
|
||||||
* commands to be executed on each w1 master kthread in one memory allocation.
|
|
||||||
*/
|
|
||||||
struct w1_cb_block {
|
|
||||||
atomic_t refcnt;
|
|
||||||
u32 portid; /* Sending process port ID */
|
|
||||||
struct cn_msg msg;
|
|
||||||
/* cn_msg data */
|
|
||||||
/* one or more variable length struct w1_cb_node */
|
|
||||||
};
|
|
||||||
struct w1_cb_node {
|
|
||||||
struct w1_async_cmd async;
|
|
||||||
/* pointers within w1_cb_block and msg data */
|
|
||||||
struct w1_cb_block *block;
|
|
||||||
struct w1_netlink_msg *m;
|
|
||||||
struct w1_slave *sl;
|
|
||||||
struct w1_master *dev;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void w1_process_cb(struct w1_master *dev, struct w1_async_cmd *async_cmd)
|
static void w1_process_cb(struct w1_master *dev, struct w1_async_cmd *async_cmd)
|
||||||
{
|
{
|
||||||
struct w1_cb_node *node = container_of(async_cmd, struct w1_cb_node,
|
struct w1_cb_node *node = container_of(async_cmd, struct w1_cb_node,
|
||||||
async);
|
async);
|
||||||
u16 mlen = node->m->len;
|
u16 mlen = node->msg->len;
|
||||||
u8 *cmd_data = node->m->data;
|
u16 len;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
struct w1_slave *sl = node->sl;
|
struct w1_slave *sl = node->sl;
|
||||||
struct w1_netlink_cmd *cmd = NULL;
|
struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)node->msg->data;
|
||||||
|
|
||||||
mutex_lock(&dev->bus_mutex);
|
mutex_lock(&dev->bus_mutex);
|
||||||
dev->portid = node->block->portid;
|
dev->priv = node->block;
|
||||||
if (sl && w1_reset_select_slave(sl))
|
if (sl && w1_reset_select_slave(sl))
|
||||||
err = -ENODEV;
|
err = -ENODEV;
|
||||||
|
node->block->cur_msg = node->msg;
|
||||||
|
|
||||||
while (mlen && !err) {
|
while (mlen && !err) {
|
||||||
cmd = (struct w1_netlink_cmd *)cmd_data;
|
|
||||||
|
|
||||||
if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) {
|
if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) {
|
||||||
err = -E2BIG;
|
err = -E2BIG;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sl)
|
if (sl)
|
||||||
err = w1_process_command_slave(sl, &node->block->msg,
|
err = w1_process_command_slave(sl, cmd);
|
||||||
node->m, cmd);
|
|
||||||
else
|
else
|
||||||
err = w1_process_command_master(dev, &node->block->msg,
|
err = w1_process_command_master(dev, cmd);
|
||||||
node->m, cmd);
|
w1_netlink_check_send(node->block);
|
||||||
|
|
||||||
w1_netlink_send_error(&node->block->msg, node->m, cmd,
|
w1_netlink_queue_status(node->block, node->msg, cmd, err);
|
||||||
node->block->portid, err);
|
|
||||||
err = 0;
|
err = 0;
|
||||||
|
|
||||||
cmd_data += cmd->len + sizeof(struct w1_netlink_cmd);
|
len = sizeof(*cmd) + cmd->len;
|
||||||
mlen -= cmd->len + sizeof(struct w1_netlink_cmd);
|
cmd = (struct w1_netlink_cmd *)((u8 *)cmd + len);
|
||||||
|
mlen -= len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cmd || err)
|
if (!cmd || err)
|
||||||
w1_netlink_send_error(&node->block->msg, node->m, cmd,
|
w1_netlink_queue_status(node->block, node->msg, cmd, err);
|
||||||
node->block->portid, err);
|
|
||||||
|
|
||||||
/* ref taken in w1_search_slave or w1_search_master_id when building
|
/* ref taken in w1_search_slave or w1_search_master_id when building
|
||||||
* the block
|
* the block
|
||||||
@ -442,99 +504,186 @@ static void w1_process_cb(struct w1_master *dev, struct w1_async_cmd *async_cmd)
|
|||||||
w1_unref_slave(sl);
|
w1_unref_slave(sl);
|
||||||
else
|
else
|
||||||
atomic_dec(&dev->refcnt);
|
atomic_dec(&dev->refcnt);
|
||||||
dev->portid = 0;
|
dev->priv = NULL;
|
||||||
mutex_unlock(&dev->bus_mutex);
|
mutex_unlock(&dev->bus_mutex);
|
||||||
|
|
||||||
mutex_lock(&dev->list_mutex);
|
mutex_lock(&dev->list_mutex);
|
||||||
list_del(&async_cmd->async_entry);
|
list_del(&async_cmd->async_entry);
|
||||||
mutex_unlock(&dev->list_mutex);
|
mutex_unlock(&dev->list_mutex);
|
||||||
|
|
||||||
if (atomic_sub_return(1, &node->block->refcnt) == 0)
|
w1_unref_block(node->block);
|
||||||
kfree(node->block);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
|
static void w1_list_count_cmds(struct w1_netlink_msg *msg, int *cmd_count,
|
||||||
|
u16 *slave_len)
|
||||||
{
|
{
|
||||||
struct w1_netlink_msg *m = (struct w1_netlink_msg *)(msg + 1);
|
struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)msg->data;
|
||||||
|
u16 mlen = msg->len;
|
||||||
|
u16 len;
|
||||||
|
int slave_list = 0;
|
||||||
|
while (mlen) {
|
||||||
|
if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (cmd->cmd) {
|
||||||
|
case W1_CMD_SEARCH:
|
||||||
|
case W1_CMD_ALARM_SEARCH:
|
||||||
|
case W1_CMD_LIST_SLAVES:
|
||||||
|
++slave_list;
|
||||||
|
}
|
||||||
|
++*cmd_count;
|
||||||
|
len = sizeof(*cmd) + cmd->len;
|
||||||
|
cmd = (struct w1_netlink_cmd *)((u8 *)cmd + len);
|
||||||
|
mlen -= len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slave_list) {
|
||||||
|
struct w1_master *dev = w1_search_master_id(msg->id.mst.id);
|
||||||
|
if (dev) {
|
||||||
|
/* Bytes, and likely an overstimate, and if it isn't
|
||||||
|
* the results can still be split between packets.
|
||||||
|
*/
|
||||||
|
*slave_len += sizeof(struct w1_reg_num) * slave_list *
|
||||||
|
(dev->slave_count + dev->max_slave_count);
|
||||||
|
/* search incremented it */
|
||||||
|
atomic_dec(&dev->refcnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void w1_cn_callback(struct cn_msg *cn, struct netlink_skb_parms *nsp)
|
||||||
|
{
|
||||||
|
struct w1_netlink_msg *msg = (struct w1_netlink_msg *)(cn + 1);
|
||||||
struct w1_slave *sl;
|
struct w1_slave *sl;
|
||||||
struct w1_master *dev;
|
struct w1_master *dev;
|
||||||
u16 msg_len;
|
u16 msg_len;
|
||||||
|
u16 slave_len = 0;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
struct w1_cb_block *block = NULL;
|
struct w1_cb_block *block = NULL;
|
||||||
struct w1_cb_node *node = NULL;
|
struct w1_cb_node *node = NULL;
|
||||||
int node_count = 0;
|
int node_count = 0;
|
||||||
|
int cmd_count = 0;
|
||||||
|
|
||||||
|
/* If any unknown flag is set let the application know, that way
|
||||||
|
* applications can detect the absence of features in kernels that
|
||||||
|
* don't know about them. http://lwn.net/Articles/587527/
|
||||||
|
*/
|
||||||
|
if (cn->flags & ~(W1_CN_BUNDLE)) {
|
||||||
|
w1_netlink_send_error(cn, msg, nsp->portid, -EINVAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Count the number of master or slave commands there are to allocate
|
/* Count the number of master or slave commands there are to allocate
|
||||||
* space for one cb_node each.
|
* space for one cb_node each.
|
||||||
*/
|
*/
|
||||||
msg_len = msg->len;
|
msg_len = cn->len;
|
||||||
while (msg_len && !err) {
|
while (msg_len && !err) {
|
||||||
if (m->len + sizeof(struct w1_netlink_msg) > msg_len) {
|
if (msg->len + sizeof(struct w1_netlink_msg) > msg_len) {
|
||||||
err = -E2BIG;
|
err = -E2BIG;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m->type == W1_MASTER_CMD || m->type == W1_SLAVE_CMD)
|
/* count messages for nodes and allocate any additional space
|
||||||
|
* required for slave lists
|
||||||
|
*/
|
||||||
|
if (msg->type == W1_MASTER_CMD || msg->type == W1_SLAVE_CMD) {
|
||||||
++node_count;
|
++node_count;
|
||||||
|
w1_list_count_cmds(msg, &cmd_count, &slave_len);
|
||||||
|
}
|
||||||
|
|
||||||
msg_len -= sizeof(struct w1_netlink_msg) + m->len;
|
msg_len -= sizeof(struct w1_netlink_msg) + msg->len;
|
||||||
m = (struct w1_netlink_msg *)(((u8 *)m) +
|
msg = (struct w1_netlink_msg *)(((u8 *)msg) +
|
||||||
sizeof(struct w1_netlink_msg) + m->len);
|
sizeof(struct w1_netlink_msg) + msg->len);
|
||||||
}
|
}
|
||||||
m = (struct w1_netlink_msg *)(msg + 1);
|
msg = (struct w1_netlink_msg *)(cn + 1);
|
||||||
if (node_count) {
|
if (node_count) {
|
||||||
/* msg->len doesn't include itself */
|
int size;
|
||||||
long size = sizeof(struct w1_cb_block) + msg->len +
|
u16 reply_size = sizeof(*cn) + cn->len + slave_len;
|
||||||
node_count*sizeof(struct w1_cb_node);
|
if (cn->flags & W1_CN_BUNDLE) {
|
||||||
block = kmalloc(size, GFP_KERNEL);
|
/* bundling duplicats some of the messages */
|
||||||
|
reply_size += 2 * cmd_count * (sizeof(struct cn_msg) +
|
||||||
|
sizeof(struct w1_netlink_msg) +
|
||||||
|
sizeof(struct w1_netlink_cmd));
|
||||||
|
}
|
||||||
|
reply_size = MIN(CONNECTOR_MAX_MSG_SIZE, reply_size);
|
||||||
|
|
||||||
|
/* allocate space for the block, a copy of the original message,
|
||||||
|
* one node per cmd to point into the original message,
|
||||||
|
* space for replies which is the original message size plus
|
||||||
|
* space for any list slave data and status messages
|
||||||
|
* cn->len doesn't include itself which is part of the block
|
||||||
|
* */
|
||||||
|
size = /* block + original message */
|
||||||
|
sizeof(struct w1_cb_block) + sizeof(*cn) + cn->len +
|
||||||
|
/* space for nodes */
|
||||||
|
node_count * sizeof(struct w1_cb_node) +
|
||||||
|
/* replies */
|
||||||
|
sizeof(struct cn_msg) + reply_size;
|
||||||
|
block = kzalloc(size, GFP_KERNEL);
|
||||||
if (!block) {
|
if (!block) {
|
||||||
w1_netlink_send_error(msg, m, NULL, nsp->portid,
|
/* if the system is already out of memory,
|
||||||
-ENOMEM);
|
* (A) will this work, and (B) would it be better
|
||||||
|
* to not try?
|
||||||
|
*/
|
||||||
|
w1_netlink_send_error(cn, msg, nsp->portid, -ENOMEM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
atomic_set(&block->refcnt, 1);
|
atomic_set(&block->refcnt, 1);
|
||||||
block->portid = nsp->portid;
|
block->portid = nsp->portid;
|
||||||
memcpy(&block->msg, msg, sizeof(*msg) + msg->len);
|
memcpy(&block->request_cn, cn, sizeof(*cn) + cn->len);
|
||||||
node = (struct w1_cb_node *)((u8 *)block->msg.data + msg->len);
|
node = (struct w1_cb_node *)(block->request_cn.data + cn->len);
|
||||||
|
|
||||||
|
/* Sneeky, when not bundling, reply_size is the allocated space
|
||||||
|
* required for the reply, cn_msg isn't part of maxlen so
|
||||||
|
* it should be reply_size - sizeof(struct cn_msg), however
|
||||||
|
* when checking if there is enough space, w1_reply_make_space
|
||||||
|
* is called with the full message size including cn_msg,
|
||||||
|
* because it isn't known at that time if an additional cn_msg
|
||||||
|
* will need to be allocated. So an extra cn_msg is added
|
||||||
|
* above in "size".
|
||||||
|
*/
|
||||||
|
block->maxlen = reply_size;
|
||||||
|
block->first_cn = (struct cn_msg *)(node + node_count);
|
||||||
|
memset(block->first_cn, 0, sizeof(*block->first_cn));
|
||||||
}
|
}
|
||||||
|
|
||||||
msg_len = msg->len;
|
msg_len = cn->len;
|
||||||
while (msg_len && !err) {
|
while (msg_len && !err) {
|
||||||
|
|
||||||
dev = NULL;
|
dev = NULL;
|
||||||
sl = NULL;
|
sl = NULL;
|
||||||
|
|
||||||
if (m->len + sizeof(struct w1_netlink_msg) > msg_len) {
|
if (msg->len + sizeof(struct w1_netlink_msg) > msg_len) {
|
||||||
err = -E2BIG;
|
err = -E2BIG;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* execute on this thread, no need to process later */
|
/* execute on this thread, no need to process later */
|
||||||
if (m->type == W1_LIST_MASTERS) {
|
if (msg->type == W1_LIST_MASTERS) {
|
||||||
err = w1_process_command_root(msg, m, nsp->portid);
|
err = w1_process_command_root(cn, nsp->portid);
|
||||||
goto out_cont;
|
goto out_cont;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* All following message types require additional data,
|
/* All following message types require additional data,
|
||||||
* check here before references are taken.
|
* check here before references are taken.
|
||||||
*/
|
*/
|
||||||
if (!m->len) {
|
if (!msg->len) {
|
||||||
err = -EPROTO;
|
err = -EPROTO;
|
||||||
goto out_cont;
|
goto out_cont;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* both search calls take reference counts */
|
/* both search calls take references */
|
||||||
if (m->type == W1_MASTER_CMD) {
|
if (msg->type == W1_MASTER_CMD) {
|
||||||
dev = w1_search_master_id(m->id.mst.id);
|
dev = w1_search_master_id(msg->id.mst.id);
|
||||||
} else if (m->type == W1_SLAVE_CMD) {
|
} else if (msg->type == W1_SLAVE_CMD) {
|
||||||
sl = w1_search_slave((struct w1_reg_num *)m->id.id);
|
sl = w1_search_slave((struct w1_reg_num *)msg->id.id);
|
||||||
if (sl)
|
if (sl)
|
||||||
dev = sl->master;
|
dev = sl->master;
|
||||||
} else {
|
} else {
|
||||||
printk(KERN_NOTICE
|
printk(KERN_NOTICE
|
||||||
"%s: msg: %x.%x, wrong type: %u, len: %u.\n",
|
"%s: cn: %x.%x, wrong type: %u, len: %u.\n",
|
||||||
__func__, msg->id.idx, msg->id.val,
|
__func__, cn->id.idx, cn->id.val,
|
||||||
m->type, m->len);
|
msg->type, msg->len);
|
||||||
err = -EPROTO;
|
err = -EPROTO;
|
||||||
goto out_cont;
|
goto out_cont;
|
||||||
}
|
}
|
||||||
@ -549,8 +698,8 @@ static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
|
|||||||
atomic_inc(&block->refcnt);
|
atomic_inc(&block->refcnt);
|
||||||
node->async.cb = w1_process_cb;
|
node->async.cb = w1_process_cb;
|
||||||
node->block = block;
|
node->block = block;
|
||||||
node->m = (struct w1_netlink_msg *)((u8 *)&block->msg +
|
node->msg = (struct w1_netlink_msg *)((u8 *)&block->request_cn +
|
||||||
(size_t)((u8 *)m - (u8 *)msg));
|
(size_t)((u8 *)msg - (u8 *)cn));
|
||||||
node->sl = sl;
|
node->sl = sl;
|
||||||
node->dev = dev;
|
node->dev = dev;
|
||||||
|
|
||||||
@ -561,11 +710,15 @@ static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
|
|||||||
++node;
|
++node;
|
||||||
|
|
||||||
out_cont:
|
out_cont:
|
||||||
|
/* Can't queue because that modifies block and another
|
||||||
|
* thread could be processing the messages by now and
|
||||||
|
* there isn't a lock, send directly.
|
||||||
|
*/
|
||||||
if (err)
|
if (err)
|
||||||
w1_netlink_send_error(msg, m, NULL, nsp->portid, err);
|
w1_netlink_send_error(cn, msg, nsp->portid, err);
|
||||||
msg_len -= sizeof(struct w1_netlink_msg) + m->len;
|
msg_len -= sizeof(struct w1_netlink_msg) + msg->len;
|
||||||
m = (struct w1_netlink_msg *)(((u8 *)m) +
|
msg = (struct w1_netlink_msg *)(((u8 *)msg) +
|
||||||
sizeof(struct w1_netlink_msg) + m->len);
|
sizeof(struct w1_netlink_msg) + msg->len);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Let's allow requests for nonexisting devices.
|
* Let's allow requests for nonexisting devices.
|
||||||
@ -573,8 +726,8 @@ static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
|
|||||||
if (err == -ENODEV)
|
if (err == -ENODEV)
|
||||||
err = 0;
|
err = 0;
|
||||||
}
|
}
|
||||||
if (block && atomic_sub_return(1, &block->refcnt) == 0)
|
if (block)
|
||||||
kfree(block);
|
w1_unref_block(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
int w1_init_netlink(void)
|
int w1_init_netlink(void)
|
||||||
@ -591,7 +744,7 @@ void w1_fini_netlink(void)
|
|||||||
cn_del_callback(&w1_id);
|
cn_del_callback(&w1_id);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
|
void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *cn)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,17 @@
|
|||||||
|
|
||||||
#include "w1.h"
|
#include "w1.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum w1_cn_msg_flags - bitfield flags for struct cn_msg.flags
|
||||||
|
*
|
||||||
|
* @W1_CN_BUNDLE: Request bundling replies into fewer messagse. Be prepared
|
||||||
|
* to handle multiple struct cn_msg, struct w1_netlink_msg, and
|
||||||
|
* struct w1_netlink_cmd in one packet.
|
||||||
|
*/
|
||||||
|
enum w1_cn_msg_flags {
|
||||||
|
W1_CN_BUNDLE = 1,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enum w1_netlink_message_types - message type
|
* enum w1_netlink_message_types - message type
|
||||||
*
|
*
|
||||||
@ -49,6 +60,19 @@ enum w1_netlink_message_types {
|
|||||||
W1_LIST_MASTERS,
|
W1_LIST_MASTERS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct w1_netlink_msg - holds w1 message type, id, and result
|
||||||
|
*
|
||||||
|
* @type: one of enum w1_netlink_message_types
|
||||||
|
* @status: kernel feedback for success 0 or errno failure value
|
||||||
|
* @len: length of data following w1_netlink_msg
|
||||||
|
* @id: union holding master bus id (msg.id) and slave device id (id[8]).
|
||||||
|
* @data: start address of any following data
|
||||||
|
*
|
||||||
|
* The base message structure for w1 messages over netlink.
|
||||||
|
* The netlink connector data sequence is, struct nlmsghdr, struct cn_msg,
|
||||||
|
* then one or more struct w1_netlink_msg (each with optional data).
|
||||||
|
*/
|
||||||
struct w1_netlink_msg
|
struct w1_netlink_msg
|
||||||
{
|
{
|
||||||
__u8 type;
|
__u8 type;
|
||||||
@ -66,6 +90,7 @@ struct w1_netlink_msg
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* enum w1_commands - commands available for master or slave operations
|
* enum w1_commands - commands available for master or slave operations
|
||||||
|
*
|
||||||
* @W1_CMD_READ: read len bytes
|
* @W1_CMD_READ: read len bytes
|
||||||
* @W1_CMD_WRITE: write len bytes
|
* @W1_CMD_WRITE: write len bytes
|
||||||
* @W1_CMD_SEARCH: initiate a standard search, returns only the slave
|
* @W1_CMD_SEARCH: initiate a standard search, returns only the slave
|
||||||
@ -93,6 +118,17 @@ enum w1_commands {
|
|||||||
W1_CMD_MAX
|
W1_CMD_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct w1_netlink_cmd - holds the command and data
|
||||||
|
*
|
||||||
|
* @cmd: one of enum w1_commands
|
||||||
|
* @res: reserved
|
||||||
|
* @len: length of data following w1_netlink_cmd
|
||||||
|
* @data: start address of any following data
|
||||||
|
*
|
||||||
|
* One or more struct w1_netlink_cmd is placed starting at w1_netlink_msg.data
|
||||||
|
* each with optional data.
|
||||||
|
*/
|
||||||
struct w1_netlink_cmd
|
struct w1_netlink_cmd
|
||||||
{
|
{
|
||||||
__u8 cmd;
|
__u8 cmd;
|
||||||
|
@ -71,6 +71,7 @@ struct cn_dev {
|
|||||||
int cn_add_callback(struct cb_id *id, const char *name,
|
int cn_add_callback(struct cb_id *id, const char *name,
|
||||||
void (*callback)(struct cn_msg *, struct netlink_skb_parms *));
|
void (*callback)(struct cn_msg *, struct netlink_skb_parms *));
|
||||||
void cn_del_callback(struct cb_id *);
|
void cn_del_callback(struct cb_id *);
|
||||||
|
int cn_netlink_send_mult(struct cn_msg *msg, u16 len, u32 portid, u32 group, gfp_t gfp_mask);
|
||||||
int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 group, gfp_t gfp_mask);
|
int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 group, gfp_t gfp_mask);
|
||||||
|
|
||||||
int cn_queue_add_callback(struct cn_queue_dev *dev, const char *name,
|
int cn_queue_add_callback(struct cn_queue_dev *dev, const char *name,
|
||||||
|
@ -185,8 +185,21 @@ struct extcon_specific_cable_nb {
|
|||||||
*/
|
*/
|
||||||
extern int extcon_dev_register(struct extcon_dev *edev);
|
extern int extcon_dev_register(struct extcon_dev *edev);
|
||||||
extern void extcon_dev_unregister(struct extcon_dev *edev);
|
extern void extcon_dev_unregister(struct extcon_dev *edev);
|
||||||
|
extern int devm_extcon_dev_register(struct device *dev,
|
||||||
|
struct extcon_dev *edev);
|
||||||
|
extern void devm_extcon_dev_unregister(struct device *dev,
|
||||||
|
struct extcon_dev *edev);
|
||||||
extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
|
extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Following APIs control the memory of extcon device.
|
||||||
|
*/
|
||||||
|
extern struct extcon_dev *extcon_dev_allocate(const char **cables);
|
||||||
|
extern void extcon_dev_free(struct extcon_dev *edev);
|
||||||
|
extern struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
|
||||||
|
const char **cables);
|
||||||
|
extern void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get/set/update_state access the 32b encoded state value, which represents
|
* get/set/update_state access the 32b encoded state value, which represents
|
||||||
* states of all possible cables of the multistate port. For example, if one
|
* states of all possible cables of the multistate port. For example, if one
|
||||||
@ -254,6 +267,30 @@ static inline int extcon_dev_register(struct extcon_dev *edev)
|
|||||||
|
|
||||||
static inline void extcon_dev_unregister(struct extcon_dev *edev) { }
|
static inline void extcon_dev_unregister(struct extcon_dev *edev) { }
|
||||||
|
|
||||||
|
static inline int devm_extcon_dev_register(struct device *dev,
|
||||||
|
struct extcon_dev *edev)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void devm_extcon_dev_unregister(struct device *dev,
|
||||||
|
struct extcon_dev *edev) { }
|
||||||
|
|
||||||
|
static inline struct extcon_dev *extcon_dev_allocate(const char **cables)
|
||||||
|
{
|
||||||
|
return ERR_PTR(-ENOSYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void extcon_dev_free(struct extcon_dev *edev) { }
|
||||||
|
|
||||||
|
static inline struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
|
||||||
|
const char **cables)
|
||||||
|
{
|
||||||
|
return ERR_PTR(-ENOSYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void devm_extcon_dev_free(struct extcon_dev *edev) { }
|
||||||
|
|
||||||
static inline u32 extcon_get_state(struct extcon_dev *edev)
|
static inline u32 extcon_get_state(struct extcon_dev *edev)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -696,6 +696,8 @@ struct vmbus_channel {
|
|||||||
* preserve the earlier behavior.
|
* preserve the earlier behavior.
|
||||||
*/
|
*/
|
||||||
u32 target_vp;
|
u32 target_vp;
|
||||||
|
/* The corresponding CPUID in the guest */
|
||||||
|
u32 target_cpu;
|
||||||
/*
|
/*
|
||||||
* Support for sub-channels. For high performance devices,
|
* Support for sub-channels. For high performance devices,
|
||||||
* it will be useful to have multiple sub-channels to support
|
* it will be useful to have multiple sub-channels to support
|
||||||
@ -732,6 +734,11 @@ struct vmbus_channel {
|
|||||||
* Support per-channel state for use by vmbus drivers.
|
* Support per-channel state for use by vmbus drivers.
|
||||||
*/
|
*/
|
||||||
void *per_channel_state;
|
void *per_channel_state;
|
||||||
|
/*
|
||||||
|
* To support per-cpu lookup mapping of relid to channel,
|
||||||
|
* link up channels based on their CPU affinity.
|
||||||
|
*/
|
||||||
|
struct list_head percpu_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void set_channel_read_state(struct vmbus_channel *c, bool state)
|
static inline void set_channel_read_state(struct vmbus_channel *c, bool state)
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <linux/irqreturn.h>
|
#include <linux/irqreturn.h>
|
||||||
|
|
||||||
struct mcb_driver;
|
struct mcb_driver;
|
||||||
|
struct mcb_device;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct mcb_bus - MEN Chameleon Bus
|
* struct mcb_bus - MEN Chameleon Bus
|
||||||
@ -23,11 +24,14 @@ struct mcb_driver;
|
|||||||
* @dev: pointer to carrier device
|
* @dev: pointer to carrier device
|
||||||
* @children: the child busses
|
* @children: the child busses
|
||||||
* @bus_nr: mcb bus number
|
* @bus_nr: mcb bus number
|
||||||
|
* @get_irq: callback to get IRQ number
|
||||||
*/
|
*/
|
||||||
struct mcb_bus {
|
struct mcb_bus {
|
||||||
struct list_head children;
|
struct list_head children;
|
||||||
struct device dev;
|
struct device dev;
|
||||||
|
struct device *carrier;
|
||||||
int bus_nr;
|
int bus_nr;
|
||||||
|
int (*get_irq)(struct mcb_device *dev);
|
||||||
};
|
};
|
||||||
#define to_mcb_bus(b) container_of((b), struct mcb_bus, dev)
|
#define to_mcb_bus(b) container_of((b), struct mcb_bus, dev)
|
||||||
|
|
||||||
@ -105,7 +109,7 @@ extern void mcb_unregister_driver(struct mcb_driver *driver);
|
|||||||
module_driver(__mcb_driver, mcb_register_driver, mcb_unregister_driver);
|
module_driver(__mcb_driver, mcb_register_driver, mcb_unregister_driver);
|
||||||
extern void mcb_bus_add_devices(const struct mcb_bus *bus);
|
extern void mcb_bus_add_devices(const struct mcb_bus *bus);
|
||||||
extern int mcb_device_register(struct mcb_bus *bus, struct mcb_device *dev);
|
extern int mcb_device_register(struct mcb_bus *bus, struct mcb_device *dev);
|
||||||
extern struct mcb_bus *mcb_alloc_bus(void);
|
extern struct mcb_bus *mcb_alloc_bus(struct device *carrier);
|
||||||
extern struct mcb_bus *mcb_bus_get(struct mcb_bus *bus);
|
extern struct mcb_bus *mcb_bus_get(struct mcb_bus *bus);
|
||||||
extern void mcb_bus_put(struct mcb_bus *bus);
|
extern void mcb_bus_put(struct mcb_bus *bus);
|
||||||
extern struct mcb_device *mcb_alloc_dev(struct mcb_bus *bus);
|
extern struct mcb_device *mcb_alloc_dev(struct mcb_bus *bus);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* max14577-private.h - Common API for the Maxim 14577 internal sub chip
|
* max14577-private.h - Common API for the Maxim 14577/77836 internal sub chip
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013 Samsung Electrnoics
|
* Copyright (C) 2014 Samsung Electrnoics
|
||||||
* Chanwoo Choi <cw00.choi@samsung.com>
|
* Chanwoo Choi <cw00.choi@samsung.com>
|
||||||
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||||
*
|
*
|
||||||
@ -22,9 +22,19 @@
|
|||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
#define MAX14577_REG_INVALID (0xff)
|
#define I2C_ADDR_PMIC (0x46 >> 1)
|
||||||
|
#define I2C_ADDR_MUIC (0x4A >> 1)
|
||||||
|
#define I2C_ADDR_FG (0x6C >> 1)
|
||||||
|
|
||||||
/* Slave addr = 0x4A: Interrupt */
|
enum maxim_device_type {
|
||||||
|
MAXIM_DEVICE_TYPE_UNKNOWN = 0,
|
||||||
|
MAXIM_DEVICE_TYPE_MAX14577,
|
||||||
|
MAXIM_DEVICE_TYPE_MAX77836,
|
||||||
|
|
||||||
|
MAXIM_DEVICE_TYPE_NUM,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Slave addr = 0x4A: MUIC and Charger */
|
||||||
enum max14577_reg {
|
enum max14577_reg {
|
||||||
MAX14577_REG_DEVICEID = 0x00,
|
MAX14577_REG_DEVICEID = 0x00,
|
||||||
MAX14577_REG_INT1 = 0x01,
|
MAX14577_REG_INT1 = 0x01,
|
||||||
@ -74,20 +84,22 @@ enum max14577_muic_charger_type {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* MAX14577 interrupts */
|
/* MAX14577 interrupts */
|
||||||
#define INT1_ADC_MASK (0x1 << 0)
|
#define MAX14577_INT1_ADC_MASK BIT(0)
|
||||||
#define INT1_ADCLOW_MASK (0x1 << 1)
|
#define MAX14577_INT1_ADCLOW_MASK BIT(1)
|
||||||
#define INT1_ADCERR_MASK (0x1 << 2)
|
#define MAX14577_INT1_ADCERR_MASK BIT(2)
|
||||||
|
#define MAX77836_INT1_ADC1K_MASK BIT(3)
|
||||||
|
|
||||||
#define INT2_CHGTYP_MASK (0x1 << 0)
|
#define MAX14577_INT2_CHGTYP_MASK BIT(0)
|
||||||
#define INT2_CHGDETRUN_MASK (0x1 << 1)
|
#define MAX14577_INT2_CHGDETRUN_MASK BIT(1)
|
||||||
#define INT2_DCDTMR_MASK (0x1 << 2)
|
#define MAX14577_INT2_DCDTMR_MASK BIT(2)
|
||||||
#define INT2_DBCHG_MASK (0x1 << 3)
|
#define MAX14577_INT2_DBCHG_MASK BIT(3)
|
||||||
#define INT2_VBVOLT_MASK (0x1 << 4)
|
#define MAX14577_INT2_VBVOLT_MASK BIT(4)
|
||||||
|
#define MAX77836_INT2_VIDRM_MASK BIT(5)
|
||||||
|
|
||||||
#define INT3_EOC_MASK (0x1 << 0)
|
#define MAX14577_INT3_EOC_MASK BIT(0)
|
||||||
#define INT3_CGMBC_MASK (0x1 << 1)
|
#define MAX14577_INT3_CGMBC_MASK BIT(1)
|
||||||
#define INT3_OVP_MASK (0x1 << 2)
|
#define MAX14577_INT3_OVP_MASK BIT(2)
|
||||||
#define INT3_MBCCHGERR_MASK (0x1 << 3)
|
#define MAX14577_INT3_MBCCHGERR_MASK BIT(3)
|
||||||
|
|
||||||
/* MAX14577 DEVICE ID register */
|
/* MAX14577 DEVICE ID register */
|
||||||
#define DEVID_VENDORID_SHIFT 0
|
#define DEVID_VENDORID_SHIFT 0
|
||||||
@ -99,9 +111,11 @@ enum max14577_muic_charger_type {
|
|||||||
#define STATUS1_ADC_SHIFT 0
|
#define STATUS1_ADC_SHIFT 0
|
||||||
#define STATUS1_ADCLOW_SHIFT 5
|
#define STATUS1_ADCLOW_SHIFT 5
|
||||||
#define STATUS1_ADCERR_SHIFT 6
|
#define STATUS1_ADCERR_SHIFT 6
|
||||||
|
#define MAX77836_STATUS1_ADC1K_SHIFT 7
|
||||||
#define STATUS1_ADC_MASK (0x1f << STATUS1_ADC_SHIFT)
|
#define STATUS1_ADC_MASK (0x1f << STATUS1_ADC_SHIFT)
|
||||||
#define STATUS1_ADCLOW_MASK (0x1 << STATUS1_ADCLOW_SHIFT)
|
#define STATUS1_ADCLOW_MASK BIT(STATUS1_ADCLOW_SHIFT)
|
||||||
#define STATUS1_ADCERR_MASK (0x1 << STATUS1_ADCERR_SHIFT)
|
#define STATUS1_ADCERR_MASK BIT(STATUS1_ADCERR_SHIFT)
|
||||||
|
#define MAX77836_STATUS1_ADC1K_MASK BIT(MAX77836_STATUS1_ADC1K_SHIFT)
|
||||||
|
|
||||||
/* MAX14577 STATUS2 register */
|
/* MAX14577 STATUS2 register */
|
||||||
#define STATUS2_CHGTYP_SHIFT 0
|
#define STATUS2_CHGTYP_SHIFT 0
|
||||||
@ -109,11 +123,13 @@ enum max14577_muic_charger_type {
|
|||||||
#define STATUS2_DCDTMR_SHIFT 4
|
#define STATUS2_DCDTMR_SHIFT 4
|
||||||
#define STATUS2_DBCHG_SHIFT 5
|
#define STATUS2_DBCHG_SHIFT 5
|
||||||
#define STATUS2_VBVOLT_SHIFT 6
|
#define STATUS2_VBVOLT_SHIFT 6
|
||||||
|
#define MAX77836_STATUS2_VIDRM_SHIFT 7
|
||||||
#define STATUS2_CHGTYP_MASK (0x7 << STATUS2_CHGTYP_SHIFT)
|
#define STATUS2_CHGTYP_MASK (0x7 << STATUS2_CHGTYP_SHIFT)
|
||||||
#define STATUS2_CHGDETRUN_MASK (0x1 << STATUS2_CHGDETRUN_SHIFT)
|
#define STATUS2_CHGDETRUN_MASK BIT(STATUS2_CHGDETRUN_SHIFT)
|
||||||
#define STATUS2_DCDTMR_MASK (0x1 << STATUS2_DCDTMR_SHIFT)
|
#define STATUS2_DCDTMR_MASK BIT(STATUS2_DCDTMR_SHIFT)
|
||||||
#define STATUS2_DBCHG_MASK (0x1 << STATUS2_DBCHG_SHIFT)
|
#define STATUS2_DBCHG_MASK BIT(STATUS2_DBCHG_SHIFT)
|
||||||
#define STATUS2_VBVOLT_MASK (0x1 << STATUS2_VBVOLT_SHIFT)
|
#define STATUS2_VBVOLT_MASK BIT(STATUS2_VBVOLT_SHIFT)
|
||||||
|
#define MAX77836_STATUS2_VIDRM_MASK BIT(MAX77836_STATUS2_VIDRM_SHIFT)
|
||||||
|
|
||||||
/* MAX14577 CONTROL1 register */
|
/* MAX14577 CONTROL1 register */
|
||||||
#define COMN1SW_SHIFT 0
|
#define COMN1SW_SHIFT 0
|
||||||
@ -122,8 +138,8 @@ enum max14577_muic_charger_type {
|
|||||||
#define IDBEN_SHIFT 7
|
#define IDBEN_SHIFT 7
|
||||||
#define COMN1SW_MASK (0x7 << COMN1SW_SHIFT)
|
#define COMN1SW_MASK (0x7 << COMN1SW_SHIFT)
|
||||||
#define COMP2SW_MASK (0x7 << COMP2SW_SHIFT)
|
#define COMP2SW_MASK (0x7 << COMP2SW_SHIFT)
|
||||||
#define MICEN_MASK (0x1 << MICEN_SHIFT)
|
#define MICEN_MASK BIT(MICEN_SHIFT)
|
||||||
#define IDBEN_MASK (0x1 << IDBEN_SHIFT)
|
#define IDBEN_MASK BIT(IDBEN_SHIFT)
|
||||||
#define CLEAR_IDBEN_MICEN_MASK (COMN1SW_MASK | COMP2SW_MASK)
|
#define CLEAR_IDBEN_MICEN_MASK (COMN1SW_MASK | COMP2SW_MASK)
|
||||||
#define CTRL1_SW_USB ((1 << COMP2SW_SHIFT) \
|
#define CTRL1_SW_USB ((1 << COMP2SW_SHIFT) \
|
||||||
| (1 << COMN1SW_SHIFT))
|
| (1 << COMN1SW_SHIFT))
|
||||||
@ -143,14 +159,14 @@ enum max14577_muic_charger_type {
|
|||||||
#define CTRL2_ACCDET_SHIFT (5)
|
#define CTRL2_ACCDET_SHIFT (5)
|
||||||
#define CTRL2_USBCPINT_SHIFT (6)
|
#define CTRL2_USBCPINT_SHIFT (6)
|
||||||
#define CTRL2_RCPS_SHIFT (7)
|
#define CTRL2_RCPS_SHIFT (7)
|
||||||
#define CTRL2_LOWPWR_MASK (0x1 << CTRL2_LOWPWR_SHIFT)
|
#define CTRL2_LOWPWR_MASK BIT(CTRL2_LOWPWR_SHIFT)
|
||||||
#define CTRL2_ADCEN_MASK (0x1 << CTRL2_ADCEN_SHIFT)
|
#define CTRL2_ADCEN_MASK BIT(CTRL2_ADCEN_SHIFT)
|
||||||
#define CTRL2_CPEN_MASK (0x1 << CTRL2_CPEN_SHIFT)
|
#define CTRL2_CPEN_MASK BIT(CTRL2_CPEN_SHIFT)
|
||||||
#define CTRL2_SFOUTASRT_MASK (0x1 << CTRL2_SFOUTASRT_SHIFT)
|
#define CTRL2_SFOUTASRT_MASK BIT(CTRL2_SFOUTASRT_SHIFT)
|
||||||
#define CTRL2_SFOUTORD_MASK (0x1 << CTRL2_SFOUTORD_SHIFT)
|
#define CTRL2_SFOUTORD_MASK BIT(CTRL2_SFOUTORD_SHIFT)
|
||||||
#define CTRL2_ACCDET_MASK (0x1 << CTRL2_ACCDET_SHIFT)
|
#define CTRL2_ACCDET_MASK BIT(CTRL2_ACCDET_SHIFT)
|
||||||
#define CTRL2_USBCPINT_MASK (0x1 << CTRL2_USBCPINT_SHIFT)
|
#define CTRL2_USBCPINT_MASK BIT(CTRL2_USBCPINT_SHIFT)
|
||||||
#define CTRL2_RCPS_MASK (0x1 << CTR2_RCPS_SHIFT)
|
#define CTRL2_RCPS_MASK BIT(CTRL2_RCPS_SHIFT)
|
||||||
|
|
||||||
#define CTRL2_CPEN1_LOWPWR0 ((1 << CTRL2_CPEN_SHIFT) | \
|
#define CTRL2_CPEN1_LOWPWR0 ((1 << CTRL2_CPEN_SHIFT) | \
|
||||||
(0 << CTRL2_LOWPWR_SHIFT))
|
(0 << CTRL2_LOWPWR_SHIFT))
|
||||||
@ -198,14 +214,14 @@ enum max14577_charger_reg {
|
|||||||
#define CDETCTRL1_DBEXIT_SHIFT 5
|
#define CDETCTRL1_DBEXIT_SHIFT 5
|
||||||
#define CDETCTRL1_DBIDLE_SHIFT 6
|
#define CDETCTRL1_DBIDLE_SHIFT 6
|
||||||
#define CDETCTRL1_CDPDET_SHIFT 7
|
#define CDETCTRL1_CDPDET_SHIFT 7
|
||||||
#define CDETCTRL1_CHGDETEN_MASK (0x1 << CDETCTRL1_CHGDETEN_SHIFT)
|
#define CDETCTRL1_CHGDETEN_MASK BIT(CDETCTRL1_CHGDETEN_SHIFT)
|
||||||
#define CDETCTRL1_CHGTYPMAN_MASK (0x1 << CDETCTRL1_CHGTYPMAN_SHIFT)
|
#define CDETCTRL1_CHGTYPMAN_MASK BIT(CDETCTRL1_CHGTYPMAN_SHIFT)
|
||||||
#define CDETCTRL1_DCDEN_MASK (0x1 << CDETCTRL1_DCDEN_SHIFT)
|
#define CDETCTRL1_DCDEN_MASK BIT(CDETCTRL1_DCDEN_SHIFT)
|
||||||
#define CDETCTRL1_DCD2SCT_MASK (0x1 << CDETCTRL1_DCD2SCT_SHIFT)
|
#define CDETCTRL1_DCD2SCT_MASK BIT(CDETCTRL1_DCD2SCT_SHIFT)
|
||||||
#define CDETCTRL1_DCHKTM_MASK (0x1 << CDETCTRL1_DCHKTM_SHIFT)
|
#define CDETCTRL1_DCHKTM_MASK BIT(CDETCTRL1_DCHKTM_SHIFT)
|
||||||
#define CDETCTRL1_DBEXIT_MASK (0x1 << CDETCTRL1_DBEXIT_SHIFT)
|
#define CDETCTRL1_DBEXIT_MASK BIT(CDETCTRL1_DBEXIT_SHIFT)
|
||||||
#define CDETCTRL1_DBIDLE_MASK (0x1 << CDETCTRL1_DBIDLE_SHIFT)
|
#define CDETCTRL1_DBIDLE_MASK BIT(CDETCTRL1_DBIDLE_SHIFT)
|
||||||
#define CDETCTRL1_CDPDET_MASK (0x1 << CDETCTRL1_CDPDET_SHIFT)
|
#define CDETCTRL1_CDPDET_MASK BIT(CDETCTRL1_CDPDET_SHIFT)
|
||||||
|
|
||||||
/* MAX14577 CHGCTRL1 register */
|
/* MAX14577 CHGCTRL1 register */
|
||||||
#define CHGCTRL1_TCHW_SHIFT 4
|
#define CHGCTRL1_TCHW_SHIFT 4
|
||||||
@ -213,9 +229,9 @@ enum max14577_charger_reg {
|
|||||||
|
|
||||||
/* MAX14577 CHGCTRL2 register */
|
/* MAX14577 CHGCTRL2 register */
|
||||||
#define CHGCTRL2_MBCHOSTEN_SHIFT 6
|
#define CHGCTRL2_MBCHOSTEN_SHIFT 6
|
||||||
#define CHGCTRL2_MBCHOSTEN_MASK (0x1 << CHGCTRL2_MBCHOSTEN_SHIFT)
|
#define CHGCTRL2_MBCHOSTEN_MASK BIT(CHGCTRL2_MBCHOSTEN_SHIFT)
|
||||||
#define CHGCTRL2_VCHGR_RC_SHIFT 7
|
#define CHGCTRL2_VCHGR_RC_SHIFT 7
|
||||||
#define CHGCTRL2_VCHGR_RC_MASK (0x1 << CHGCTRL2_VCHGR_RC_SHIFT)
|
#define CHGCTRL2_VCHGR_RC_MASK BIT(CHGCTRL2_VCHGR_RC_SHIFT)
|
||||||
|
|
||||||
/* MAX14577 CHGCTRL3 register */
|
/* MAX14577 CHGCTRL3 register */
|
||||||
#define CHGCTRL3_MBCCVWRC_SHIFT 0
|
#define CHGCTRL3_MBCCVWRC_SHIFT 0
|
||||||
@ -225,7 +241,7 @@ enum max14577_charger_reg {
|
|||||||
#define CHGCTRL4_MBCICHWRCH_SHIFT 0
|
#define CHGCTRL4_MBCICHWRCH_SHIFT 0
|
||||||
#define CHGCTRL4_MBCICHWRCH_MASK (0xf << CHGCTRL4_MBCICHWRCH_SHIFT)
|
#define CHGCTRL4_MBCICHWRCH_MASK (0xf << CHGCTRL4_MBCICHWRCH_SHIFT)
|
||||||
#define CHGCTRL4_MBCICHWRCL_SHIFT 4
|
#define CHGCTRL4_MBCICHWRCL_SHIFT 4
|
||||||
#define CHGCTRL4_MBCICHWRCL_MASK (0x1 << CHGCTRL4_MBCICHWRCL_SHIFT)
|
#define CHGCTRL4_MBCICHWRCL_MASK BIT(CHGCTRL4_MBCICHWRCL_SHIFT)
|
||||||
|
|
||||||
/* MAX14577 CHGCTRL5 register */
|
/* MAX14577 CHGCTRL5 register */
|
||||||
#define CHGCTRL5_EOCS_SHIFT 0
|
#define CHGCTRL5_EOCS_SHIFT 0
|
||||||
@ -233,7 +249,7 @@ enum max14577_charger_reg {
|
|||||||
|
|
||||||
/* MAX14577 CHGCTRL6 register */
|
/* MAX14577 CHGCTRL6 register */
|
||||||
#define CHGCTRL6_AUTOSTOP_SHIFT 5
|
#define CHGCTRL6_AUTOSTOP_SHIFT 5
|
||||||
#define CHGCTRL6_AUTOSTOP_MASK (0x1 << CHGCTRL6_AUTOSTOP_SHIFT)
|
#define CHGCTRL6_AUTOSTOP_MASK BIT(CHGCTRL6_AUTOSTOP_SHIFT)
|
||||||
|
|
||||||
/* MAX14577 CHGCTRL7 register */
|
/* MAX14577 CHGCTRL7 register */
|
||||||
#define CHGCTRL7_OTPCGHCVS_SHIFT 0
|
#define CHGCTRL7_OTPCGHCVS_SHIFT 0
|
||||||
@ -245,14 +261,111 @@ enum max14577_charger_reg {
|
|||||||
#define MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP 50000
|
#define MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP 50000
|
||||||
#define MAX14577_REGULATOR_CURRENT_LIMIT_MAX 950000
|
#define MAX14577_REGULATOR_CURRENT_LIMIT_MAX 950000
|
||||||
|
|
||||||
|
/* MAX77836 regulator current limits (as in CHGCTRL4 register), uA */
|
||||||
|
#define MAX77836_REGULATOR_CURRENT_LIMIT_MIN 45000
|
||||||
|
#define MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START 100000
|
||||||
|
#define MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP 25000
|
||||||
|
#define MAX77836_REGULATOR_CURRENT_LIMIT_MAX 475000
|
||||||
|
|
||||||
/* MAX14577 regulator SFOUT LDO voltage, fixed, uV */
|
/* MAX14577 regulator SFOUT LDO voltage, fixed, uV */
|
||||||
#define MAX14577_REGULATOR_SAFEOUT_VOLTAGE 4900000
|
#define MAX14577_REGULATOR_SAFEOUT_VOLTAGE 4900000
|
||||||
|
|
||||||
|
/* MAX77836 regulator LDOx voltage, uV */
|
||||||
|
#define MAX77836_REGULATOR_LDO_VOLTAGE_MIN 800000
|
||||||
|
#define MAX77836_REGULATOR_LDO_VOLTAGE_MAX 3950000
|
||||||
|
#define MAX77836_REGULATOR_LDO_VOLTAGE_STEP 50000
|
||||||
|
#define MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM 64
|
||||||
|
|
||||||
|
/* Slave addr = 0x46: PMIC */
|
||||||
|
enum max77836_pmic_reg {
|
||||||
|
MAX77836_PMIC_REG_PMIC_ID = 0x20,
|
||||||
|
MAX77836_PMIC_REG_PMIC_REV = 0x21,
|
||||||
|
MAX77836_PMIC_REG_INTSRC = 0x22,
|
||||||
|
MAX77836_PMIC_REG_INTSRC_MASK = 0x23,
|
||||||
|
MAX77836_PMIC_REG_TOPSYS_INT = 0x24,
|
||||||
|
MAX77836_PMIC_REG_TOPSYS_INT_MASK = 0x26,
|
||||||
|
MAX77836_PMIC_REG_TOPSYS_STAT = 0x28,
|
||||||
|
MAX77836_PMIC_REG_MRSTB_CNTL = 0x2A,
|
||||||
|
MAX77836_PMIC_REG_LSCNFG = 0x2B,
|
||||||
|
|
||||||
|
MAX77836_LDO_REG_CNFG1_LDO1 = 0x51,
|
||||||
|
MAX77836_LDO_REG_CNFG2_LDO1 = 0x52,
|
||||||
|
MAX77836_LDO_REG_CNFG1_LDO2 = 0x53,
|
||||||
|
MAX77836_LDO_REG_CNFG2_LDO2 = 0x54,
|
||||||
|
MAX77836_LDO_REG_CNFG_LDO_BIAS = 0x55,
|
||||||
|
|
||||||
|
MAX77836_COMP_REG_COMP1 = 0x60,
|
||||||
|
|
||||||
|
MAX77836_PMIC_REG_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX77836_INTSRC_MASK_TOP_INT_SHIFT 1
|
||||||
|
#define MAX77836_INTSRC_MASK_MUIC_CHG_INT_SHIFT 3
|
||||||
|
#define MAX77836_INTSRC_MASK_TOP_INT_MASK BIT(MAX77836_INTSRC_MASK_TOP_INT_SHIFT)
|
||||||
|
#define MAX77836_INTSRC_MASK_MUIC_CHG_INT_MASK BIT(MAX77836_INTSRC_MASK_MUIC_CHG_INT_SHIFT)
|
||||||
|
|
||||||
|
/* MAX77836 PMIC interrupts */
|
||||||
|
#define MAX77836_TOPSYS_INT_T120C_SHIFT 0
|
||||||
|
#define MAX77836_TOPSYS_INT_T140C_SHIFT 1
|
||||||
|
#define MAX77836_TOPSYS_INT_T120C_MASK BIT(MAX77836_TOPSYS_INT_T120C_SHIFT)
|
||||||
|
#define MAX77836_TOPSYS_INT_T140C_MASK BIT(MAX77836_TOPSYS_INT_T140C_SHIFT)
|
||||||
|
|
||||||
|
/* LDO1/LDO2 CONFIG1 register */
|
||||||
|
#define MAX77836_CNFG1_LDO_PWRMD_SHIFT 6
|
||||||
|
#define MAX77836_CNFG1_LDO_TV_SHIFT 0
|
||||||
|
#define MAX77836_CNFG1_LDO_PWRMD_MASK (0x3 << MAX77836_CNFG1_LDO_PWRMD_SHIFT)
|
||||||
|
#define MAX77836_CNFG1_LDO_TV_MASK (0x3f << MAX77836_CNFG1_LDO_TV_SHIFT)
|
||||||
|
|
||||||
|
/* LDO1/LDO2 CONFIG2 register */
|
||||||
|
#define MAX77836_CNFG2_LDO_OVCLMPEN_SHIFT 7
|
||||||
|
#define MAX77836_CNFG2_LDO_ALPMEN_SHIFT 6
|
||||||
|
#define MAX77836_CNFG2_LDO_COMP_SHIFT 4
|
||||||
|
#define MAX77836_CNFG2_LDO_POK_SHIFT 3
|
||||||
|
#define MAX77836_CNFG2_LDO_ADE_SHIFT 1
|
||||||
|
#define MAX77836_CNFG2_LDO_SS_SHIFT 0
|
||||||
|
#define MAX77836_CNFG2_LDO_OVCLMPEN_MASK BIT(MAX77836_CNFG2_LDO_OVCLMPEN_SHIFT)
|
||||||
|
#define MAX77836_CNFG2_LDO_ALPMEN_MASK BIT(MAX77836_CNFG2_LDO_ALPMEN_SHIFT)
|
||||||
|
#define MAX77836_CNFG2_LDO_COMP_MASK (0x3 << MAX77836_CNFG2_LDO_COMP_SHIFT)
|
||||||
|
#define MAX77836_CNFG2_LDO_POK_MASK BIT(MAX77836_CNFG2_LDO_POK_SHIFT)
|
||||||
|
#define MAX77836_CNFG2_LDO_ADE_MASK BIT(MAX77836_CNFG2_LDO_ADE_SHIFT)
|
||||||
|
#define MAX77836_CNFG2_LDO_SS_MASK BIT(MAX77836_CNFG2_LDO_SS_SHIFT)
|
||||||
|
|
||||||
|
/* Slave addr = 0x6C: Fuel-Gauge/Battery */
|
||||||
|
enum max77836_fg_reg {
|
||||||
|
MAX77836_FG_REG_VCELL_MSB = 0x02,
|
||||||
|
MAX77836_FG_REG_VCELL_LSB = 0x03,
|
||||||
|
MAX77836_FG_REG_SOC_MSB = 0x04,
|
||||||
|
MAX77836_FG_REG_SOC_LSB = 0x05,
|
||||||
|
MAX77836_FG_REG_MODE_H = 0x06,
|
||||||
|
MAX77836_FG_REG_MODE_L = 0x07,
|
||||||
|
MAX77836_FG_REG_VERSION_MSB = 0x08,
|
||||||
|
MAX77836_FG_REG_VERSION_LSB = 0x09,
|
||||||
|
MAX77836_FG_REG_HIBRT_H = 0x0A,
|
||||||
|
MAX77836_FG_REG_HIBRT_L = 0x0B,
|
||||||
|
MAX77836_FG_REG_CONFIG_H = 0x0C,
|
||||||
|
MAX77836_FG_REG_CONFIG_L = 0x0D,
|
||||||
|
MAX77836_FG_REG_VALRT_MIN = 0x14,
|
||||||
|
MAX77836_FG_REG_VALRT_MAX = 0x15,
|
||||||
|
MAX77836_FG_REG_CRATE_MSB = 0x16,
|
||||||
|
MAX77836_FG_REG_CRATE_LSB = 0x17,
|
||||||
|
MAX77836_FG_REG_VRESET = 0x18,
|
||||||
|
MAX77836_FG_REG_FGID = 0x19,
|
||||||
|
MAX77836_FG_REG_STATUS_H = 0x1A,
|
||||||
|
MAX77836_FG_REG_STATUS_L = 0x1B,
|
||||||
|
/*
|
||||||
|
* TODO: TABLE registers
|
||||||
|
* TODO: CMD register
|
||||||
|
*/
|
||||||
|
|
||||||
|
MAX77836_FG_REG_END,
|
||||||
|
};
|
||||||
|
|
||||||
enum max14577_irq {
|
enum max14577_irq {
|
||||||
/* INT1 */
|
/* INT1 */
|
||||||
MAX14577_IRQ_INT1_ADC,
|
MAX14577_IRQ_INT1_ADC,
|
||||||
MAX14577_IRQ_INT1_ADCLOW,
|
MAX14577_IRQ_INT1_ADCLOW,
|
||||||
MAX14577_IRQ_INT1_ADCERR,
|
MAX14577_IRQ_INT1_ADCERR,
|
||||||
|
MAX77836_IRQ_INT1_ADC1K,
|
||||||
|
|
||||||
/* INT2 */
|
/* INT2 */
|
||||||
MAX14577_IRQ_INT2_CHGTYP,
|
MAX14577_IRQ_INT2_CHGTYP,
|
||||||
@ -260,6 +373,7 @@ enum max14577_irq {
|
|||||||
MAX14577_IRQ_INT2_DCDTMR,
|
MAX14577_IRQ_INT2_DCDTMR,
|
||||||
MAX14577_IRQ_INT2_DBCHG,
|
MAX14577_IRQ_INT2_DBCHG,
|
||||||
MAX14577_IRQ_INT2_VBVOLT,
|
MAX14577_IRQ_INT2_VBVOLT,
|
||||||
|
MAX77836_IRQ_INT2_VIDRM,
|
||||||
|
|
||||||
/* INT3 */
|
/* INT3 */
|
||||||
MAX14577_IRQ_INT3_EOC,
|
MAX14577_IRQ_INT3_EOC,
|
||||||
@ -267,21 +381,25 @@ enum max14577_irq {
|
|||||||
MAX14577_IRQ_INT3_OVP,
|
MAX14577_IRQ_INT3_OVP,
|
||||||
MAX14577_IRQ_INT3_MBCCHGERR,
|
MAX14577_IRQ_INT3_MBCCHGERR,
|
||||||
|
|
||||||
|
/* TOPSYS_INT, only MAX77836 */
|
||||||
|
MAX77836_IRQ_TOPSYS_T140C,
|
||||||
|
MAX77836_IRQ_TOPSYS_T120C,
|
||||||
|
|
||||||
MAX14577_IRQ_NUM,
|
MAX14577_IRQ_NUM,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct max14577 {
|
struct max14577 {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct i2c_client *i2c; /* Slave addr = 0x4A */
|
struct i2c_client *i2c; /* Slave addr = 0x4A */
|
||||||
|
struct i2c_client *i2c_pmic; /* Slave addr = 0x46 */
|
||||||
|
enum maxim_device_type dev_type;
|
||||||
|
|
||||||
struct regmap *regmap;
|
struct regmap *regmap; /* For MUIC and Charger */
|
||||||
|
struct regmap *regmap_pmic;
|
||||||
|
|
||||||
struct regmap_irq_chip_data *irq_data;
|
struct regmap_irq_chip_data *irq_data; /* For MUIC and Charger */
|
||||||
|
struct regmap_irq_chip_data *irq_data_pmic;
|
||||||
int irq;
|
int irq;
|
||||||
|
|
||||||
/* Device ID */
|
|
||||||
u8 vendor_id; /* Vendor Identification */
|
|
||||||
u8 device_id; /* Chip Version */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* MAX14577 shared regmap API function */
|
/* MAX14577 shared regmap API function */
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* max14577.h - Driver for the Maxim 14577
|
* max14577.h - Driver for the Maxim 14577/77836
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013 Samsung Electrnoics
|
* Copyright (C) 2014 Samsung Electrnoics
|
||||||
* Chanwoo Choi <cw00.choi@samsung.com>
|
* Chanwoo Choi <cw00.choi@samsung.com>
|
||||||
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||||
*
|
*
|
||||||
@ -20,6 +20,9 @@
|
|||||||
* MAX14577 has MUIC, Charger devices.
|
* MAX14577 has MUIC, Charger devices.
|
||||||
* The devices share the same I2C bus and interrupt line
|
* The devices share the same I2C bus and interrupt line
|
||||||
* included in this mfd driver.
|
* included in this mfd driver.
|
||||||
|
*
|
||||||
|
* MAX77836 has additional PMIC and Fuel-Gauge on different I2C slave
|
||||||
|
* addresses.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __MAX14577_H__
|
#ifndef __MAX14577_H__
|
||||||
@ -32,7 +35,17 @@ enum max14577_regulators {
|
|||||||
MAX14577_SAFEOUT = 0,
|
MAX14577_SAFEOUT = 0,
|
||||||
MAX14577_CHARGER,
|
MAX14577_CHARGER,
|
||||||
|
|
||||||
MAX14577_REG_MAX,
|
MAX14577_REGULATOR_NUM,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* MAX77836 regulator IDs */
|
||||||
|
enum max77836_regulators {
|
||||||
|
MAX77836_SAFEOUT = 0,
|
||||||
|
MAX77836_CHARGER,
|
||||||
|
MAX77836_LDO1,
|
||||||
|
MAX77836_LDO2,
|
||||||
|
|
||||||
|
MAX77836_REGULATOR_NUM,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct max14577_regulator_platform_data {
|
struct max14577_regulator_platform_data {
|
||||||
|
@ -415,7 +415,7 @@ struct palmas_usb {
|
|||||||
struct palmas *palmas;
|
struct palmas *palmas;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
|
||||||
struct extcon_dev edev;
|
struct extcon_dev *edev;
|
||||||
|
|
||||||
int id_otg_irq;
|
int id_otg_irq;
|
||||||
int id_irq;
|
int id_irq;
|
||||||
|
@ -64,7 +64,7 @@ struct miscdevice {
|
|||||||
umode_t mode;
|
umode_t mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int misc_register(struct miscdevice * misc);
|
extern int misc_register(struct miscdevice *misc);
|
||||||
extern int misc_deregister(struct miscdevice *misc);
|
extern int misc_deregister(struct miscdevice *misc);
|
||||||
|
|
||||||
#define MODULE_ALIAS_MISCDEV(minor) \
|
#define MODULE_ALIAS_MISCDEV(minor) \
|
||||||
|
Loading…
Reference in New Issue
Block a user