mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-06 05:02:31 +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:
|
||||
|
||||
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
|
||||
{
|
||||
@ -71,15 +72,21 @@ void cn_del_callback(struct cb_id *id);
|
||||
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
|
||||
softirq context, but may silently fail under strong memory pressure.
|
||||
If there are no listeners for given group -ESRCH can be returned.
|
||||
|
||||
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.
|
||||
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,
|
||||
and message will be delivered to the group which was
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
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 = <...>;
|
||||
|
||||
#address-cells = <2>;
|
||||
#size-cells <0>;
|
||||
#size-cells = <0>;
|
||||
|
||||
child@0 {
|
||||
compatible = "...";
|
||||
|
@ -82,7 +82,7 @@ driver - (standard) symlink to the w1 driver
|
||||
w1_master_add - Manually register a slave device
|
||||
w1_master_attempts - the number of times a search was attempted
|
||||
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_pullup - 5V strong pullup 0 enabled, 1 disabled
|
||||
w1_master_remove - Manually remove a slave device
|
||||
|
@ -30,7 +30,7 @@ Protocol.
|
||||
W1_SLAVE_CMD
|
||||
userspace command for slave device
|
||||
(read/write/touch)
|
||||
__u8 res - reserved
|
||||
__u8 status - error indication from kernel
|
||||
__u16 len - size of data attached to this header data
|
||||
union {
|
||||
__u8 id[8]; - slave unique device id
|
||||
@ -44,10 +44,14 @@ Protocol.
|
||||
__u8 cmd - command opcode.
|
||||
W1_CMD_READ - read 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_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
|
||||
__u16 len - length of data for this command
|
||||
For read command data must be allocated like for write command
|
||||
@ -87,8 +91,7 @@ format:
|
||||
id0 ... idN
|
||||
|
||||
Each message is at most 4k in size, so if number of master devices
|
||||
exceeds this, it will be split into several messages,
|
||||
cn.seq will be increased for each one.
|
||||
exceeds this, it will be split into several messages.
|
||||
|
||||
W1 search and alarm search commands.
|
||||
request:
|
||||
|
@ -2188,6 +2188,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
|
||||
S: Supported
|
||||
F: drivers/char/*
|
||||
F: drivers/misc/*
|
||||
F: include/linux/miscdevice.h
|
||||
|
||||
CHECKPATCH
|
||||
M: Andy Whitcroft <apw@canonical.com>
|
||||
|
@ -83,7 +83,6 @@ obj-$(CONFIG_PCCARD) += pcmcia/
|
||||
obj-$(CONFIG_DIO) += dio/
|
||||
obj-$(CONFIG_SBUS) += sbus/
|
||||
obj-$(CONFIG_ZORRO) += zorro/
|
||||
obj-$(CONFIG_MAC) += macintosh/
|
||||
obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/
|
||||
obj-$(CONFIG_PARIDE) += block/paride/
|
||||
obj-$(CONFIG_TC) += tc/
|
||||
@ -141,7 +140,6 @@ obj-y += clk/
|
||||
|
||||
obj-$(CONFIG_MAILBOX) += mailbox/
|
||||
obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
|
||||
obj-$(CONFIG_NFC) += nfc/
|
||||
obj-$(CONFIG_IOMMU_SUPPORT) += iommu/
|
||||
obj-$(CONFIG_REMOTEPROC) += remoteproc/
|
||||
obj-$(CONFIG_RPMSG) += rpmsg/
|
||||
|
@ -345,7 +345,6 @@ static int __init applicom_init(void)
|
||||
free_irq(apbs[i].irq, &dummy);
|
||||
iounmap(apbs[i].RamIO);
|
||||
}
|
||||
pci_disable_device(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,8 @@ static struct cn_dev cdev;
|
||||
static int cn_already_initialized;
|
||||
|
||||
/*
|
||||
* Sends mult (multiple) cn_msg at a time.
|
||||
*
|
||||
* msg->seq and msg->ack are used to determine message genealogy.
|
||||
* When someone sends message it puts there locally unique sequence
|
||||
* 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
|
||||
* 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
|
||||
* 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)
|
||||
{
|
||||
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))
|
||||
return -ESRCH;
|
||||
|
||||
size = sizeof(*msg) + msg->len;
|
||||
size = sizeof(*msg) + len;
|
||||
|
||||
skb = nlmsg_new(size, gfp_mask);
|
||||
if (!skb)
|
||||
@ -121,6 +126,14 @@ int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
|
||||
gfp_mask);
|
||||
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);
|
||||
|
||||
/*
|
||||
|
@ -28,13 +28,13 @@ config EXTCON_ADC_JACK
|
||||
Say Y here to enable extcon device driver based on ADC values.
|
||||
|
||||
config EXTCON_MAX14577
|
||||
tristate "MAX14577 EXTCON Support"
|
||||
tristate "MAX14577/77836 EXTCON Support"
|
||||
depends on MFD_MAX14577
|
||||
select IRQ_DOMAIN
|
||||
select REGMAP_I2C
|
||||
help
|
||||
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.
|
||||
|
||||
config EXTCON_MAX77693
|
||||
|
@ -39,7 +39,7 @@
|
||||
* @chan: iio channel being queried.
|
||||
*/
|
||||
struct adc_jack_data {
|
||||
struct extcon_dev edev;
|
||||
struct extcon_dev *edev;
|
||||
|
||||
const char **cable_names;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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) */
|
||||
|
||||
extcon_set_state(&data->edev, state);
|
||||
extcon_set_state(data->edev, state);
|
||||
}
|
||||
|
||||
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)
|
||||
return -ENOMEM;
|
||||
|
||||
data->edev.name = pdata->name;
|
||||
|
||||
if (!pdata->cable_names) {
|
||||
err = -EINVAL;
|
||||
dev_err(&pdev->dev, "error: cable_names not defined.\n");
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data->edev.dev.parent = &pdev->dev;
|
||||
data->edev.supported_cable = pdata->cable_names;
|
||||
data->edev = devm_extcon_dev_allocate(&pdev->dev, 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 */
|
||||
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) {
|
||||
err = -EINVAL;
|
||||
dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n",
|
||||
i - 1);
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
data->num_cables = i;
|
||||
|
||||
if (!pdata->adc_conditions ||
|
||||
!pdata->adc_conditions[0].state) {
|
||||
err = -EINVAL;
|
||||
dev_err(&pdev->dev, "error: adc_conditions not defined.\n");
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
data->adc_conditions = pdata->adc_conditions;
|
||||
|
||||
@ -138,10 +138,8 @@ static int adc_jack_probe(struct platform_device *pdev)
|
||||
data->num_conditions = i;
|
||||
|
||||
data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel);
|
||||
if (IS_ERR(data->chan)) {
|
||||
err = PTR_ERR(data->chan);
|
||||
goto out;
|
||||
}
|
||||
if (IS_ERR(data->chan))
|
||||
return PTR_ERR(data->chan);
|
||||
|
||||
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);
|
||||
|
||||
err = extcon_dev_register(&data->edev);
|
||||
err = devm_extcon_dev_register(&pdev->dev, data->edev);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
|
||||
data->irq = platform_get_irq(pdev, 0);
|
||||
if (!data->irq) {
|
||||
dev_err(&pdev->dev, "platform_get_irq failed\n");
|
||||
err = -ENODEV;
|
||||
goto err_irq;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
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) {
|
||||
dev_err(&pdev->dev, "error: irq %d\n", data->irq);
|
||||
goto err_irq;
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_irq:
|
||||
extcon_dev_unregister(&data->edev);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
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);
|
||||
cancel_work_sync(&data->handler.work);
|
||||
extcon_dev_unregister(&data->edev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ struct arizona_extcon_info {
|
||||
|
||||
int hpdet_ip;
|
||||
|
||||
struct extcon_dev edev;
|
||||
struct extcon_dev *edev;
|
||||
};
|
||||
|
||||
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 */
|
||||
ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL);
|
||||
ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL);
|
||||
if (ret < 0) {
|
||||
dev_err(arizona->dev, "Failed to check cable state: %d\n",
|
||||
ret);
|
||||
@ -581,7 +581,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
|
||||
else
|
||||
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)
|
||||
dev_err(arizona->dev, "Failed to report HP/line: %d\n",
|
||||
ret);
|
||||
@ -664,7 +664,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
|
||||
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
|
||||
|
||||
/* Just report headphone */
|
||||
ret = extcon_update_state(&info->edev,
|
||||
ret = extcon_update_state(info->edev,
|
||||
1 << ARIZONA_CABLE_HEADPHONE,
|
||||
1 << ARIZONA_CABLE_HEADPHONE);
|
||||
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);
|
||||
|
||||
/* Just report headphone */
|
||||
ret = extcon_update_state(&info->edev,
|
||||
ret = extcon_update_state(info->edev,
|
||||
1 << ARIZONA_CABLE_HEADPHONE,
|
||||
1 << ARIZONA_CABLE_HEADPHONE);
|
||||
if (ret != 0)
|
||||
@ -764,7 +764,7 @@ static void arizona_micd_detect(struct work_struct *work)
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
/* 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) {
|
||||
dev_err(arizona->dev, "Failed to check cable state: %d\n",
|
||||
ret);
|
||||
@ -812,7 +812,7 @@ static void arizona_micd_detect(struct work_struct *work)
|
||||
if (info->detecting && (val & ARIZONA_MICD_LVL_8)) {
|
||||
arizona_identify_headphone(info);
|
||||
|
||||
ret = extcon_update_state(&info->edev,
|
||||
ret = extcon_update_state(info->edev,
|
||||
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) {
|
||||
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);
|
||||
|
||||
if (ret != 0)
|
||||
@ -1038,7 +1038,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
||||
info->micd_ranges[i].key, 0);
|
||||
input_sync(info->input);
|
||||
|
||||
ret = extcon_update_state(&info->edev, 0xffffffff, 0);
|
||||
ret = extcon_update_state(info->edev, 0xffffffff, 0);
|
||||
if (ret != 0)
|
||||
dev_err(arizona->dev, "Removal report failed: %d\n",
|
||||
ret);
|
||||
@ -1105,15 +1105,14 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
dev_err(&pdev->dev, "Failed to allocate memory\n");
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info->micvdd = devm_regulator_get(arizona->dev, "MICVDD");
|
||||
if (IS_ERR(info->micvdd)) {
|
||||
ret = PTR_ERR(info->micvdd);
|
||||
dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret);
|
||||
goto err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_init(&info->lock);
|
||||
@ -1151,15 +1150,19 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
||||
break;
|
||||
}
|
||||
|
||||
info->edev.name = "Headset Jack";
|
||||
info->edev.dev.parent = arizona->dev;
|
||||
info->edev.supported_cable = arizona_cable;
|
||||
info->edev = devm_extcon_dev_allocate(&pdev->dev, arizona_cable);
|
||||
if (IS_ERR(info->edev)) {
|
||||
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) {
|
||||
dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
info->input = devm_input_allocate_device(&pdev->dev);
|
||||
@ -1410,8 +1413,6 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
||||
err_input:
|
||||
err_register:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
extcon_dev_unregister(&info->edev);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1445,7 +1446,6 @@ static int arizona_extcon_remove(struct platform_device *pdev)
|
||||
regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
|
||||
ARIZONA_JD1_ENA, 0);
|
||||
arizona_clk32k_disable(arizona);
|
||||
extcon_dev_unregister(&info->edev);
|
||||
|
||||
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
|
||||
* @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);
|
||||
|
||||
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
|
||||
/*
|
||||
* extcon_get_edev_by_phandle - Get the extcon device from devicetree
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include <linux/extcon/extcon-gpio.h>
|
||||
|
||||
struct gpio_extcon_data {
|
||||
struct extcon_dev edev;
|
||||
struct extcon_dev *edev;
|
||||
unsigned gpio;
|
||||
bool gpio_active_low;
|
||||
const char *state_on;
|
||||
@ -53,7 +53,7 @@ static void gpio_extcon_work(struct work_struct *work)
|
||||
state = gpio_get_value(data->gpio);
|
||||
if (data->gpio_active_low)
|
||||
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)
|
||||
@ -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)
|
||||
{
|
||||
struct gpio_extcon_data *extcon_data =
|
||||
container_of(edev, struct gpio_extcon_data, edev);
|
||||
struct device *dev = edev->dev.parent;
|
||||
struct gpio_extcon_data *extcon_data = dev_get_drvdata(dev);
|
||||
const char *state;
|
||||
|
||||
if (extcon_get_state(edev))
|
||||
state = extcon_data->state_on;
|
||||
else
|
||||
@ -98,15 +99,21 @@ static int gpio_extcon_probe(struct platform_device *pdev)
|
||||
if (!extcon_data)
|
||||
return -ENOMEM;
|
||||
|
||||
extcon_data->edev.name = pdata->name;
|
||||
extcon_data->edev.dev.parent = &pdev->dev;
|
||||
extcon_data->edev = devm_extcon_dev_allocate(&pdev->dev, NULL);
|
||||
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_active_low = pdata->gpio_active_low;
|
||||
extcon_data->state_on = pdata->state_on;
|
||||
extcon_data->state_off = pdata->state_off;
|
||||
extcon_data->check_on_resume = pdata->check_on_resume;
|
||||
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,
|
||||
pdev->name);
|
||||
@ -121,34 +128,27 @@ static int gpio_extcon_probe(struct platform_device *pdev)
|
||||
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)
|
||||
return ret;
|
||||
|
||||
INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work);
|
||||
|
||||
extcon_data->irq = gpio_to_irq(extcon_data->gpio);
|
||||
if (extcon_data->irq < 0) {
|
||||
ret = extcon_data->irq;
|
||||
goto err;
|
||||
}
|
||||
if (extcon_data->irq < 0)
|
||||
return extcon_data->irq;
|
||||
|
||||
ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler,
|
||||
pdata->irq_flags, pdev->name,
|
||||
extcon_data);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, extcon_data);
|
||||
/* Perform initial detection */
|
||||
gpio_extcon_work(&extcon_data->work.work);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
extcon_dev_unregister(&extcon_data->edev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
free_irq(extcon_data->irq, extcon_data);
|
||||
extcon_dev_unregister(&extcon_data->edev);
|
||||
|
||||
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>
|
||||
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||
*
|
||||
* 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
|
||||
@ -24,7 +25,6 @@
|
||||
#include <linux/mfd/max14577-private.h>
|
||||
#include <linux/extcon.h>
|
||||
|
||||
#define DEV_NAME "max14577-muic"
|
||||
#define DELAY_MS_DEFAULT 17000 /* unit: millisecond */
|
||||
|
||||
enum max14577_muic_adc_debounce_time {
|
||||
@ -40,6 +40,42 @@ enum max14577_muic_status {
|
||||
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 device *dev;
|
||||
struct max14577 *max14577;
|
||||
@ -48,6 +84,8 @@ struct max14577_muic_info {
|
||||
int prev_chg_type;
|
||||
u8 status[MAX14577_MUIC_STATUS_END];
|
||||
|
||||
struct max14577_muic_irq *muic_irqs;
|
||||
unsigned int muic_irqs_num;
|
||||
bool irq_adc;
|
||||
bool irq_chg;
|
||||
struct work_struct irq_work;
|
||||
@ -74,29 +112,6 @@ enum max14577_muic_cable_group {
|
||||
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 */
|
||||
enum max14577_muic_acc_type {
|
||||
MAX14577_MUIC_ADC_GROUND = 0x0,
|
||||
@ -528,21 +543,12 @@ static void max14577_muic_irq_work(struct work_struct *work)
|
||||
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) {
|
||||
case MAX14577_IRQ_INT1_ADC:
|
||||
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
|
||||
type of charger accessory */
|
||||
info->irq_adc = true;
|
||||
break;
|
||||
return 1;
|
||||
case MAX14577_IRQ_INT2_CHGTYP:
|
||||
case MAX14577_IRQ_INT2_CHGDETRUN:
|
||||
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:
|
||||
/* Handle charger accessory */
|
||||
info->irq_chg = true;
|
||||
break;
|
||||
return 1;
|
||||
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",
|
||||
irq_type);
|
||||
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);
|
||||
|
||||
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 */
|
||||
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
|
||||
struct max14577_muic_irq *muic_irq = &muic_irqs[i];
|
||||
for (i = 0; i < info->muic_irqs_num; i++) {
|
||||
struct max14577_muic_irq *muic_irq = &info->muic_irqs[i];
|
||||
unsigned int virq = 0;
|
||||
|
||||
virq = regmap_irq_get_virq(max14577->irq_data, muic_irq->irq);
|
||||
if (!virq)
|
||||
if (virq <= 0)
|
||||
return -EINVAL;
|
||||
muic_irq->virq = virq;
|
||||
|
||||
@ -668,14 +739,16 @@ static int max14577_muic_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* Initialize extcon device */
|
||||
info->edev = devm_kzalloc(&pdev->dev, sizeof(*info->edev), GFP_KERNEL);
|
||||
if (!info->edev) {
|
||||
info->edev = devm_extcon_dev_allocate(&pdev->dev,
|
||||
max14577_extcon_cable);
|
||||
if (IS_ERR(info->edev)) {
|
||||
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
info->edev->name = DEV_NAME;
|
||||
info->edev->supported_cable = max14577_extcon_cable;
|
||||
ret = extcon_dev_register(info->edev);
|
||||
|
||||
info->edev->name = dev_name(&pdev->dev);
|
||||
|
||||
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register extcon device\n");
|
||||
return ret;
|
||||
@ -694,7 +767,7 @@ static int max14577_muic_probe(struct platform_device *pdev)
|
||||
MAX14577_REG_DEVICEID, &id);
|
||||
if (ret < 0) {
|
||||
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);
|
||||
|
||||
@ -710,19 +783,10 @@ static int max14577_muic_probe(struct platform_device *pdev)
|
||||
* driver should notify cable state to upper layer.
|
||||
*/
|
||||
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);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to schedule delayed work for cable detect\n");
|
||||
goto err_extcon;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err_extcon:
|
||||
extcon_dev_unregister(info->edev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
cancel_work_sync(&info->irq_work);
|
||||
extcon_dev_unregister(info->edev);
|
||||
|
||||
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 = {
|
||||
.driver = {
|
||||
.name = DEV_NAME,
|
||||
.name = "max14577-muic",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = max14577_muic_probe,
|
||||
.remove = max14577_muic_remove,
|
||||
.id_table = max14577_muic_id,
|
||||
};
|
||||
|
||||
module_platform_driver(max14577_muic_driver);
|
||||
|
||||
MODULE_DESCRIPTION("MAXIM 14577 Extcon driver");
|
||||
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
|
||||
MODULE_DESCRIPTION("Maxim 14577/77836 Extcon driver");
|
||||
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:extcon-max14577");
|
||||
|
@ -1175,25 +1175,24 @@ static int max77693_muic_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* Initialize extcon device */
|
||||
info->edev = devm_kzalloc(&pdev->dev, sizeof(struct extcon_dev),
|
||||
GFP_KERNEL);
|
||||
if (!info->edev) {
|
||||
info->edev = devm_extcon_dev_allocate(&pdev->dev,
|
||||
max77693_extcon_cable);
|
||||
if (IS_ERR(info->edev)) {
|
||||
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_irq;
|
||||
}
|
||||
info->edev->name = DEV_NAME;
|
||||
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) {
|
||||
dev_err(&pdev->dev, "failed to register extcon device\n");
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
|
||||
/* 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;
|
||||
num_init_data = pdata->muic_data->num_init_data;
|
||||
} else {
|
||||
@ -1226,7 +1225,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
|
||||
= init_data[i].data;
|
||||
}
|
||||
|
||||
if (pdata->muic_data) {
|
||||
if (pdata && pdata->muic_data) {
|
||||
struct max77693_muic_platform_data *muic_pdata
|
||||
= pdata->muic_data;
|
||||
|
||||
@ -1267,7 +1266,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
|
||||
MAX77693_MUIC_REG_ID, &id);
|
||||
if (ret < 0) {
|
||||
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);
|
||||
|
||||
@ -1283,12 +1282,11 @@ static int max77693_muic_probe(struct platform_device *pdev)
|
||||
* driver should notify cable state to upper layer.
|
||||
*/
|
||||
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;
|
||||
|
||||
err_extcon:
|
||||
extcon_dev_unregister(info->edev);
|
||||
err_irq:
|
||||
while (--i >= 0)
|
||||
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);
|
||||
cancel_work_sync(&info->irq_work);
|
||||
input_unregister_device(info->dock);
|
||||
extcon_dev_unregister(info->edev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -699,23 +699,22 @@ static int max8997_muic_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* External connector */
|
||||
info->edev = devm_kzalloc(&pdev->dev, sizeof(struct extcon_dev),
|
||||
GFP_KERNEL);
|
||||
if (!info->edev) {
|
||||
info->edev = devm_extcon_dev_allocate(&pdev->dev, max8997_extcon_cable);
|
||||
if (IS_ERR(info->edev)) {
|
||||
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_irq;
|
||||
}
|
||||
info->edev->name = DEV_NAME;
|
||||
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) {
|
||||
dev_err(&pdev->dev, "failed to register extcon device\n");
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
if (pdata->muic_pdata) {
|
||||
if (pdata && pdata->muic_pdata) {
|
||||
struct max8997_muic_platform_data *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.
|
||||
*/
|
||||
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;
|
||||
|
||||
@ -789,8 +789,6 @@ static int max8997_muic_remove(struct platform_device *pdev)
|
||||
free_irq(muic_irqs[i].virq, info);
|
||||
cancel_work_sync(&info->irq_work);
|
||||
|
||||
extcon_dev_unregister(info->edev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mfd/palmas.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 (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");
|
||||
} else {
|
||||
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)) {
|
||||
if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) {
|
||||
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");
|
||||
} else {
|
||||
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_EN_HI_CLR_ID_GND);
|
||||
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");
|
||||
} else if ((set & 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_EN_HI_CLR_ID_FLOAT);
|
||||
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");
|
||||
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) &&
|
||||
(!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) {
|
||||
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");
|
||||
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) &&
|
||||
(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
|
||||
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");
|
||||
}
|
||||
|
||||
@ -186,13 +187,20 @@ static int palmas_usb_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, palmas_usb);
|
||||
|
||||
palmas_usb->edev.supported_cable = palmas_extcon_cable;
|
||||
palmas_usb->edev.dev.parent = palmas_usb->dev;
|
||||
palmas_usb->edev.mutually_exclusive = mutually_exclusive;
|
||||
palmas_usb->edev = devm_extcon_dev_allocate(&pdev->dev,
|
||||
palmas_extcon_cable);
|
||||
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) {
|
||||
dev_err(&pdev->dev, "failed to register extcon device\n");
|
||||
kfree(palmas_usb->edev->name);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -206,7 +214,8 @@ static int palmas_usb_probe(struct platform_device *pdev)
|
||||
if (status < 0) {
|
||||
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
|
||||
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) {
|
||||
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
|
||||
palmas_usb->vbus_irq, status);
|
||||
goto fail_extcon;
|
||||
kfree(palmas_usb->edev->name);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
palmas_enable_irq(palmas_usb);
|
||||
device_set_wakeup_capable(&pdev->dev, true);
|
||||
return 0;
|
||||
|
||||
fail_extcon:
|
||||
extcon_dev_unregister(&palmas_usb->edev);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int palmas_usb_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct palmas_usb *palmas_usb = platform_get_drvdata(pdev);
|
||||
|
||||
extcon_dev_unregister(&palmas_usb->edev);
|
||||
kfree(palmas_usb->edev->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -471,18 +471,26 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)
|
||||
}
|
||||
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)
|
||||
{
|
||||
struct vmbus_channel_close_channel *msg;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
channel->state = CHANNEL_OPEN_STATE;
|
||||
channel->sc_creation_callback = NULL;
|
||||
/* Stop callback and cancel the timer asap */
|
||||
spin_lock_irqsave(&channel->inbound_lock, flags);
|
||||
channel->onchannel_callback = NULL;
|
||||
spin_unlock_irqrestore(&channel->inbound_lock, flags);
|
||||
if (channel->target_cpu != smp_processor_id())
|
||||
smp_call_function_single(channel->target_cpu, reset_channel_cb,
|
||||
channel, true);
|
||||
else
|
||||
reset_channel_cb(channel);
|
||||
|
||||
/* Send a closing message */
|
||||
|
||||
@ -674,8 +682,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
|
||||
u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset,
|
||||
multi_pagebuffer->len);
|
||||
|
||||
|
||||
if ((pfncount < 0) || (pfncount > MAX_MULTIPAGE_BUFFER_COUNT))
|
||||
if (pfncount > MAX_MULTIPAGE_BUFFER_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
|
@ -149,6 +149,7 @@ static struct vmbus_channel *alloc_channel(void)
|
||||
spin_lock_init(&channel->sc_lock);
|
||||
|
||||
INIT_LIST_HEAD(&channel->sc_list);
|
||||
INIT_LIST_HEAD(&channel->percpu_list);
|
||||
|
||||
channel->controlwq = create_workqueue("hv_vmbus_ctl");
|
||||
if (!channel->controlwq) {
|
||||
@ -188,7 +189,20 @@ static void free_channel(struct vmbus_channel *channel)
|
||||
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 -
|
||||
@ -210,6 +224,12 @@ static void vmbus_process_rescind_offer(struct work_struct *work)
|
||||
msg.header.msgtype = CHANNELMSG_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) {
|
||||
spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
|
||||
list_del(&channel->listentry);
|
||||
@ -245,6 +265,7 @@ static void vmbus_process_offer(struct work_struct *work)
|
||||
work);
|
||||
struct vmbus_channel *channel;
|
||||
bool fnew = true;
|
||||
bool enq = false;
|
||||
int ret;
|
||||
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,
|
||||
&vmbus_connection.chn_list);
|
||||
enq = true;
|
||||
}
|
||||
|
||||
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) {
|
||||
/*
|
||||
* 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);
|
||||
list_add_tail(&newchannel->sc_list, &channel->sc_list);
|
||||
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;
|
||||
if (channel->sc_creation_callback != NULL)
|
||||
channel->sc_creation_callback(newchannel);
|
||||
@ -365,7 +404,7 @@ static u32 next_vp;
|
||||
* performance critical channels (IDE, SCSI and Network) will be uniformly
|
||||
* 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;
|
||||
int i;
|
||||
@ -387,10 +426,13 @@ static u32 get_vp_index(uuid_le *type_guid)
|
||||
* Also if the channel is not a performance critical
|
||||
* channel, bind it to cpu 0.
|
||||
*/
|
||||
return 0;
|
||||
channel->target_cpu = 0;
|
||||
channel->target_vp = 0;
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
newchannel->target_vp = get_vp_index(&offer->offer.if_type);
|
||||
init_vp_index(newchannel, &offer->offer.if_type);
|
||||
|
||||
memcpy(&newchannel->offermsg, offer,
|
||||
sizeof(struct vmbus_channel_offer_channel));
|
||||
|
@ -224,8 +224,8 @@ int vmbus_connect(void)
|
||||
vmbus_connection.int_page = NULL;
|
||||
}
|
||||
|
||||
free_pages((unsigned long)vmbus_connection.monitor_pages[0], 1);
|
||||
free_pages((unsigned long)vmbus_connection.monitor_pages[1], 1);
|
||||
free_pages((unsigned long)vmbus_connection.monitor_pages[0], 0);
|
||||
free_pages((unsigned long)vmbus_connection.monitor_pages[1], 0);
|
||||
vmbus_connection.monitor_pages[0] = NULL;
|
||||
vmbus_connection.monitor_pages[1] = NULL;
|
||||
|
||||
@ -234,6 +234,28 @@ int vmbus_connect(void)
|
||||
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
|
||||
@ -277,7 +299,6 @@ struct vmbus_channel *relid2channel(u32 relid)
|
||||
static void process_chn_event(u32 relid)
|
||||
{
|
||||
struct vmbus_channel *channel;
|
||||
unsigned long flags;
|
||||
void *arg;
|
||||
bool read_state;
|
||||
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
|
||||
* channel callback to process the event
|
||||
*/
|
||||
channel = relid2channel(relid);
|
||||
channel = pcpu_relid2channel(relid);
|
||||
|
||||
if (!channel) {
|
||||
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
|
||||
* is no driver handling the device. An unloading driver
|
||||
* sets the onchannel_callback to NULL under the
|
||||
* protection of the channel inbound_lock. Thus, checking
|
||||
* and invoking the driver specific callback takes care of
|
||||
* orderly unloading of the driver.
|
||||
* sets the onchannel_callback to NULL on the same CPU
|
||||
* as where this interrupt is handled (in an interrupt context).
|
||||
* Thus, checking and invoking the driver specific callback takes
|
||||
* care of orderly unloading of the driver.
|
||||
*/
|
||||
|
||||
spin_lock_irqsave(&channel->inbound_lock, flags);
|
||||
if (channel->onchannel_callback != NULL) {
|
||||
arg = channel->channel_callback_context;
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
hv_context.vp_index[cpu] = (u32)vp_index;
|
||||
|
||||
INIT_LIST_HEAD(&hv_context.percpu_list[cpu]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
@ -459,6 +460,11 @@ static bool do_hot_add;
|
||||
*/
|
||||
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_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 void post_status(struct hv_dynmem_device *dm);
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
|
||||
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.
|
||||
*/
|
||||
wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ);
|
||||
|
||||
post_status(&dm_device);
|
||||
}
|
||||
|
||||
return;
|
||||
@ -951,11 +958,17 @@ static void post_status(struct hv_dynmem_device *dm)
|
||||
{
|
||||
struct dm_status status;
|
||||
struct sysinfo val;
|
||||
unsigned long now = jiffies;
|
||||
unsigned long last_post = last_post_time;
|
||||
|
||||
if (pressure_report_delay > 0) {
|
||||
--pressure_report_delay;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!time_after(now, (last_post_time + HZ)))
|
||||
return;
|
||||
|
||||
si_meminfo(&val);
|
||||
memset(&status, 0, sizeof(struct dm_status));
|
||||
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))
|
||||
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,
|
||||
sizeof(struct dm_status),
|
||||
(unsigned long)NULL,
|
||||
@ -1117,7 +1138,7 @@ static void balloon_up(struct work_struct *dummy)
|
||||
|
||||
if (ret == -EAGAIN)
|
||||
msleep(20);
|
||||
|
||||
post_status(&dm_device);
|
||||
} while (ret == -EAGAIN);
|
||||
|
||||
if (ret) {
|
||||
@ -1144,8 +1165,10 @@ static void balloon_down(struct hv_dynmem_device *dm,
|
||||
struct dm_unballoon_response resp;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < range_count; i++)
|
||||
for (i = 0; i < range_count; i++) {
|
||||
free_balloon_pages(dm, &range_array[i]);
|
||||
post_status(&dm_device);
|
||||
}
|
||||
|
||||
if (req->more_pages == 1)
|
||||
return;
|
||||
|
@ -510,6 +510,11 @@ struct hv_context {
|
||||
* basis.
|
||||
*/
|
||||
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;
|
||||
|
@ -183,14 +183,14 @@ EXPORT_SYMBOL_GPL(mcb_device_register);
|
||||
*
|
||||
* 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;
|
||||
int bus_nr;
|
||||
|
||||
bus = kzalloc(sizeof(struct mcb_bus), GFP_KERNEL);
|
||||
if (!bus)
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
bus_nr = ida_simple_get(&mcb_ida, 0, 0, GFP_KERNEL);
|
||||
if (bus_nr < 0) {
|
||||
@ -200,7 +200,7 @@ struct mcb_bus *mcb_alloc_bus(void)
|
||||
|
||||
INIT_LIST_HEAD(&bus->children);
|
||||
bus->bus_nr = bus_nr;
|
||||
|
||||
bus->carrier = carrier;
|
||||
return bus;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mcb_alloc_bus);
|
||||
@ -378,6 +378,13 @@ void mcb_release_mem(struct resource *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
|
||||
* @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)
|
||||
{
|
||||
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);
|
||||
|
||||
|
@ -20,6 +20,15 @@ struct priv {
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
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);
|
||||
if (ret < 0)
|
||||
|
@ -331,15 +331,15 @@ config MFD_88PM860X
|
||||
battery-charger under the corresponding menus.
|
||||
|
||||
config MFD_MAX14577
|
||||
bool "Maxim Semiconductor MAX14577 MUIC + Charger Support"
|
||||
bool "Maxim Semiconductor MAX14577/77836 MUIC + Charger Support"
|
||||
depends on I2C=y
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
select REGMAP_IRQ
|
||||
select IRQ_DOMAIN
|
||||
help
|
||||
Say yes here to add support for Maxim Semiconductor MAX14577.
|
||||
This is a Micro-USB IC with Charger controls on chip.
|
||||
Say yes here to add support for Maxim Semiconductor MAX14577 and
|
||||
MAX77836 Micro-USB ICs with battery charger.
|
||||
This driver provides common support for accessing the device;
|
||||
additional drivers must be enabled in order to use the functionality
|
||||
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>
|
||||
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||
*
|
||||
@ -21,6 +21,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/max14577.h>
|
||||
#include <linux/mfd/max14577-private.h>
|
||||
@ -37,7 +38,38 @@ static struct mfd_cell max14577_devs[] = {
|
||||
{ .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) {
|
||||
case MAX14577_REG_INT1 ... MAX14577_REG_STATUS3:
|
||||
@ -48,49 +80,221 @@ static bool max14577_volatile_reg(struct device *dev, unsigned int reg)
|
||||
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,
|
||||
.val_bits = 8,
|
||||
.volatile_reg = max14577_volatile_reg,
|
||||
.volatile_reg = max14577_muic_volatile_reg,
|
||||
.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[] = {
|
||||
/* INT1 interrupts */
|
||||
{ .reg_offset = 0, .mask = INT1_ADC_MASK, },
|
||||
{ .reg_offset = 0, .mask = INT1_ADCLOW_MASK, },
|
||||
{ .reg_offset = 0, .mask = INT1_ADCERR_MASK, },
|
||||
{ .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, },
|
||||
{ .reg_offset = 0, .mask = MAX14577_INT1_ADCLOW_MASK, },
|
||||
{ .reg_offset = 0, .mask = MAX14577_INT1_ADCERR_MASK, },
|
||||
/* INT2 interrupts */
|
||||
{ .reg_offset = 1, .mask = INT2_CHGTYP_MASK, },
|
||||
{ .reg_offset = 1, .mask = INT2_CHGDETRUN_MASK, },
|
||||
{ .reg_offset = 1, .mask = INT2_DCDTMR_MASK, },
|
||||
{ .reg_offset = 1, .mask = INT2_DBCHG_MASK, },
|
||||
{ .reg_offset = 1, .mask = INT2_VBVOLT_MASK, },
|
||||
{ .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, },
|
||||
/* INT3 interrupts */
|
||||
{ .reg_offset = 2, .mask = INT3_EOC_MASK, },
|
||||
{ .reg_offset = 2, .mask = INT3_CGMBC_MASK, },
|
||||
{ .reg_offset = 2, .mask = INT3_OVP_MASK, },
|
||||
{ .reg_offset = 2, .mask = INT3_MBCCHGERR_MASK, },
|
||||
{ .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 max14577_irq_chip = {
|
||||
.name = "max14577",
|
||||
.status_base = MAX14577_REG_INT1,
|
||||
.mask_base = MAX14577_REG_INTMASK1,
|
||||
.mask_invert = 1,
|
||||
.mask_invert = true,
|
||||
.num_regs = 3,
|
||||
.irqs = 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,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct max14577 *max14577;
|
||||
struct max14577_platform_data *pdata = dev_get_platdata(&i2c->dev);
|
||||
struct device_node *np = i2c->dev.of_node;
|
||||
u8 reg_data;
|
||||
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) {
|
||||
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->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)) {
|
||||
ret = PTR_ERR(max14577->regmap);
|
||||
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;
|
||||
}
|
||||
|
||||
ret = max14577_read_reg(max14577->regmap, MAX14577_REG_DEVICEID,
|
||||
®_data);
|
||||
if (ret) {
|
||||
dev_err(max14577->dev, "Device not found on this channel: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
if (np) {
|
||||
const struct of_device_id *of_id;
|
||||
|
||||
of_id = of_match_device(max14577_dt_match, &i2c->dev);
|
||||
if (of_id)
|
||||
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,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0,
|
||||
&max14577_irq_chip,
|
||||
irq_flags, 0, irq_chip,
|
||||
&max14577->irq_data);
|
||||
if (ret != 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(max14577->dev, -1, max14577_devs,
|
||||
ARRAY_SIZE(max14577_devs), NULL, 0,
|
||||
/* Max77836 specific initialization code (additional regmap) */
|
||||
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));
|
||||
if (ret < 0)
|
||||
goto err_mfd;
|
||||
@ -156,6 +381,9 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
|
||||
return 0;
|
||||
|
||||
err_mfd:
|
||||
if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836)
|
||||
max77836_remove(max14577);
|
||||
err_max77836:
|
||||
regmap_del_irq_chip(max14577->irq, max14577->irq_data);
|
||||
|
||||
return ret;
|
||||
@ -167,12 +395,15 @@ static int max14577_i2c_remove(struct i2c_client *i2c)
|
||||
|
||||
mfd_remove_devices(max14577->dev);
|
||||
regmap_del_irq_chip(max14577->irq, max14577->irq_data);
|
||||
if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836)
|
||||
max77836_remove(max14577);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
@ -215,11 +446,6 @@ static int max14577_resume(struct device *dev)
|
||||
}
|
||||
#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 struct i2c_driver max14577_i2c_driver = {
|
||||
@ -236,6 +462,9 @@ static struct i2c_driver max14577_i2c_driver = {
|
||||
|
||||
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);
|
||||
}
|
||||
subsys_initcall(max14577_i2c_init);
|
||||
@ -247,5 +476,5 @@ static void __exit max14577_i2c_exit(void)
|
||||
module_exit(max14577_i2c_exit);
|
||||
|
||||
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");
|
||||
|
@ -54,6 +54,7 @@ config AD525X_DPOT_SPI
|
||||
config ATMEL_PWM
|
||||
tristate "Atmel AT32/AT91 PWM support"
|
||||
depends on HAVE_CLK
|
||||
depends on AVR32 || AT91SAM9263 || AT91SAM9RL || AT91SAM9G45
|
||||
help
|
||||
This option enables device driver support for the PWM channels
|
||||
on certain Atmel processors. Pulse Width Modulation is used for
|
||||
@ -200,7 +201,7 @@ config ICS932S401
|
||||
|
||||
config ATMEL_SSC
|
||||
tristate "Device driver for Atmel SSC peripheral"
|
||||
depends on HAS_IOMEM
|
||||
depends on HAS_IOMEM && (AVR32 || ARCH_AT91 || COMPILE_TEST)
|
||||
---help---
|
||||
This option enables device driver support for Atmel Synchronized
|
||||
Serial Communication peripheral (SSC).
|
||||
@ -468,7 +469,7 @@ config BMP085_SPI
|
||||
config PCH_PHUB
|
||||
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB"
|
||||
select GENERIC_NET_UTILS
|
||||
depends on PCI
|
||||
depends on PCI && (X86_32 || COMPILE_TEST)
|
||||
help
|
||||
This driver is for PCH(Platform controller Hub) PHUB(Packet Hub) of
|
||||
Intel Topcliff which is an IOH(Input/Output Hub) for x86 embedded
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
@ -366,11 +367,17 @@ static const struct dev_pm_ops charlcd_pm_ops = {
|
||||
.resume = charlcd_resume,
|
||||
};
|
||||
|
||||
static const struct of_device_id charlcd_match[] = {
|
||||
{ .compatible = "arm,versatile-lcd", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver charlcd_driver = {
|
||||
.driver = {
|
||||
.name = DRIVERNAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &charlcd_pm_ops,
|
||||
.of_match_table = of_match_ptr(charlcd_match),
|
||||
},
|
||||
.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 i2c_client *client = to_i2c_client(dev);
|
||||
char *endp;
|
||||
u64 val;
|
||||
__le32 val_le;
|
||||
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);
|
||||
|
||||
/* Decode input */
|
||||
val = simple_strtoull(buf, &endp, 0);
|
||||
if (buf == endp) {
|
||||
rc = kstrtoull(buf, 0, &val);
|
||||
if (rc < 0) {
|
||||
dev_dbg(dev, "input string not a number\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -348,7 +348,7 @@ int genwqe_init_debugfs(struct genwqe_dev *cd)
|
||||
char name[64];
|
||||
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);
|
||||
if (!root) {
|
||||
@ -454,7 +454,7 @@ int genwqe_init_debugfs(struct genwqe_dev *cd)
|
||||
}
|
||||
|
||||
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,
|
||||
&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 rc;
|
||||
int rc = 0;
|
||||
struct pci_dev *pci_dev = cd->pci_dev;
|
||||
|
||||
if (sgl->fpage) {
|
||||
|
@ -111,8 +111,6 @@ int mei_amthif_host_init(struct mei_device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
cl->state = MEI_FILE_CONNECTING;
|
||||
|
||||
ret = mei_cl_connect(cl, NULL);
|
||||
|
||||
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;
|
||||
|
||||
if (length > dev->me_clients[id].props.max_msg_length)
|
||||
return -EINVAL;
|
||||
return -EFBIG;
|
||||
|
||||
cb = mei_io_cb_init(cl, NULL);
|
||||
if (!cb)
|
||||
@ -427,8 +427,6 @@ int mei_cl_enable_device(struct mei_cl_device *device)
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
cl->state = MEI_FILE_CONNECTING;
|
||||
|
||||
err = mei_cl_connect(cl, NULL);
|
||||
if (err < 0) {
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mei.h>
|
||||
|
||||
@ -415,6 +416,10 @@ void mei_host_client_init(struct work_struct *work)
|
||||
dev->reset_count = 0;
|
||||
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
dev_dbg(&dev->pdev->dev, "hbuf is not ready\n");
|
||||
return false;
|
||||
@ -460,9 +471,18 @@ int mei_cl_disconnect(struct mei_cl *cl)
|
||||
if (cl->state != MEI_FILE_DISCONNECTING)
|
||||
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);
|
||||
if (!cb)
|
||||
return -ENOMEM;
|
||||
if (!cb) {
|
||||
rets = -ENOMEM;
|
||||
goto free;
|
||||
}
|
||||
|
||||
cb->fop_type = MEI_FOP_CLOSE;
|
||||
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");
|
||||
|
||||
if (err)
|
||||
cl_dbg(dev, cl, "wait failed disconnect err=%08x\n",
|
||||
err);
|
||||
cl_dbg(dev, cl, "wait failed disconnect err=%d\n", err);
|
||||
|
||||
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_wr_list, cl);
|
||||
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);
|
||||
return rets;
|
||||
}
|
||||
@ -557,6 +580,13 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
|
||||
|
||||
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);
|
||||
if (!cb) {
|
||||
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 */
|
||||
if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
|
||||
cl->state = MEI_FILE_CONNECTING;
|
||||
if (mei_hbm_cl_connect_req(dev, cl)) {
|
||||
rets = -ENODEV;
|
||||
goto out;
|
||||
@ -596,6 +627,10 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
|
||||
rets = cl->status;
|
||||
|
||||
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);
|
||||
return rets;
|
||||
}
|
||||
@ -713,23 +748,31 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
|
||||
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);
|
||||
if (!cb)
|
||||
return -ENOMEM;
|
||||
if (!cb) {
|
||||
rets = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* always allocate at least client max message */
|
||||
length = max_t(size_t, length, dev->me_clients[i].props.max_msg_length);
|
||||
rets = mei_io_cb_alloc_resp_buf(cb, length);
|
||||
if (rets)
|
||||
goto err;
|
||||
goto out;
|
||||
|
||||
cb->fop_type = MEI_FOP_READ;
|
||||
if (mei_hbuf_acquire(dev)) {
|
||||
if (mei_hbm_cl_flow_control_req(dev, cl)) {
|
||||
cl_err(dev, cl, "flow control send failed\n");
|
||||
rets = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
rets = mei_hbm_cl_flow_control_req(dev, cl);
|
||||
if (rets < 0)
|
||||
goto out;
|
||||
|
||||
list_add_tail(&cb->list, &dev->read_list.list);
|
||||
} else {
|
||||
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;
|
||||
|
||||
return rets;
|
||||
err:
|
||||
mei_io_cb_free(cb);
|
||||
out:
|
||||
cl_dbg(dev, cl, "rpm: autosuspend\n");
|
||||
pm_runtime_mark_last_busy(&dev->pdev->dev);
|
||||
pm_runtime_put_autosuspend(&dev->pdev->dev);
|
||||
|
||||
if (rets)
|
||||
mei_io_cb_free(cb);
|
||||
|
||||
return rets;
|
||||
}
|
||||
|
||||
@ -776,7 +824,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
return rets;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
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->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;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -14,10 +14,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/mei.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "mei_dev.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
|
||||
*
|
||||
@ -69,9 +99,7 @@ static int mei_hbm_me_cl_allocate(struct mei_device *dev)
|
||||
struct mei_me_client *clients;
|
||||
int b;
|
||||
|
||||
dev->me_clients_num = 0;
|
||||
dev->me_client_presentation_num = 0;
|
||||
dev->me_client_index = 0;
|
||||
mei_hbm_reset(dev);
|
||||
|
||||
/* count how many ME clients we have */
|
||||
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)
|
||||
return 0;
|
||||
|
||||
kfree(dev->me_clients);
|
||||
dev->me_clients = NULL;
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%ld.\n",
|
||||
dev->me_clients_num * sizeof(struct mei_me_client));
|
||||
/* 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 ret;
|
||||
@ -289,6 +303,34 @@ static int mei_hbm_prop_req(struct mei_device *dev)
|
||||
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
|
||||
*
|
||||
@ -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);
|
||||
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:
|
||||
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_reset(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_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_connect_req(struct mei_device *dev, struct mei_cl *cl);
|
||||
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_ */
|
||||
|
||||
|
@ -133,6 +133,8 @@
|
||||
#define ME_CB_RW 8
|
||||
/* ME_CSR_HA - ME Control Status Host Access register (read only) */
|
||||
#define ME_CSR_HA 0xC
|
||||
/* H_HGC_CSR - PGI register */
|
||||
#define H_HPG_CSR 0x10
|
||||
|
||||
|
||||
/* register bits of H_CSR (Host Control Status register) */
|
||||
@ -162,6 +164,8 @@ access to ME_CBD */
|
||||
#define ME_CBWP_HRA 0x00FF0000
|
||||
/* ME CB Read Pointer HRA - host read only access to ME_CBRP */
|
||||
#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 */
|
||||
#define ME_RST_HRA 0x00000010
|
||||
/* 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 */
|
||||
#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_ */
|
||||
|
@ -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)
|
||||
{
|
||||
struct mei_me_hw *hw = to_me_hw(dev);
|
||||
u32 hcsr = mei_hcsr_read(to_me_hw(dev));
|
||||
/* Doesn't change in runtime */
|
||||
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
|
||||
*
|
||||
@ -164,6 +181,9 @@ static void mei_me_hw_reset_release(struct mei_device *dev)
|
||||
hcsr |= H_IG;
|
||||
hcsr &= ~H_RST;
|
||||
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.
|
||||
@ -183,8 +203,21 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
|
||||
else
|
||||
hcsr &= ~H_IE;
|
||||
|
||||
dev->recvd_hw_ready = false;
|
||||
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)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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)
|
||||
{
|
||||
int err;
|
||||
if (mei_me_hw_is_ready(dev))
|
||||
return 0;
|
||||
|
||||
dev->recvd_hw_ready = false;
|
||||
mutex_unlock(&dev->device_lock);
|
||||
err = wait_event_interruptible_timeout(dev->wait_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
@ -491,14 +660,13 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
|
||||
/* check if we need to start the dev */
|
||||
if (!mei_host_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->recvd_hw_ready = true;
|
||||
wake_up_interruptible(&dev->wait_hw_ready);
|
||||
} else {
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "Reset Completed.\n");
|
||||
mei_me_hw_reset_release(dev);
|
||||
dev_dbg(&dev->pdev->dev, "Spurious Interrupt\n");
|
||||
}
|
||||
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);
|
||||
|
||||
rets = mei_irq_write_handler(dev, &complete_list);
|
||||
|
||||
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
|
||||
/*
|
||||
* During PG handshake only allowed write is the replay to the
|
||||
* 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);
|
||||
|
||||
@ -535,8 +709,65 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
|
||||
mutex_unlock(&dev->device_lock);
|
||||
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 = {
|
||||
|
||||
.pg_state = mei_me_pg_state,
|
||||
|
||||
.fw_status = mei_me_fw_status,
|
||||
.host_is_ready = mei_me_host_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_start = mei_me_hw_start,
|
||||
|
||||
.pg_is_enabled = mei_me_pg_is_enabled,
|
||||
|
||||
.intr_clear = mei_me_intr_clear,
|
||||
.intr_enable = mei_me_intr_enable,
|
||||
.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
|
||||
};
|
||||
|
||||
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
|
||||
*
|
||||
* @pdev: The pci device structure
|
||||
* @cfg: per device generation config
|
||||
*
|
||||
* 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;
|
||||
|
||||
@ -575,7 +875,7 @@ struct mei_device *mei_me_dev_init(struct pci_dev *pdev)
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
mei_device_init(dev);
|
||||
mei_device_init(dev, cfg);
|
||||
|
||||
dev->ops = &mei_me_hw_ops;
|
||||
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "mei_dev.h"
|
||||
#include "client.h"
|
||||
|
||||
#define MEI_ME_RPM_TIMEOUT 500 /* ms */
|
||||
|
||||
struct mei_me_hw {
|
||||
void __iomem *mem_addr;
|
||||
/*
|
||||
@ -31,11 +33,22 @@ struct mei_me_hw {
|
||||
*/
|
||||
u32 host_hw_state;
|
||||
u32 me_hw_state;
|
||||
enum mei_pg_state pg_state;
|
||||
};
|
||||
|
||||
#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_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_OP_MODE_MSK 0x000F0000
|
||||
# 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 */
|
||||
|
||||
|
@ -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",
|
||||
hw->aliveness, 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);
|
||||
}
|
||||
return do_req;
|
||||
@ -213,6 +213,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected)
|
||||
do {
|
||||
hw->aliveness = mei_txe_aliveness_get(dev);
|
||||
if (hw->aliveness == expected) {
|
||||
dev->pg_event = MEI_PG_EVENT_IDLE;
|
||||
dev_dbg(&dev->pdev->dev,
|
||||
"aliveness settled after %d msecs\n", t);
|
||||
return t;
|
||||
@ -223,6 +224,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected)
|
||||
t += MSEC_PER_SEC / 5;
|
||||
} while (t < SEC_ALIVENESS_WAIT_TIMEOUT);
|
||||
|
||||
dev->pg_event = MEI_PG_EVENT_IDLE;
|
||||
dev_err(&dev->pdev->dev, "aliveness timed out\n");
|
||||
return -ETIME;
|
||||
}
|
||||
@ -249,19 +251,22 @@ static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected)
|
||||
return 0;
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
err = wait_event_timeout(hw->wait_aliveness,
|
||||
hw->recvd_aliveness, timeout);
|
||||
err = wait_event_timeout(hw->wait_aliveness_resp,
|
||||
dev->pg_event == MEI_PG_EVENT_RECEIVED, timeout);
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
hw->aliveness = mei_txe_aliveness_get(dev);
|
||||
ret = hw->aliveness == expected ? 0 : -ETIME;
|
||||
|
||||
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
|
||||
dev_dbg(&dev->pdev->dev, "aliveness settled after %d msecs\n",
|
||||
jiffies_to_msecs(timeout - err));
|
||||
hw->recvd_aliveness = false;
|
||||
dev_dbg(&dev->pdev->dev, "aliveness settled after = %d msec aliveness = %d event = %d\n",
|
||||
jiffies_to_msecs(timeout - err),
|
||||
hw->aliveness, dev->pg_event);
|
||||
|
||||
dev->pg_event = MEI_PG_EVENT_IDLE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -279,6 +284,32 @@ int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req)
|
||||
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
|
||||
*
|
||||
@ -589,7 +620,10 @@ static int mei_txe_write(struct mei_device *dev,
|
||||
mei_txe_input_ready_interrupt_enable(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;
|
||||
}
|
||||
|
||||
@ -960,9 +994,9 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id)
|
||||
/* Clear the interrupt cause */
|
||||
dev_dbg(&dev->pdev->dev,
|
||||
"Aliveness Interrupt: Status: %d\n", hw->aliveness);
|
||||
hw->recvd_aliveness = true;
|
||||
if (waitqueue_active(&hw->wait_aliveness))
|
||||
wake_up(&hw->wait_aliveness);
|
||||
dev->pg_event = MEI_PG_EVENT_RECEIVED;
|
||||
if (waitqueue_active(&hw->wait_aliveness_resp))
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 = {
|
||||
|
||||
.fw_status = mei_txe_fw_status,
|
||||
.host_is_ready = mei_txe_host_is_ready,
|
||||
|
||||
.pg_state = mei_txe_pg_state,
|
||||
|
||||
.hw_is_ready = mei_txe_hw_is_ready,
|
||||
.hw_reset = mei_txe_hw_reset,
|
||||
.hw_config = mei_txe_hw_config,
|
||||
.hw_start = mei_txe_hw_start,
|
||||
|
||||
.pg_is_enabled = mei_txe_pg_is_enabled,
|
||||
|
||||
.intr_clear = mei_txe_intr_clear,
|
||||
.intr_enable = mei_txe_intr_enable,
|
||||
.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
|
||||
*
|
||||
* @pdev - pci device
|
||||
* @cfg - per device generation config
|
||||
*
|
||||
* 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_txe_hw *hw;
|
||||
@ -1051,11 +1134,11 @@ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev)
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
mei_device_init(dev);
|
||||
mei_device_init(dev, cfg);
|
||||
|
||||
hw = to_txe_hw(dev);
|
||||
|
||||
init_waitqueue_head(&hw->wait_aliveness);
|
||||
init_waitqueue_head(&hw->wait_aliveness_resp);
|
||||
|
||||
dev->ops = &mei_txe_hw_ops;
|
||||
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include "hw.h"
|
||||
#include "hw-txe-regs.h"
|
||||
|
||||
#define MEI_TXI_RPM_TIMEOUT 500 /* ms */
|
||||
|
||||
/* Flatten Hierarchy interrupt cause */
|
||||
#define TXE_INTR_READINESS_BIT 0 /* HISR_INT_0_STS */
|
||||
#define TXE_INTR_READINESS HISR_INT_0_STS
|
||||
@ -35,12 +37,11 @@
|
||||
/**
|
||||
* struct mei_txe_hw - txe hardware specifics
|
||||
*
|
||||
* @mem_addr: SeC and BRIDGE bars
|
||||
* @aliveness: aliveness (power gating) state of the hardware
|
||||
* @readiness: readiness state of the hardware
|
||||
* @wait_aliveness: aliveness wait queue
|
||||
* @recvd_aliveness: aliveness interrupt was recived
|
||||
* @intr_cause: translated interrupt cause
|
||||
* @mem_addr: SeC and BRIDGE bars
|
||||
* @aliveness: aliveness (power gating) state of the hardware
|
||||
* @readiness: readiness state of the hardware
|
||||
* @wait_aliveness_resp: aliveness wait queue
|
||||
* @intr_cause: translated interrupt cause
|
||||
*/
|
||||
struct mei_txe_hw {
|
||||
void __iomem *mem_addr[NUM_OF_MEM_BARS];
|
||||
@ -48,8 +49,7 @@ struct mei_txe_hw {
|
||||
u32 readiness;
|
||||
u32 slots;
|
||||
|
||||
wait_queue_head_t wait_aliveness;
|
||||
bool recvd_aliveness;
|
||||
wait_queue_head_t wait_aliveness_resp;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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_thread_handler(int irq, void *dev_id);
|
||||
|
@ -31,14 +31,21 @@
|
||||
#define MEI_IAMTHIF_STALL_TIMER 12 /* 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 */
|
||||
|
||||
/*
|
||||
* MEI Version
|
||||
*/
|
||||
#define HBM_MINOR_VERSION 0
|
||||
#define HBM_MINOR_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 */
|
||||
#define MEI_HBM_CMD_OP_MSK 0x7f
|
||||
/* Host bus message command RESPONSE */
|
||||
@ -69,6 +76,11 @@
|
||||
|
||||
#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
|
||||
* used by hbm_host_stop_request.reason
|
||||
@ -207,6 +219,17 @@ struct hbm_props_response {
|
||||
struct mei_client_properties client_properties;
|
||||
} __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
|
||||
*
|
||||
|
@ -74,9 +74,13 @@ int mei_reset(struct mei_device *dev)
|
||||
if (state != MEI_DEV_INITIALIZING &&
|
||||
state != MEI_DEV_DISABLED &&
|
||||
state != MEI_DEV_POWER_DOWN &&
|
||||
state != MEI_DEV_POWER_UP)
|
||||
dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
|
||||
mei_dev_state_str(state));
|
||||
state != MEI_DEV_POWER_UP) {
|
||||
struct mei_fw_status fw_status;
|
||||
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
|
||||
* 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_hbm_reset(dev);
|
||||
|
||||
dev->me_clients_num = 0;
|
||||
dev->rd_msg_hdr = 0;
|
||||
dev->wd_pending = false;
|
||||
|
||||
@ -303,15 +307,58 @@ void mei_stop(struct mei_device *dev)
|
||||
}
|
||||
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 */
|
||||
INIT_LIST_HEAD(&dev->file_list);
|
||||
INIT_LIST_HEAD(&dev->device_list);
|
||||
mutex_init(&dev->device_lock);
|
||||
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_stop_wd);
|
||||
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
|
||||
*/
|
||||
bitmap_set(dev->host_clients_map, 0, 1);
|
||||
|
||||
dev->pg_event = MEI_PG_EVENT_IDLE;
|
||||
dev->cfg = cfg;
|
||||
}
|
||||
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->state = MEI_FILE_CONNECTING;
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "Connect to FW Client ID = %d\n",
|
||||
cl->me_client_id);
|
||||
|
@ -153,6 +153,20 @@ struct mei_msg_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
|
||||
*
|
||||
@ -213,6 +227,7 @@ struct mei_cl {
|
||||
|
||||
/** struct mei_hw_ops
|
||||
*
|
||||
* @fw_status - read FW status from PCI config space
|
||||
* @host_is_ready - query for host readiness
|
||||
|
||||
* @hw_is_ready - query if hw is ready
|
||||
@ -220,6 +235,9 @@ struct mei_cl {
|
||||
* @hw_start - start hw after reset
|
||||
* @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_enable - enable interrupts
|
||||
* @intr_disable - disable interrupts
|
||||
@ -237,6 +255,8 @@ struct mei_cl {
|
||||
*/
|
||||
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 (*hw_is_ready)(struct mei_device *dev);
|
||||
@ -244,6 +264,9 @@ struct mei_hw_ops {
|
||||
int (*hw_start)(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_enable)(struct mei_device *dev);
|
||||
void (*intr_disable)(struct mei_device *dev);
|
||||
@ -331,16 +354,61 @@ struct mei_cl_device {
|
||||
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
|
||||
|
||||
* @reset_count - limits the number of consecutive resets
|
||||
* @hbm_state - state of host bus message protocol
|
||||
* @pg_event - power gating event
|
||||
* @mem_addr - mem mapped base register address
|
||||
|
||||
* @hbuf_depth - depth of hardware host/write buffer is slots
|
||||
* @hbuf_is_ready - query if the host host/write buffer is ready
|
||||
* @wr_msg - the buffer for hbm control messages
|
||||
* @cfg - per device generation config and ops
|
||||
*/
|
||||
struct mei_device {
|
||||
struct pci_dev *pdev; /* pointer to pci device struct */
|
||||
@ -371,6 +439,7 @@ struct mei_device {
|
||||
* waiting queue for receive message from FW
|
||||
*/
|
||||
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_stop_wd;
|
||||
|
||||
@ -382,6 +451,14 @@ struct mei_device {
|
||||
enum mei_hbm_state hbm_state;
|
||||
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 */
|
||||
u32 rd_msg_hdr;
|
||||
|
||||
@ -442,6 +519,7 @@ struct mei_device {
|
||||
|
||||
|
||||
const struct mei_hw_ops *ops;
|
||||
const struct mei_cfg *cfg;
|
||||
char hw[0] __aligned(sizeof(void *));
|
||||
};
|
||||
|
||||
@ -474,7 +552,7 @@ static inline u32 mei_slots2data(int slots)
|
||||
/*
|
||||
* 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_start(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
|
||||
*/
|
||||
|
||||
|
||||
static inline void mei_hw_config(struct mei_device *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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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_write_is_idle(struct mei_device *dev);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
int mei_dbgfs_register(struct mei_device *dev, const char *name);
|
||||
void mei_dbgfs_deregister(struct mei_device *dev);
|
||||
|
@ -33,6 +33,8 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/miscdevice.h>
|
||||
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mei.h>
|
||||
|
||||
#include "mei_dev.h"
|
||||
@ -42,42 +44,44 @@
|
||||
|
||||
/* mei_pci_tbl - PCI Device ID Table */
|
||||
static const struct pci_device_id mei_me_pci_tbl[] = {
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G35)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82Q965)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G965)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GM965)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GME965)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q35)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82G33)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q33)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82X38)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_3200)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_6)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_7)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_8)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_9)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_10)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_1)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_2)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_3)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_4)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_1)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_2)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_3)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_4)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_1)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_2)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_CPT_1)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PBG_1)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_1)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_H)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_W)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_HR)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_WPT_LP)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_82946GZ, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_82G35, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_82Q965, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_82G965, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_82GM965, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_82GME965, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q35, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82G33, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q33, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82X38, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_3200, mei_me_legacy_cfg)},
|
||||
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_6, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_7, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_8, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_9, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_10, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_1, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_2, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_3, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_4, mei_me_legacy_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_1, mei_me_ich_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_2, mei_me_ich_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_3, mei_me_ich_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_4, mei_me_ich_cfg)},
|
||||
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_1, mei_me_pch_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_2, mei_me_pch_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_CPT_1, mei_me_pch_cpt_pbg_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_PBG_1, mei_me_pch_cpt_pbg_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_PPT_1, mei_me_pch_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_PPT_2, mei_me_pch_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_PPT_3, mei_me_pch_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_H, mei_me_lpt_cfg)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_W, mei_me_lpt_cfg)},
|
||||
{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 */
|
||||
{0, }
|
||||
@ -85,44 +89,33 @@ static const struct pci_device_id 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
|
||||
*
|
||||
* @pdev: PCI device structure
|
||||
* @ent: entry into pci_device_table
|
||||
* @cfg: per generation config
|
||||
*
|
||||
* returns true if ME Interface is valid, false otherwise
|
||||
*/
|
||||
static bool mei_me_quirk_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
const struct mei_cfg *cfg)
|
||||
{
|
||||
u32 reg;
|
||||
/* Cougar Point || Patsburg */
|
||||
if (ent->device == MEI_DEV_ID_CPT_1 ||
|
||||
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;
|
||||
if (cfg->quirk_probe && cfg->quirk_probe(pdev)) {
|
||||
dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
no_mei:
|
||||
dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
const struct mei_cfg *cfg = (struct mei_cfg *)(ent->driver_data);
|
||||
struct mei_device *dev;
|
||||
struct mei_me_hw *hw;
|
||||
int err;
|
||||
|
||||
|
||||
if (!mei_me_quirk_probe(pdev, ent)) {
|
||||
err = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
if (!mei_me_quirk_probe(pdev, cfg))
|
||||
return -ENODEV;
|
||||
|
||||
/* enable pci dev */
|
||||
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 */
|
||||
dev = mei_me_dev_init(pdev);
|
||||
dev = mei_me_dev_init(pdev, cfg);
|
||||
if (!dev) {
|
||||
err = -ENOMEM;
|
||||
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;
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_ME_RPM_TIMEOUT);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
|
||||
err = mei_register(dev);
|
||||
if (err)
|
||||
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);
|
||||
|
||||
/*
|
||||
* 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");
|
||||
|
||||
return 0;
|
||||
@ -259,12 +265,18 @@ static void mei_me_remove(struct pci_dev *pdev)
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
if (mei_pg_is_enabled(dev))
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
|
||||
hw = to_me_hw(dev);
|
||||
|
||||
|
||||
dev_dbg(&pdev->dev, "stop\n");
|
||||
mei_stop(dev);
|
||||
|
||||
if (!pci_dev_run_wake(pdev))
|
||||
mei_me_unset_pm_domain(dev);
|
||||
|
||||
/* disable interrupts */
|
||||
mei_disable_interrupts(dev);
|
||||
|
||||
@ -343,12 +355,120 @@ static int mei_me_pci_resume(struct device *device)
|
||||
|
||||
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)
|
||||
#else
|
||||
#define MEI_ME_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
#endif /* CONFIG_PM */
|
||||
/*
|
||||
* PCI driver structure
|
||||
*/
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mei.h>
|
||||
|
||||
@ -35,11 +36,18 @@
|
||||
#include "hw-txe.h"
|
||||
|
||||
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, }
|
||||
};
|
||||
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)
|
||||
{
|
||||
@ -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)
|
||||
{
|
||||
const struct mei_cfg *cfg = (struct mei_cfg *)(ent->driver_data);
|
||||
struct mei_device *dev;
|
||||
struct mei_txe_hw *hw;
|
||||
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 */
|
||||
dev = mei_txe_dev_init(pdev);
|
||||
dev = mei_txe_dev_init(pdev, cfg);
|
||||
if (!dev) {
|
||||
err = -ENOMEM;
|
||||
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;
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_TXI_RPM_TIMEOUT);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
|
||||
err = mei_register(dev);
|
||||
if (err)
|
||||
goto release_irq;
|
||||
|
||||
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;
|
||||
|
||||
release_irq:
|
||||
@ -187,10 +209,15 @@ static void mei_txe_remove(struct pci_dev *pdev)
|
||||
return;
|
||||
}
|
||||
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
|
||||
hw = to_txe_hw(dev);
|
||||
|
||||
mei_stop(dev);
|
||||
|
||||
if (!pci_dev_run_wake(pdev))
|
||||
mei_txe_unset_pm_domain(dev);
|
||||
|
||||
/* disable interrupts */
|
||||
mei_disable_interrupts(dev);
|
||||
free_irq(pdev->irq, dev);
|
||||
@ -265,15 +292,131 @@ static int mei_txe_pci_resume(struct device *device)
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mei_txe_pm_ops,
|
||||
mei_txe_pci_suspend,
|
||||
mei_txe_pci_resume);
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int mei_txe_pm_runtime_idle(struct device *device)
|
||||
{
|
||||
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)
|
||||
#else
|
||||
#define MEI_TXE_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
/*
|
||||
* PCI driver structure
|
||||
*/
|
||||
|
@ -84,8 +84,6 @@ int mei_wd_host_init(struct mei_device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
cl->state = MEI_FILE_CONNECTING;
|
||||
|
||||
ret = mei_cl_connect(cl, NULL);
|
||||
|
||||
if (ret) {
|
||||
|
@ -266,11 +266,12 @@ config REGULATOR_LP8788
|
||||
This driver supports LP8788 voltage regulator chip.
|
||||
|
||||
config REGULATOR_MAX14577
|
||||
tristate "Maxim 14577 regulator"
|
||||
tristate "Maxim 14577/77836 regulator"
|
||||
depends on MFD_MAX14577
|
||||
help
|
||||
This driver controls a Maxim 14577 regulator via I2C bus.
|
||||
The regulators include safeout LDO and current regulator 'CHARGER'.
|
||||
This driver controls a Maxim MAX14577/77836 regulator via I2C bus.
|
||||
The MAX14577 regulators include safeout LDO and charger current
|
||||
regulator. The MAX77836 has two additional LDOs.
|
||||
|
||||
config REGULATOR_MAX1586
|
||||
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
|
||||
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||
@ -22,6 +22,42 @@
|
||||
#include <linux/mfd/max14577-private.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)
|
||||
{
|
||||
int rid = rdev_get_id(rdev);
|
||||
@ -47,6 +83,9 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev)
|
||||
{
|
||||
u8 reg_data;
|
||||
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)
|
||||
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);
|
||||
|
||||
if ((reg_data & CHGCTRL4_MBCICHWRCL_MASK) == 0)
|
||||
return MAX14577_REGULATOR_CURRENT_LIMIT_MIN;
|
||||
return limits->min;
|
||||
|
||||
reg_data = ((reg_data & CHGCTRL4_MBCICHWRCH_MASK) >>
|
||||
CHGCTRL4_MBCICHWRCH_SHIFT);
|
||||
return MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START +
|
||||
reg_data * MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP;
|
||||
return limits->high_start + reg_data * limits->high_step;
|
||||
}
|
||||
|
||||
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;
|
||||
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)
|
||||
return -EINVAL;
|
||||
|
||||
if (min_uA > MAX14577_REGULATOR_CURRENT_LIMIT_MAX ||
|
||||
max_uA < MAX14577_REGULATOR_CURRENT_LIMIT_MIN)
|
||||
if (min_uA > limits->max || max_uA < limits->min)
|
||||
return -EINVAL;
|
||||
|
||||
if (max_uA < MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START) {
|
||||
/* Less than 200 mA, so set 90mA (turn only Low Bit off) */
|
||||
if (max_uA < limits->high_start) {
|
||||
/*
|
||||
* Less than high_start,
|
||||
* so set the minimal current (turn only Low Bit off)
|
||||
*/
|
||||
u8 reg_data = 0x0 << CHGCTRL4_MBCICHWRCL_SHIFT;
|
||||
return max14577_update_reg(rdev->regmap,
|
||||
MAX14577_CHG_REG_CHG_CTRL4,
|
||||
CHGCTRL4_MBCICHWRCL_MASK, reg_data);
|
||||
}
|
||||
|
||||
/* max_uA is in range: <LIMIT_HIGH_START, inifinite>, so search for
|
||||
* valid current starting from LIMIT_MAX. */
|
||||
for (i = MAX14577_REGULATOR_CURRENT_LIMIT_MAX;
|
||||
i >= MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START;
|
||||
i -= MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP) {
|
||||
/*
|
||||
* max_uA is in range: <high_start, inifinite>, so search for
|
||||
* valid current starting from maximum current.
|
||||
*/
|
||||
for (i = limits->max; i >= limits->high_start; i -= limits->high_step) {
|
||||
if (i <= max_uA)
|
||||
break;
|
||||
current_bits--;
|
||||
}
|
||||
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;
|
||||
/* and set proper High Bits */
|
||||
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,
|
||||
};
|
||||
|
||||
static const struct regulator_desc supported_regulators[] = {
|
||||
static const struct regulator_desc max14577_supported_regulators[] = {
|
||||
[MAX14577_SAFEOUT] = {
|
||||
.name = "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
|
||||
static struct of_regulator_match max14577_regulator_matches[] = {
|
||||
{ .name = "SAFEOUT", },
|
||||
{ .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;
|
||||
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");
|
||||
if (!np) {
|
||||
@ -158,8 +274,19 @@ static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_regulator_match(&pdev->dev, np, max14577_regulator_matches,
|
||||
MAX14577_REG_MAX);
|
||||
switch (dev_type) {
|
||||
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)
|
||||
dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret);
|
||||
else
|
||||
@ -170,31 +297,74 @@ static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev)
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
#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)
|
||||
{
|
||||
@ -202,15 +372,29 @@ static int max14577_regulator_probe(struct platform_device *pdev)
|
||||
struct max14577_platform_data *pdata = dev_get_platdata(max14577->dev);
|
||||
int i, ret;
|
||||
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)
|
||||
return ret;
|
||||
|
||||
config.dev = &pdev->dev;
|
||||
config.regmap = max14577->regmap;
|
||||
switch (dev_type) {
|
||||
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;
|
||||
/*
|
||||
* 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.of_node = pdata->regulators[i].of_node;
|
||||
} else {
|
||||
config.init_data = match_init_data(i);
|
||||
config.of_node = match_of_node(i);
|
||||
config.init_data = match_init_data(i, dev_type);
|
||||
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,
|
||||
&supported_regulators[i], &config);
|
||||
if (IS_ERR(regulator)) {
|
||||
ret = PTR_ERR(regulator);
|
||||
dev_err(&pdev->dev,
|
||||
"Regulator init failed for ID %d with error: %d\n",
|
||||
i, ret);
|
||||
"Regulator init failed for %d/%s with error: %d\n",
|
||||
i, supported_regulators[i].name, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@ -238,20 +424,41 @@ static int max14577_regulator_probe(struct platform_device *pdev)
|
||||
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 = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "max14577-regulator",
|
||||
},
|
||||
.probe = max14577_regulator_probe,
|
||||
.probe = max14577_regulator_probe,
|
||||
.id_table = max14577_regulator_id,
|
||||
};
|
||||
|
||||
static int __init max14577_regulator_init(void)
|
||||
{
|
||||
/* Check for valid values for charger */
|
||||
BUILD_BUG_ON(MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START +
|
||||
MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf !=
|
||||
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);
|
||||
}
|
||||
@ -264,6 +471,6 @@ static void __exit max14577_regulator_exit(void)
|
||||
module_exit(max14577_regulator_exit);
|
||||
|
||||
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_ALIAS("platform:max14577-regulator");
|
||||
|
@ -655,7 +655,7 @@ static int uio_mmap_physical(struct vm_area_struct *vma)
|
||||
|
||||
if (mem->addr & ~PAGE_MASK)
|
||||
return -ENODEV;
|
||||
if (vma->vm_end - vma->vm_start > mem->size)
|
||||
if (vma->vm_end - vma->vm_start > PAGE_ALIGN(mem->size))
|
||||
return -EINVAL;
|
||||
|
||||
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);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to get IRQ\n");
|
||||
goto bad0;
|
||||
goto bad1;
|
||||
}
|
||||
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);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to register uio device\n");
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
goto bad1;
|
||||
}
|
||||
|
||||
@ -282,7 +283,6 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
bad1:
|
||||
kfree(priv);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
bad0:
|
||||
/* kfree uioinfo for OF */
|
||||
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
|
||||
* @dev: w1_master device
|
||||
*
|
||||
* The w1 master list_mutex must be held.
|
||||
*
|
||||
* Return: 1 if there were commands to executed 0 otherwise
|
||||
*/
|
||||
int w1_process_callbacks(struct w1_master *dev)
|
||||
|
@ -203,7 +203,6 @@ enum w1_master_flags {
|
||||
* @search_id: allows continuing a search
|
||||
* @refcnt: reference count
|
||||
* @priv: private data storage
|
||||
* @priv_size: size allocated
|
||||
* @enable_pullup: allows a strong pullup
|
||||
* @pullup_duration: time for the next strong pullup
|
||||
* @flags: one of w1_master_flags
|
||||
@ -214,7 +213,6 @@ enum w1_master_flags {
|
||||
* @dev: sysfs device
|
||||
* @bus_master: io operations available
|
||||
* @seq: sequence number used for netlink broadcasts
|
||||
* @portid: destination for the current netlink command
|
||||
*/
|
||||
struct w1_master
|
||||
{
|
||||
@ -241,7 +239,6 @@ struct w1_master
|
||||
atomic_t refcnt;
|
||||
|
||||
void *priv;
|
||||
int priv_size;
|
||||
|
||||
/** 5V strong pullup enabled flag, 1 enabled, zero disabled. */
|
||||
int enable_pullup;
|
||||
@ -260,11 +257,6 @@ struct w1_master
|
||||
struct w1_bus_master *bus_master;
|
||||
|
||||
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))
|
||||
flush_signals(current);
|
||||
mutex_lock(&dev->list_mutex);
|
||||
w1_process_callbacks(dev);
|
||||
mutex_unlock(&dev->list_mutex);
|
||||
}
|
||||
mutex_lock(&dev->list_mutex);
|
||||
w1_process_callbacks(dev);
|
||||
mutex_unlock(&dev->list_mutex);
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.id.mst.id = dev->id;
|
||||
|
@ -29,51 +29,247 @@
|
||||
#include "w1_netlink.h"
|
||||
|
||||
#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)
|
||||
{
|
||||
char buf[sizeof(struct cn_msg) + sizeof(struct w1_netlink_msg)];
|
||||
struct cn_msg *m = (struct cn_msg *)buf;
|
||||
struct w1_netlink_msg *w = (struct w1_netlink_msg *)(m+1);
|
||||
struct {
|
||||
struct cn_msg cn;
|
||||
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;
|
||||
m->id.val = CN_W1_VAL;
|
||||
packet.cn.seq = dev->seq++;
|
||||
packet.cn.len = sizeof(*msg);
|
||||
|
||||
m->seq = dev->seq++;
|
||||
m->len = sizeof(struct w1_netlink_msg);
|
||||
memcpy(&packet.msg, msg, sizeof(*msg));
|
||||
packet.msg.len = 0;
|
||||
|
||||
memcpy(w, msg, sizeof(struct w1_netlink_msg));
|
||||
|
||||
cn_netlink_send(m, dev->portid, 0, GFP_KERNEL);
|
||||
cn_netlink_send(&packet.cn, 0, 0, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static void w1_send_slave(struct w1_master *dev, u64 rn)
|
||||
{
|
||||
struct cn_msg *msg = dev->priv;
|
||||
struct w1_netlink_msg *hdr = (struct w1_netlink_msg *)(msg + 1);
|
||||
struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1);
|
||||
int avail;
|
||||
struct w1_cb_block *block = dev->priv;
|
||||
struct w1_netlink_cmd *cache_cmd = block->cmd;
|
||||
u64 *data;
|
||||
|
||||
avail = dev->priv_size - cmd->len;
|
||||
w1_reply_make_space(block, sizeof(*data));
|
||||
|
||||
if (avail < 8) {
|
||||
msg->ack++;
|
||||
cn_netlink_send(msg, dev->portid, 0, GFP_KERNEL);
|
||||
|
||||
msg->len = sizeof(struct w1_netlink_msg) +
|
||||
sizeof(struct w1_netlink_cmd);
|
||||
hdr->len = sizeof(struct w1_netlink_cmd);
|
||||
cmd->len = 0;
|
||||
/* Add cmd back if the packet was sent */
|
||||
if (!block->cmd) {
|
||||
cache_cmd->len = 0;
|
||||
w1_netlink_queue_cmd(block, cache_cmd);
|
||||
}
|
||||
|
||||
data = (void *)(cmd + 1) + cmd->len;
|
||||
data = (u64 *)(block->cmd->data + block->cmd->len);
|
||||
|
||||
*data = rn;
|
||||
cmd->len += 8;
|
||||
hdr->len += 8;
|
||||
msg->len += 8;
|
||||
block->cn->len += sizeof(*data);
|
||||
block->msg->len += sizeof(*data);
|
||||
block->cmd->len += sizeof(*data);
|
||||
}
|
||||
|
||||
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) */
|
||||
static int w1_get_slaves(struct w1_master *dev,
|
||||
struct cn_msg *req_msg, struct w1_netlink_msg *req_hdr,
|
||||
struct w1_netlink_cmd *req_cmd)
|
||||
static int w1_get_slaves(struct w1_master *dev, struct w1_netlink_cmd *req_cmd)
|
||||
{
|
||||
struct cn_msg *msg;
|
||||
struct w1_netlink_msg *hdr;
|
||||
struct w1_netlink_cmd *cmd;
|
||||
struct w1_slave *sl;
|
||||
|
||||
msg = kzalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
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);
|
||||
req_cmd->len = 0;
|
||||
w1_netlink_queue_cmd(dev->priv, req_cmd);
|
||||
|
||||
if (req_cmd->cmd == W1_CMD_LIST_SLAVES) {
|
||||
__u64 rn;
|
||||
u64 rn;
|
||||
mutex_lock(&dev->list_mutex);
|
||||
list_for_each_entry(sl, &dev->slist, w1_slave_entry) {
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
|
||||
msg->ack = 0;
|
||||
cn_netlink_send(msg, dev->portid, 0, GFP_KERNEL);
|
||||
|
||||
dev->priv = NULL;
|
||||
dev->priv_size = 0;
|
||||
|
||||
kfree(msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int w1_send_read_reply(struct cn_msg *msg, struct w1_netlink_msg *hdr,
|
||||
struct w1_netlink_cmd *cmd, u32 portid)
|
||||
{
|
||||
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)
|
||||
static int w1_process_command_io(struct w1_master *dev,
|
||||
struct w1_netlink_cmd *cmd)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
switch (cmd->cmd) {
|
||||
case W1_CMD_TOUCH:
|
||||
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;
|
||||
case W1_CMD_READ:
|
||||
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;
|
||||
case W1_CMD_WRITE:
|
||||
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,
|
||||
struct cn_msg *msg, struct w1_netlink_msg *hdr,
|
||||
struct w1_netlink_cmd *cmd)
|
||||
{
|
||||
struct w1_slave *sl;
|
||||
int err = 0;
|
||||
struct w1_reg_num *id;
|
||||
|
||||
if (cmd->len != 8)
|
||||
if (cmd->len != sizeof(*id))
|
||||
return -EINVAL;
|
||||
|
||||
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,
|
||||
struct cn_msg *req_msg, struct w1_netlink_msg *req_hdr,
|
||||
struct w1_netlink_cmd *req_cmd)
|
||||
{
|
||||
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_LIST_SLAVES:
|
||||
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);
|
||||
break;
|
||||
case W1_CMD_READ:
|
||||
case W1_CMD_WRITE:
|
||||
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;
|
||||
case W1_CMD_RESET:
|
||||
err = w1_reset_bus(dev);
|
||||
@ -269,8 +391,7 @@ static int w1_process_command_master(struct w1_master *dev,
|
||||
case W1_CMD_SLAVE_REMOVE:
|
||||
mutex_unlock(&dev->bus_mutex);
|
||||
mutex_lock(&dev->mutex);
|
||||
err = w1_process_command_addremove(dev, req_msg, req_hdr,
|
||||
req_cmd);
|
||||
err = w1_process_command_addremove(dev, req_cmd);
|
||||
mutex_unlock(&dev->mutex);
|
||||
mutex_lock(&dev->bus_mutex);
|
||||
break;
|
||||
@ -282,22 +403,21 @@ static int w1_process_command_master(struct w1_master *dev,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int w1_process_command_slave(struct w1_slave *sl, struct cn_msg *msg,
|
||||
struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd)
|
||||
static int w1_process_command_slave(struct w1_slave *sl,
|
||||
struct w1_netlink_cmd *cmd)
|
||||
{
|
||||
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,
|
||||
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,
|
||||
struct w1_netlink_msg *mcmd, u32 portid)
|
||||
static int w1_process_command_root(struct cn_msg *req_cn, u32 portid)
|
||||
{
|
||||
struct w1_master *m;
|
||||
struct w1_master *dev;
|
||||
struct cn_msg *cn;
|
||||
struct w1_netlink_msg *w;
|
||||
struct w1_netlink_msg *msg;
|
||||
u32 *id;
|
||||
|
||||
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.val = CN_W1_VAL;
|
||||
|
||||
cn->seq = msg->seq;
|
||||
cn->ack = 1;
|
||||
cn->seq = req_cn->seq;
|
||||
cn->ack = req_cn->seq + 1;
|
||||
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;
|
||||
w->status = 0;
|
||||
w->len = 0;
|
||||
id = (u32 *)(w + 1);
|
||||
msg->type = W1_LIST_MASTERS;
|
||||
msg->status = 0;
|
||||
msg->len = 0;
|
||||
id = (u32 *)msg->data;
|
||||
|
||||
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)) {
|
||||
cn_netlink_send(cn, portid, 0, GFP_KERNEL);
|
||||
cn->ack++;
|
||||
cn->len = sizeof(struct w1_netlink_msg);
|
||||
w->len = 0;
|
||||
id = (u32 *)(w + 1);
|
||||
msg->len = 0;
|
||||
id = (u32 *)msg->data;
|
||||
}
|
||||
|
||||
*id = m->id;
|
||||
w->len += sizeof(*id);
|
||||
*id = dev->id;
|
||||
msg->len += sizeof(*id);
|
||||
cn->len += sizeof(*id);
|
||||
id++;
|
||||
}
|
||||
cn->ack = 0;
|
||||
cn_netlink_send(cn, portid, 0, GFP_KERNEL);
|
||||
mutex_unlock(&w1_mlock);
|
||||
|
||||
@ -340,100 +458,44 @@ static int w1_process_command_root(struct cn_msg *msg,
|
||||
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)
|
||||
{
|
||||
struct w1_cb_node *node = container_of(async_cmd, struct w1_cb_node,
|
||||
async);
|
||||
u16 mlen = node->m->len;
|
||||
u8 *cmd_data = node->m->data;
|
||||
u16 mlen = node->msg->len;
|
||||
u16 len;
|
||||
int err = 0;
|
||||
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);
|
||||
dev->portid = node->block->portid;
|
||||
dev->priv = node->block;
|
||||
if (sl && w1_reset_select_slave(sl))
|
||||
err = -ENODEV;
|
||||
node->block->cur_msg = node->msg;
|
||||
|
||||
while (mlen && !err) {
|
||||
cmd = (struct w1_netlink_cmd *)cmd_data;
|
||||
|
||||
if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) {
|
||||
err = -E2BIG;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sl)
|
||||
err = w1_process_command_slave(sl, &node->block->msg,
|
||||
node->m, cmd);
|
||||
err = w1_process_command_slave(sl, cmd);
|
||||
else
|
||||
err = w1_process_command_master(dev, &node->block->msg,
|
||||
node->m, cmd);
|
||||
err = w1_process_command_master(dev, cmd);
|
||||
w1_netlink_check_send(node->block);
|
||||
|
||||
w1_netlink_send_error(&node->block->msg, node->m, cmd,
|
||||
node->block->portid, err);
|
||||
w1_netlink_queue_status(node->block, node->msg, cmd, err);
|
||||
err = 0;
|
||||
|
||||
cmd_data += cmd->len + sizeof(struct w1_netlink_cmd);
|
||||
mlen -= cmd->len + sizeof(struct w1_netlink_cmd);
|
||||
len = sizeof(*cmd) + cmd->len;
|
||||
cmd = (struct w1_netlink_cmd *)((u8 *)cmd + len);
|
||||
mlen -= len;
|
||||
}
|
||||
|
||||
if (!cmd || err)
|
||||
w1_netlink_send_error(&node->block->msg, node->m, cmd,
|
||||
node->block->portid, err);
|
||||
w1_netlink_queue_status(node->block, node->msg, cmd, err);
|
||||
|
||||
/* ref taken in w1_search_slave or w1_search_master_id when building
|
||||
* 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);
|
||||
else
|
||||
atomic_dec(&dev->refcnt);
|
||||
dev->portid = 0;
|
||||
dev->priv = NULL;
|
||||
mutex_unlock(&dev->bus_mutex);
|
||||
|
||||
mutex_lock(&dev->list_mutex);
|
||||
list_del(&async_cmd->async_entry);
|
||||
mutex_unlock(&dev->list_mutex);
|
||||
|
||||
if (atomic_sub_return(1, &node->block->refcnt) == 0)
|
||||
kfree(node->block);
|
||||
w1_unref_block(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_master *dev;
|
||||
u16 msg_len;
|
||||
u16 slave_len = 0;
|
||||
int err = 0;
|
||||
struct w1_cb_block *block = NULL;
|
||||
struct w1_cb_node *node = NULL;
|
||||
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
|
||||
* space for one cb_node each.
|
||||
*/
|
||||
msg_len = msg->len;
|
||||
msg_len = cn->len;
|
||||
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;
|
||||
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;
|
||||
w1_list_count_cmds(msg, &cmd_count, &slave_len);
|
||||
}
|
||||
|
||||
msg_len -= sizeof(struct w1_netlink_msg) + m->len;
|
||||
m = (struct w1_netlink_msg *)(((u8 *)m) +
|
||||
sizeof(struct w1_netlink_msg) + m->len);
|
||||
msg_len -= sizeof(struct w1_netlink_msg) + msg->len;
|
||||
msg = (struct w1_netlink_msg *)(((u8 *)msg) +
|
||||
sizeof(struct w1_netlink_msg) + msg->len);
|
||||
}
|
||||
m = (struct w1_netlink_msg *)(msg + 1);
|
||||
msg = (struct w1_netlink_msg *)(cn + 1);
|
||||
if (node_count) {
|
||||
/* msg->len doesn't include itself */
|
||||
long size = sizeof(struct w1_cb_block) + msg->len +
|
||||
node_count*sizeof(struct w1_cb_node);
|
||||
block = kmalloc(size, GFP_KERNEL);
|
||||
int size;
|
||||
u16 reply_size = sizeof(*cn) + cn->len + slave_len;
|
||||
if (cn->flags & W1_CN_BUNDLE) {
|
||||
/* 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) {
|
||||
w1_netlink_send_error(msg, m, NULL, nsp->portid,
|
||||
-ENOMEM);
|
||||
/* if the system is already out of memory,
|
||||
* (A) will this work, and (B) would it be better
|
||||
* to not try?
|
||||
*/
|
||||
w1_netlink_send_error(cn, msg, nsp->portid, -ENOMEM);
|
||||
return;
|
||||
}
|
||||
atomic_set(&block->refcnt, 1);
|
||||
block->portid = nsp->portid;
|
||||
memcpy(&block->msg, msg, sizeof(*msg) + msg->len);
|
||||
node = (struct w1_cb_node *)((u8 *)block->msg.data + msg->len);
|
||||
memcpy(&block->request_cn, cn, sizeof(*cn) + cn->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) {
|
||||
|
||||
dev = 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;
|
||||
break;
|
||||
}
|
||||
|
||||
/* execute on this thread, no need to process later */
|
||||
if (m->type == W1_LIST_MASTERS) {
|
||||
err = w1_process_command_root(msg, m, nsp->portid);
|
||||
if (msg->type == W1_LIST_MASTERS) {
|
||||
err = w1_process_command_root(cn, nsp->portid);
|
||||
goto out_cont;
|
||||
}
|
||||
|
||||
/* All following message types require additional data,
|
||||
* check here before references are taken.
|
||||
*/
|
||||
if (!m->len) {
|
||||
if (!msg->len) {
|
||||
err = -EPROTO;
|
||||
goto out_cont;
|
||||
}
|
||||
|
||||
/* both search calls take reference counts */
|
||||
if (m->type == W1_MASTER_CMD) {
|
||||
dev = w1_search_master_id(m->id.mst.id);
|
||||
} else if (m->type == W1_SLAVE_CMD) {
|
||||
sl = w1_search_slave((struct w1_reg_num *)m->id.id);
|
||||
/* both search calls take references */
|
||||
if (msg->type == W1_MASTER_CMD) {
|
||||
dev = w1_search_master_id(msg->id.mst.id);
|
||||
} else if (msg->type == W1_SLAVE_CMD) {
|
||||
sl = w1_search_slave((struct w1_reg_num *)msg->id.id);
|
||||
if (sl)
|
||||
dev = sl->master;
|
||||
} else {
|
||||
printk(KERN_NOTICE
|
||||
"%s: msg: %x.%x, wrong type: %u, len: %u.\n",
|
||||
__func__, msg->id.idx, msg->id.val,
|
||||
m->type, m->len);
|
||||
"%s: cn: %x.%x, wrong type: %u, len: %u.\n",
|
||||
__func__, cn->id.idx, cn->id.val,
|
||||
msg->type, msg->len);
|
||||
err = -EPROTO;
|
||||
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);
|
||||
node->async.cb = w1_process_cb;
|
||||
node->block = block;
|
||||
node->m = (struct w1_netlink_msg *)((u8 *)&block->msg +
|
||||
(size_t)((u8 *)m - (u8 *)msg));
|
||||
node->msg = (struct w1_netlink_msg *)((u8 *)&block->request_cn +
|
||||
(size_t)((u8 *)msg - (u8 *)cn));
|
||||
node->sl = sl;
|
||||
node->dev = dev;
|
||||
|
||||
@ -561,11 +710,15 @@ static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
|
||||
++node;
|
||||
|
||||
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)
|
||||
w1_netlink_send_error(msg, m, NULL, nsp->portid, err);
|
||||
msg_len -= sizeof(struct w1_netlink_msg) + m->len;
|
||||
m = (struct w1_netlink_msg *)(((u8 *)m) +
|
||||
sizeof(struct w1_netlink_msg) + m->len);
|
||||
w1_netlink_send_error(cn, msg, nsp->portid, err);
|
||||
msg_len -= sizeof(struct w1_netlink_msg) + msg->len;
|
||||
msg = (struct w1_netlink_msg *)(((u8 *)msg) +
|
||||
sizeof(struct w1_netlink_msg) + msg->len);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
err = 0;
|
||||
}
|
||||
if (block && atomic_sub_return(1, &block->refcnt) == 0)
|
||||
kfree(block);
|
||||
if (block)
|
||||
w1_unref_block(block);
|
||||
}
|
||||
|
||||
int w1_init_netlink(void)
|
||||
@ -591,7 +744,7 @@ void w1_fini_netlink(void)
|
||||
cn_del_callback(&w1_id);
|
||||
}
|
||||
#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"
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
@ -49,6 +60,19 @@ enum w1_netlink_message_types {
|
||||
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
|
||||
{
|
||||
__u8 type;
|
||||
@ -66,6 +90,7 @@ struct w1_netlink_msg
|
||||
|
||||
/**
|
||||
* enum w1_commands - commands available for master or slave operations
|
||||
*
|
||||
* @W1_CMD_READ: read len bytes
|
||||
* @W1_CMD_WRITE: write len bytes
|
||||
* @W1_CMD_SEARCH: initiate a standard search, returns only the slave
|
||||
@ -93,6 +118,17 @@ enum w1_commands {
|
||||
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
|
||||
{
|
||||
__u8 cmd;
|
||||
|
@ -71,6 +71,7 @@ struct cn_dev {
|
||||
int cn_add_callback(struct cb_id *id, const char *name,
|
||||
void (*callback)(struct cn_msg *, struct netlink_skb_parms *));
|
||||
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_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 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);
|
||||
|
||||
/*
|
||||
* 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
|
||||
* 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 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)
|
||||
{
|
||||
return 0;
|
||||
|
@ -696,6 +696,8 @@ struct vmbus_channel {
|
||||
* preserve the earlier behavior.
|
||||
*/
|
||||
u32 target_vp;
|
||||
/* The corresponding CPUID in the guest */
|
||||
u32 target_cpu;
|
||||
/*
|
||||
* Support for sub-channels. For high performance devices,
|
||||
* 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.
|
||||
*/
|
||||
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)
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/irqreturn.h>
|
||||
|
||||
struct mcb_driver;
|
||||
struct mcb_device;
|
||||
|
||||
/**
|
||||
* struct mcb_bus - MEN Chameleon Bus
|
||||
@ -23,11 +24,14 @@ struct mcb_driver;
|
||||
* @dev: pointer to carrier device
|
||||
* @children: the child busses
|
||||
* @bus_nr: mcb bus number
|
||||
* @get_irq: callback to get IRQ number
|
||||
*/
|
||||
struct mcb_bus {
|
||||
struct list_head children;
|
||||
struct device dev;
|
||||
struct device *carrier;
|
||||
int bus_nr;
|
||||
int (*get_irq)(struct mcb_device *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);
|
||||
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 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 void mcb_bus_put(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>
|
||||
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||
*
|
||||
@ -22,9 +22,19 @@
|
||||
#include <linux/i2c.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 {
|
||||
MAX14577_REG_DEVICEID = 0x00,
|
||||
MAX14577_REG_INT1 = 0x01,
|
||||
@ -74,20 +84,22 @@ enum max14577_muic_charger_type {
|
||||
};
|
||||
|
||||
/* MAX14577 interrupts */
|
||||
#define INT1_ADC_MASK (0x1 << 0)
|
||||
#define INT1_ADCLOW_MASK (0x1 << 1)
|
||||
#define INT1_ADCERR_MASK (0x1 << 2)
|
||||
#define MAX14577_INT1_ADC_MASK BIT(0)
|
||||
#define MAX14577_INT1_ADCLOW_MASK BIT(1)
|
||||
#define MAX14577_INT1_ADCERR_MASK BIT(2)
|
||||
#define MAX77836_INT1_ADC1K_MASK BIT(3)
|
||||
|
||||
#define INT2_CHGTYP_MASK (0x1 << 0)
|
||||
#define INT2_CHGDETRUN_MASK (0x1 << 1)
|
||||
#define INT2_DCDTMR_MASK (0x1 << 2)
|
||||
#define INT2_DBCHG_MASK (0x1 << 3)
|
||||
#define INT2_VBVOLT_MASK (0x1 << 4)
|
||||
#define MAX14577_INT2_CHGTYP_MASK BIT(0)
|
||||
#define MAX14577_INT2_CHGDETRUN_MASK BIT(1)
|
||||
#define MAX14577_INT2_DCDTMR_MASK BIT(2)
|
||||
#define MAX14577_INT2_DBCHG_MASK BIT(3)
|
||||
#define MAX14577_INT2_VBVOLT_MASK BIT(4)
|
||||
#define MAX77836_INT2_VIDRM_MASK BIT(5)
|
||||
|
||||
#define INT3_EOC_MASK (0x1 << 0)
|
||||
#define INT3_CGMBC_MASK (0x1 << 1)
|
||||
#define INT3_OVP_MASK (0x1 << 2)
|
||||
#define INT3_MBCCHGERR_MASK (0x1 << 3)
|
||||
#define MAX14577_INT3_EOC_MASK BIT(0)
|
||||
#define MAX14577_INT3_CGMBC_MASK BIT(1)
|
||||
#define MAX14577_INT3_OVP_MASK BIT(2)
|
||||
#define MAX14577_INT3_MBCCHGERR_MASK BIT(3)
|
||||
|
||||
/* MAX14577 DEVICE ID register */
|
||||
#define DEVID_VENDORID_SHIFT 0
|
||||
@ -99,9 +111,11 @@ enum max14577_muic_charger_type {
|
||||
#define STATUS1_ADC_SHIFT 0
|
||||
#define STATUS1_ADCLOW_SHIFT 5
|
||||
#define STATUS1_ADCERR_SHIFT 6
|
||||
#define MAX77836_STATUS1_ADC1K_SHIFT 7
|
||||
#define STATUS1_ADC_MASK (0x1f << STATUS1_ADC_SHIFT)
|
||||
#define STATUS1_ADCLOW_MASK (0x1 << STATUS1_ADCLOW_SHIFT)
|
||||
#define STATUS1_ADCERR_MASK (0x1 << STATUS1_ADCERR_SHIFT)
|
||||
#define STATUS1_ADCLOW_MASK BIT(STATUS1_ADCLOW_SHIFT)
|
||||
#define STATUS1_ADCERR_MASK BIT(STATUS1_ADCERR_SHIFT)
|
||||
#define MAX77836_STATUS1_ADC1K_MASK BIT(MAX77836_STATUS1_ADC1K_SHIFT)
|
||||
|
||||
/* MAX14577 STATUS2 register */
|
||||
#define STATUS2_CHGTYP_SHIFT 0
|
||||
@ -109,11 +123,13 @@ enum max14577_muic_charger_type {
|
||||
#define STATUS2_DCDTMR_SHIFT 4
|
||||
#define STATUS2_DBCHG_SHIFT 5
|
||||
#define STATUS2_VBVOLT_SHIFT 6
|
||||
#define MAX77836_STATUS2_VIDRM_SHIFT 7
|
||||
#define STATUS2_CHGTYP_MASK (0x7 << STATUS2_CHGTYP_SHIFT)
|
||||
#define STATUS2_CHGDETRUN_MASK (0x1 << STATUS2_CHGDETRUN_SHIFT)
|
||||
#define STATUS2_DCDTMR_MASK (0x1 << STATUS2_DCDTMR_SHIFT)
|
||||
#define STATUS2_DBCHG_MASK (0x1 << STATUS2_DBCHG_SHIFT)
|
||||
#define STATUS2_VBVOLT_MASK (0x1 << STATUS2_VBVOLT_SHIFT)
|
||||
#define STATUS2_CHGDETRUN_MASK BIT(STATUS2_CHGDETRUN_SHIFT)
|
||||
#define STATUS2_DCDTMR_MASK BIT(STATUS2_DCDTMR_SHIFT)
|
||||
#define STATUS2_DBCHG_MASK BIT(STATUS2_DBCHG_SHIFT)
|
||||
#define STATUS2_VBVOLT_MASK BIT(STATUS2_VBVOLT_SHIFT)
|
||||
#define MAX77836_STATUS2_VIDRM_MASK BIT(MAX77836_STATUS2_VIDRM_SHIFT)
|
||||
|
||||
/* MAX14577 CONTROL1 register */
|
||||
#define COMN1SW_SHIFT 0
|
||||
@ -122,8 +138,8 @@ enum max14577_muic_charger_type {
|
||||
#define IDBEN_SHIFT 7
|
||||
#define COMN1SW_MASK (0x7 << COMN1SW_SHIFT)
|
||||
#define COMP2SW_MASK (0x7 << COMP2SW_SHIFT)
|
||||
#define MICEN_MASK (0x1 << MICEN_SHIFT)
|
||||
#define IDBEN_MASK (0x1 << IDBEN_SHIFT)
|
||||
#define MICEN_MASK BIT(MICEN_SHIFT)
|
||||
#define IDBEN_MASK BIT(IDBEN_SHIFT)
|
||||
#define CLEAR_IDBEN_MICEN_MASK (COMN1SW_MASK | COMP2SW_MASK)
|
||||
#define CTRL1_SW_USB ((1 << COMP2SW_SHIFT) \
|
||||
| (1 << COMN1SW_SHIFT))
|
||||
@ -143,14 +159,14 @@ enum max14577_muic_charger_type {
|
||||
#define CTRL2_ACCDET_SHIFT (5)
|
||||
#define CTRL2_USBCPINT_SHIFT (6)
|
||||
#define CTRL2_RCPS_SHIFT (7)
|
||||
#define CTRL2_LOWPWR_MASK (0x1 << CTRL2_LOWPWR_SHIFT)
|
||||
#define CTRL2_ADCEN_MASK (0x1 << CTRL2_ADCEN_SHIFT)
|
||||
#define CTRL2_CPEN_MASK (0x1 << CTRL2_CPEN_SHIFT)
|
||||
#define CTRL2_SFOUTASRT_MASK (0x1 << CTRL2_SFOUTASRT_SHIFT)
|
||||
#define CTRL2_SFOUTORD_MASK (0x1 << CTRL2_SFOUTORD_SHIFT)
|
||||
#define CTRL2_ACCDET_MASK (0x1 << CTRL2_ACCDET_SHIFT)
|
||||
#define CTRL2_USBCPINT_MASK (0x1 << CTRL2_USBCPINT_SHIFT)
|
||||
#define CTRL2_RCPS_MASK (0x1 << CTR2_RCPS_SHIFT)
|
||||
#define CTRL2_LOWPWR_MASK BIT(CTRL2_LOWPWR_SHIFT)
|
||||
#define CTRL2_ADCEN_MASK BIT(CTRL2_ADCEN_SHIFT)
|
||||
#define CTRL2_CPEN_MASK BIT(CTRL2_CPEN_SHIFT)
|
||||
#define CTRL2_SFOUTASRT_MASK BIT(CTRL2_SFOUTASRT_SHIFT)
|
||||
#define CTRL2_SFOUTORD_MASK BIT(CTRL2_SFOUTORD_SHIFT)
|
||||
#define CTRL2_ACCDET_MASK BIT(CTRL2_ACCDET_SHIFT)
|
||||
#define CTRL2_USBCPINT_MASK BIT(CTRL2_USBCPINT_SHIFT)
|
||||
#define CTRL2_RCPS_MASK BIT(CTRL2_RCPS_SHIFT)
|
||||
|
||||
#define CTRL2_CPEN1_LOWPWR0 ((1 << CTRL2_CPEN_SHIFT) | \
|
||||
(0 << CTRL2_LOWPWR_SHIFT))
|
||||
@ -198,14 +214,14 @@ enum max14577_charger_reg {
|
||||
#define CDETCTRL1_DBEXIT_SHIFT 5
|
||||
#define CDETCTRL1_DBIDLE_SHIFT 6
|
||||
#define CDETCTRL1_CDPDET_SHIFT 7
|
||||
#define CDETCTRL1_CHGDETEN_MASK (0x1 << CDETCTRL1_CHGDETEN_SHIFT)
|
||||
#define CDETCTRL1_CHGTYPMAN_MASK (0x1 << CDETCTRL1_CHGTYPMAN_SHIFT)
|
||||
#define CDETCTRL1_DCDEN_MASK (0x1 << CDETCTRL1_DCDEN_SHIFT)
|
||||
#define CDETCTRL1_DCD2SCT_MASK (0x1 << CDETCTRL1_DCD2SCT_SHIFT)
|
||||
#define CDETCTRL1_DCHKTM_MASK (0x1 << CDETCTRL1_DCHKTM_SHIFT)
|
||||
#define CDETCTRL1_DBEXIT_MASK (0x1 << CDETCTRL1_DBEXIT_SHIFT)
|
||||
#define CDETCTRL1_DBIDLE_MASK (0x1 << CDETCTRL1_DBIDLE_SHIFT)
|
||||
#define CDETCTRL1_CDPDET_MASK (0x1 << CDETCTRL1_CDPDET_SHIFT)
|
||||
#define CDETCTRL1_CHGDETEN_MASK BIT(CDETCTRL1_CHGDETEN_SHIFT)
|
||||
#define CDETCTRL1_CHGTYPMAN_MASK BIT(CDETCTRL1_CHGTYPMAN_SHIFT)
|
||||
#define CDETCTRL1_DCDEN_MASK BIT(CDETCTRL1_DCDEN_SHIFT)
|
||||
#define CDETCTRL1_DCD2SCT_MASK BIT(CDETCTRL1_DCD2SCT_SHIFT)
|
||||
#define CDETCTRL1_DCHKTM_MASK BIT(CDETCTRL1_DCHKTM_SHIFT)
|
||||
#define CDETCTRL1_DBEXIT_MASK BIT(CDETCTRL1_DBEXIT_SHIFT)
|
||||
#define CDETCTRL1_DBIDLE_MASK BIT(CDETCTRL1_DBIDLE_SHIFT)
|
||||
#define CDETCTRL1_CDPDET_MASK BIT(CDETCTRL1_CDPDET_SHIFT)
|
||||
|
||||
/* MAX14577 CHGCTRL1 register */
|
||||
#define CHGCTRL1_TCHW_SHIFT 4
|
||||
@ -213,9 +229,9 @@ enum max14577_charger_reg {
|
||||
|
||||
/* MAX14577 CHGCTRL2 register */
|
||||
#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_MASK (0x1 << CHGCTRL2_VCHGR_RC_SHIFT)
|
||||
#define CHGCTRL2_VCHGR_RC_MASK BIT(CHGCTRL2_VCHGR_RC_SHIFT)
|
||||
|
||||
/* MAX14577 CHGCTRL3 register */
|
||||
#define CHGCTRL3_MBCCVWRC_SHIFT 0
|
||||
@ -225,7 +241,7 @@ enum max14577_charger_reg {
|
||||
#define CHGCTRL4_MBCICHWRCH_SHIFT 0
|
||||
#define CHGCTRL4_MBCICHWRCH_MASK (0xf << CHGCTRL4_MBCICHWRCH_SHIFT)
|
||||
#define CHGCTRL4_MBCICHWRCL_SHIFT 4
|
||||
#define CHGCTRL4_MBCICHWRCL_MASK (0x1 << CHGCTRL4_MBCICHWRCL_SHIFT)
|
||||
#define CHGCTRL4_MBCICHWRCL_MASK BIT(CHGCTRL4_MBCICHWRCL_SHIFT)
|
||||
|
||||
/* MAX14577 CHGCTRL5 register */
|
||||
#define CHGCTRL5_EOCS_SHIFT 0
|
||||
@ -233,7 +249,7 @@ enum max14577_charger_reg {
|
||||
|
||||
/* MAX14577 CHGCTRL6 register */
|
||||
#define CHGCTRL6_AUTOSTOP_SHIFT 5
|
||||
#define CHGCTRL6_AUTOSTOP_MASK (0x1 << CHGCTRL6_AUTOSTOP_SHIFT)
|
||||
#define CHGCTRL6_AUTOSTOP_MASK BIT(CHGCTRL6_AUTOSTOP_SHIFT)
|
||||
|
||||
/* MAX14577 CHGCTRL7 register */
|
||||
#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_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 */
|
||||
#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 {
|
||||
/* INT1 */
|
||||
MAX14577_IRQ_INT1_ADC,
|
||||
MAX14577_IRQ_INT1_ADCLOW,
|
||||
MAX14577_IRQ_INT1_ADCERR,
|
||||
MAX77836_IRQ_INT1_ADC1K,
|
||||
|
||||
/* INT2 */
|
||||
MAX14577_IRQ_INT2_CHGTYP,
|
||||
@ -260,6 +373,7 @@ enum max14577_irq {
|
||||
MAX14577_IRQ_INT2_DCDTMR,
|
||||
MAX14577_IRQ_INT2_DBCHG,
|
||||
MAX14577_IRQ_INT2_VBVOLT,
|
||||
MAX77836_IRQ_INT2_VIDRM,
|
||||
|
||||
/* INT3 */
|
||||
MAX14577_IRQ_INT3_EOC,
|
||||
@ -267,21 +381,25 @@ enum max14577_irq {
|
||||
MAX14577_IRQ_INT3_OVP,
|
||||
MAX14577_IRQ_INT3_MBCCHGERR,
|
||||
|
||||
/* TOPSYS_INT, only MAX77836 */
|
||||
MAX77836_IRQ_TOPSYS_T140C,
|
||||
MAX77836_IRQ_TOPSYS_T120C,
|
||||
|
||||
MAX14577_IRQ_NUM,
|
||||
};
|
||||
|
||||
struct max14577 {
|
||||
struct device *dev;
|
||||
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;
|
||||
|
||||
/* Device ID */
|
||||
u8 vendor_id; /* Vendor Identification */
|
||||
u8 device_id; /* Chip Version */
|
||||
};
|
||||
|
||||
/* 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>
|
||||
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||
*
|
||||
@ -20,6 +20,9 @@
|
||||
* MAX14577 has MUIC, Charger devices.
|
||||
* The devices share the same I2C bus and interrupt line
|
||||
* included in this mfd driver.
|
||||
*
|
||||
* MAX77836 has additional PMIC and Fuel-Gauge on different I2C slave
|
||||
* addresses.
|
||||
*/
|
||||
|
||||
#ifndef __MAX14577_H__
|
||||
@ -32,7 +35,17 @@ enum max14577_regulators {
|
||||
MAX14577_SAFEOUT = 0,
|
||||
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 {
|
||||
|
@ -415,7 +415,7 @@ struct palmas_usb {
|
||||
struct palmas *palmas;
|
||||
struct device *dev;
|
||||
|
||||
struct extcon_dev edev;
|
||||
struct extcon_dev *edev;
|
||||
|
||||
int id_otg_irq;
|
||||
int id_irq;
|
||||
|
@ -64,7 +64,7 @@ struct miscdevice {
|
||||
umode_t mode;
|
||||
};
|
||||
|
||||
extern int misc_register(struct miscdevice * misc);
|
||||
extern int misc_register(struct miscdevice *misc);
|
||||
extern int misc_deregister(struct miscdevice *misc);
|
||||
|
||||
#define MODULE_ALIAS_MISCDEV(minor) \
|
||||
|
Loading…
Reference in New Issue
Block a user